feat: a skill creating skill as default

This commit is contained in:
Georg Bauer
2026-07-30 13:41:04 +02:00
parent f2fb8d79f7
commit 4175706533
7 changed files with 343 additions and 151 deletions

View File

@@ -40,7 +40,7 @@ identifier = "de.rfc1437.ds4server"
description = "A native macOS coding-agent GUI for DwarfStar"
binaries = [{ path = "ds4-server", main = true }]
icons = ["assets/DS4Server.icns", "assets/app-icon.png"]
resources = ["metal"]
resources = ["metal", "assets/dev-brain"]
[package.metadata.packager.macos]
minimum-system-version = "13.0"

View File

@@ -0,0 +1,15 @@
# Dev Brain purpose
Dev Brain compiles durable, source-backed knowledge from the registered projects.
## Priorities
- Architecture, behavior, decisions, invariants, workflows, and relationships.
- Small topic pages that answer recurring development questions.
- Visible uncertainty and exact project provenance.
## Recurring questions
- Where does a behavior live, and what must remain invariant when it changes?
- Which decisions constrain the current implementation?
- Which project sources must be rechecked before relying on this page?

View File

@@ -0,0 +1,67 @@
# Dev Brain schema
The registered projects are authoritative. This vault is a derived, human-readable wiki.
## Managed paths
DS4Server manages `purpose.md`, `schema.md`, `index.md`, `skills.md`, `log.md`, and topic pages below `projects/`, `subsystems/`, `concepts/`, `decisions/`, `invariants/`, `workflows/`, and `skills/`. Topic pages must opt in with `dev_brain: true`. Other notes, hidden files, Obsidian settings, attachments, and trash are never modified.
## Topic frontmatter
```yaml
---
dev_brain: true
type: subsystem # project, subsystem, concept, decision, invariant, workflow, or skill
project: Registered project name
status: verified # verified, stale, or needs-review
verified_at: 2026-07-27T12:00:00Z
sources:
- project: Registered project name
path: src/example.rs
symbol: optional_symbol
revision: latest-commit-containing-this-file-content
# For a clean tracked file, use `git log -1 --format=%H -- src/example.rs`,
# or a unique lowercase hex prefix of at least 7 characters. Use hash when
# this file differs from HEAD, is untracked, or the registered project is not Git.
---
```
`project` names a registered project, not this Dev Brain vault; `dev_brain_info` lists the exact registered names and folders. Each source `path` is relative to that registered project's folder; never resolve it inside the vault. Cite only files that support the page's claims, not every dirty file in the project. Each source has exactly one evidence version: `revision` or a lowercase SHA-256 `hash`. A revision is the newest commit that changed that source file, returned by `git log -1 --format=%H -- path`; validation requires that per-file revision and compares its file content with the current clean file. Every source therefore has its own revision; never stamp all sources with the repository's overall HEAD. Use a hash when that specific file differs from HEAD, is untracked, or its registered project is not Git.
## Skills
Skills are on-demand instructions stored as one Markdown file per skill below `skills/`. They use the same provenance and status fields as topic pages, set `type: skill`, and additionally require `name` and `description` frontmatter. Names contain 164 lowercase letters, digits, or hyphens, with no leading, trailing, or consecutive hyphens. Descriptions contain 11024 characters and explain both what the skill does and when to use it. Only skills whose status and current evidence are both `verified` appear in generated `skills.md` and the session system prompt. The prompt exposes each verified skill's name, description, and Markdown path; read the complete matching file on demand before following it. `index.md` lists all managed topic pages, while `skills.md` lists verified skills only. Do not hand-edit either generated index.
Files with `built_in: true` are app-managed skills restored from the DS4Server bundle. Do not create or edit them by hand.
```yaml
---
dev_brain: true
type: skill
name: review-release
description: Verify a release candidate against the project checklist. Use before publishing a release.
project: Registered project name
status: verified
verified_at: 2026-07-27T12:00:00Z
sources:
- project: Registered project name
path: docs/releasing.md
revision: full-or-unique-short-clean-git-revision
---
```
## Compilation
Read the purpose first. Examine high-signal manifests, documentation, schemas, entry points, public interfaces, and tests. Create the smallest coherent topic set that answers the purpose; do not mirror every source file. Use ordinary Obsidian wikilinks and embeds to connect topics.
## Querying
Start with `index.md` or ranked search, then follow links and backlinks. A `verified` page is authoritative only while all recorded evidence is current. Treat `stale` pages as navigation leads and `needs-review` pages as explicit uncertainty; check project sources before making claims from either.
## Refresh and semantic validation
When validation reports drifted sources, handle each listed file independently: re-read it, compare its current behavior with the page's documented findings, update, split, merge, or retire affected knowledge, then update only that source's revision or hash. For a large revision-backed file, `git diff <recorded-revision> -- path` shows the change from the recorded evidence to the current working-tree version; use it to focus the review, then read enough current context to validate the affected findings. Inspect the commits affecting that path since the recorded revision as well. When code was removed, inspect the full commits and search the current project, callers, and tests before concluding the behavior disappeared: it may have been renamed, replaced, or moved into another file that should replace or join the page's cited sources. Repeat for every listed file and re-run validation. Keep `index.md` exact, and append a material update entry to `log.md` with source revisions. Only mark a page `verified` after its claims have been checked against its current evidence. Unsupported conclusions stay `needs-review`.
## Publication
Call `dev_brain_info`, then maintain these pages with the ordinary file tools. Append material updates to `log.md`; `index.md` and `skills.md` are generated and should not be hand-maintained. Finish with `dev_brain_validate`. Structural or provenance errors must be repaired, while broken links are warnings so valid work remains editable instead of being discarded.

View File

@@ -0,0 +1,58 @@
---
dev_brain: true
type: skill
built_in: true
name: create-dev-brain-skill
description: Create or update a source-backed skill in Dev Brain. Use when adding reusable instructions that should activate on demand in future development tasks.
project: DS4Server built-in
status: verified
verified_at: 2026-07-30T00:00:00Z
sources: []
---
# Create a Dev Brain skill
Create a skill when a repeatable development task needs instructions beyond the model's general knowledge. Keep it concise and source-backed.
## File structure
A Dev Brain skill is one Markdown file, not a directory:
```text
skills/
└── skill-name.md
```
Use this structure:
```yaml
---
dev_brain: true
type: skill
name: skill-name
description: State what the skill does and when to use it.
project: Exact registered project name
status: needs-review # change to verified only after checking every claim
verified_at: 2026-07-30T12:00:00Z
sources:
- project: Exact registered project name
path: path/to/authoritative-source.md
revision: latest-commit-that-changed-this-file
---
# Human-readable skill title
Write direct instructions for completing the task. Link to related Dev Brain pages when useful.
```
Use lowercase letters, digits, and hyphens for `name`. Put all trigger conditions in `description`. Cite at least one registered project source, using the exact per-file revision for a clean Git file or its lowercase SHA-256 hash when required by `schema.md`.
## Create and activate
1. Call `dev_brain_info` and read `purpose.md` and `schema.md`.
2. Read the authoritative project sources for the instructions.
3. Write `skills/<name>.md` with current evidence and `status: verified`; use `needs-review` until every claim is checked.
4. Append the material change to `log.md`.
5. Run `dev_brain_validate`.
Validation is the activation step. It verifies the file, rebuilds generated `skills.md`, and makes the skill's name, description, and path available to the agent. Never edit `skills.md` directly.

View File

@@ -633,9 +633,15 @@ impl App {
}
Message::ConfirmRestoreDevBrainDefaultGuides => {
self.restore_dev_brain_confirmation = false;
self.preference_error = crate::dev_brain::restore_default_guides(Path::new(
&self.preference_draft.dev_brain_vault_path,
))
let projects = self
.projects
.iter()
.map(|project| project.project.clone())
.collect::<Vec<_>>();
self.preference_error = crate::dev_brain::restore_default_guides(
Path::new(&self.preference_draft.dev_brain_vault_path),
&projects,
)
.err();
}
Message::CancelRestoreDevBrainDefaultGuides => {

View File

@@ -199,9 +199,9 @@ impl App {
.size(12)
.width(Length::Fill),
hint(
action_button("Recreate purpose.md and schema.md")
action_button("Recreate guidance and skills")
.on_press(Message::RestoreDevBrainDefaultGuides),
"Overwrites only purpose.md and schema.md with this version's defaults. Topic pages, generated indexes, and log.md are preserved.",
"Overwrites purpose.md, schema.md, and included skills with this version's defaults. Other skill pages, generated indexes, topic pages, and log.md are preserved.",
),
]
.spacing(12)
@@ -752,7 +752,7 @@ impl App {
let confirmation = container(
column![
text("Recreate Dev Brain guidance?").size(22),
text("This replaces purpose.md and schema.md in the selected vault with this version's defaults. Topic pages, generated indexes, skills, and log.md are preserved.")
text("This replaces purpose.md, schema.md, and included skills in the selected vault with this version's defaults. Other skill pages, generated indexes, topic pages, and log.md are preserved.")
.size(13),
row![
Space::new().width(Length::Fill),

View File

@@ -38,87 +38,11 @@ pub(crate) const TOOL_SCHEMAS: &str = r#"{"type":"function","function":{"name":"
{"type":"function","function":{"name":"dev_brain_search","description":"Search the validated Dev Brain index with freshness and project evidence. Use ordinary search for literal or regex file search.","parameters":{"type":"object","properties":{"query":{"type":"string"},"limit":{"type":"number"},"authoritative":{"type":"boolean"}},"required":["query"]}}}
{"type":"function","function":{"name":"dev_brain_validate","description":"Validate managed pages after ordinary file edits, report every drifted project source for semantic reinspection and per-source revision updates, deterministically rebuild index.md and skills.md, refresh search, and report repairable link warnings.","parameters":{"type":"object","properties":{}}}}"#;
const DEFAULT_PURPOSE: &str = "# Dev Brain purpose\n\n\
Dev Brain compiles durable, source-backed knowledge from the registered projects.\n\n\
## Priorities\n\n\
- Architecture, behavior, decisions, invariants, workflows, and relationships.\n\
- Small topic pages that answer recurring development questions.\n\
- Visible uncertainty and exact project provenance.\n\n\
## Recurring questions\n\n\
- Where does a behavior live, and what must remain invariant when it changes?\n\
- Which decisions constrain the current implementation?\n\
- Which project sources must be rechecked before relying on this page?\n";
const DEFAULT_INDEX: &str = "# Dev Brain index\n\nNo topic pages have been compiled yet.\n";
const DEFAULT_SKILLS: &str = "# Dev Brain skills\n\nNo verified skills are available.\n";
const DEFAULT_LOG: &str =
"# Dev Brain log\n\n<!-- Append material wiki updates and their source revisions below. -->\n";
const DEFAULT_SCHEMA: &str = r#"# Dev Brain schema
The registered projects are authoritative. This vault is a derived, human-readable wiki.
## Managed paths
DS4Server manages `purpose.md`, `schema.md`, `index.md`, `skills.md`, `log.md`, and topic pages below `projects/`, `subsystems/`, `concepts/`, `decisions/`, `invariants/`, `workflows/`, and `skills/`. Topic pages must opt in with `dev_brain: true`. Other notes, hidden files, Obsidian settings, attachments, and trash are never modified.
## Topic frontmatter
```yaml
---
dev_brain: true
type: subsystem # project, subsystem, concept, decision, invariant, workflow, or skill
project: Registered project name
status: verified # verified, stale, or needs-review
verified_at: 2026-07-27T12:00:00Z
sources:
- project: Registered project name
path: src/example.rs
symbol: optional_symbol
revision: latest-commit-containing-this-file-content
# For a clean tracked file, use `git log -1 --format=%H -- src/example.rs`,
# or a unique lowercase hex prefix of at least 7 characters. Use hash when
# this file differs from HEAD, is untracked, or the registered project is not Git.
---
```
`project` names a registered project, not this Dev Brain vault; `dev_brain_info` lists the exact registered names and folders. Each source `path` is relative to that registered project's folder; never resolve it inside the vault. Cite only files that support the page's claims, not every dirty file in the project. Each source has exactly one evidence version: `revision` or a lowercase SHA-256 `hash`. A revision is the newest commit that changed that source file, returned by `git log -1 --format=%H -- path`; validation requires that per-file revision and compares its file content with the current clean file. Every source therefore has its own revision; never stamp all sources with the repository's overall HEAD. Use a hash when that specific file differs from HEAD, is untracked, or its registered project is not Git.
## Skills
Skills are on-demand instructions stored as Markdown below `skills/`. They use the same provenance and status fields as topic pages, set `type: skill`, and additionally require `name` and `description` frontmatter. Names contain 164 lowercase letters, digits, or hyphens, with no leading, trailing, or consecutive hyphens. Descriptions contain 11024 characters and explain both what the skill does and when to use it. Only skills whose status and current evidence are both `verified` appear in generated `skills.md` and the session system prompt. The prompt exposes each verified skill's name, description, and Markdown path; read the complete matching file on demand before following it. `index.md` lists all managed topic pages, while `skills.md` lists verified skills only. Do not hand-edit either generated index.
```yaml
---
dev_brain: true
type: skill
name: review-release
description: Verify a release candidate against the project checklist. Use before publishing a release.
project: Registered project name
status: verified
verified_at: 2026-07-27T12:00:00Z
sources:
- project: Registered project name
path: docs/releasing.md
revision: full-or-unique-short-clean-git-revision
---
```
## Compilation
Read the purpose first. Examine high-signal manifests, documentation, schemas, entry points, public interfaces, and tests. Create the smallest coherent topic set that answers the purpose; do not mirror every source file. Use ordinary Obsidian wikilinks and embeds to connect topics.
## Querying
Start with `index.md` or ranked search, then follow links and backlinks. A `verified` page is authoritative only while all recorded evidence is current. Treat `stale` pages as navigation leads and `needs-review` pages as explicit uncertainty; check project sources before making claims from either.
## Refresh and semantic validation
When validation reports drifted sources, handle each listed file independently: re-read it, compare its current behavior with the page's documented findings, update, split, merge, or retire affected knowledge, then update only that source's revision or hash. For a large revision-backed file, `git diff <recorded-revision> -- path` shows the change from the recorded evidence to the current working-tree version; use it to focus the review, then read enough current context to validate the affected findings. Inspect the commits affecting that path since the recorded revision as well. When code was removed, inspect the full commits and search the current project, callers, and tests before concluding the behavior disappeared: it may have been renamed, replaced, or moved into another file that should replace or join the page's cited sources. Repeat for every listed file and re-run validation. Keep `index.md` exact, and append a material update entry to `log.md` with source revisions. Only mark a page `verified` after its claims have been checked against its current evidence. Unsupported conclusions stay `needs-review`.
## Publication
Call `dev_brain_info`, then maintain these pages with the ordinary file tools. Append material updates to `log.md`; `index.md` and `skills.md` are generated and should not be hand-maintained. Finish with `dev_brain_validate`. Structural or provenance errors must be repaired, while broken links are warnings so valid work remains editable instead of being discarded.
"#;
const INCLUDED_SKILLS: [&str; 1] = ["skills/create-dev-brain-skill.md"];
#[derive(Clone)]
struct RegisteredProject {
@@ -145,6 +69,8 @@ struct TopicFrontmatter {
dev_brain: bool,
#[serde(rename = "type")]
page_type: String,
#[serde(default)]
built_in: bool,
project: String,
status: String,
verified_at: String,
@@ -692,11 +618,7 @@ pub(crate) fn skills_prompt(
let vault = config.vault()?;
ensure_contract(&vault)?;
let projects = registered_projects(projects)?;
let pages = load_and_validate_pages(&vault, &vault, &projects, false)?;
let skills = render_skills(&pages);
if fs::read_to_string(vault.join("skills.md")).map_or(true, |current| current != skills) {
publish_files(&vault, "skills", &[("skills.md", &skills)])?;
}
let skills = publish_skills_index(&vault, &projects)?;
Ok(format!(
"# Available Dev Brain skills\n\nThe following generated index contains verified skills only. Paths are relative to {}. When a task matches, read the complete Markdown file before acting.\n\n{}",
vault.display(),
@@ -732,7 +654,7 @@ fn registered_projects(projects: &[Project]) -> Result<Vec<RegisteredProject>, S
Ok(projects)
}
pub(crate) fn restore_default_guides(vault: &Path) -> Result<(), String> {
pub(crate) fn restore_default_guides(vault: &Path, projects: &[Project]) -> Result<(), String> {
let _guard = VAULT_LOCK
.write()
.unwrap_or_else(|poisoned| poisoned.into_inner());
@@ -742,13 +664,22 @@ pub(crate) fn restore_default_guides(vault: &Path) -> Result<(), String> {
"The selected vault has no Dev Brain index.md and log.md contract to restore.".into(),
);
}
publish_defaults(
&vault,
&[
("purpose.md", DEFAULT_PURPOSE),
("schema.md", DEFAULT_SCHEMA),
],
)
let purpose = read_default_asset("purpose.md")?;
let schema = read_default_asset("schema.md")?;
let skills = read_included_skills()?;
let mut files = vec![
("purpose.md", purpose.as_str()),
("schema.md", schema.as_str()),
];
files.extend(
skills
.iter()
.map(|(path, content)| (*path, content.as_str())),
);
publish_defaults(&vault, &files)?;
let projects = registered_projects(projects)?;
publish_skills_index(&vault, &projects)?;
Ok(())
}
fn ensure_contract(vault: &Path) -> Result<(), String> {
@@ -773,16 +704,22 @@ fn ensure_contract(vault: &Path) -> Result<(), String> {
);
}
if present == 0 {
publish_defaults(
vault,
&[
("purpose.md", DEFAULT_PURPOSE),
("schema.md", DEFAULT_SCHEMA),
("index.md", DEFAULT_INDEX),
("skills.md", DEFAULT_SKILLS),
("log.md", DEFAULT_LOG),
],
)?;
let purpose = read_default_asset("purpose.md")?;
let schema = read_default_asset("schema.md")?;
let skills = read_included_skills()?;
let mut files = vec![
("purpose.md", purpose.as_str()),
("schema.md", schema.as_str()),
("index.md", DEFAULT_INDEX),
("skills.md", DEFAULT_SKILLS),
("log.md", DEFAULT_LOG),
];
files.extend(
skills
.iter()
.map(|(path, content)| (*path, content.as_str())),
);
publish_defaults(vault, &files)?;
} else if !skills_path.is_file() {
publish_defaults(vault, &[("skills.md", DEFAULT_SKILLS)])?;
}
@@ -809,7 +746,11 @@ fn publish_files(vault: &Path, label: &str, files: &[(&str, &str)]) -> Result<()
let result = (|| {
let mut changed = BTreeSet::new();
for (path, content) in files {
fs::write(staging.join(path), content).map_err(|error| error.to_string())?;
let destination = staging.join(path);
if let Some(parent) = destination.parent() {
fs::create_dir_all(parent).map_err(|error| error.to_string())?;
}
fs::write(destination, content).map_err(|error| error.to_string())?;
changed.insert(PathBuf::from(path));
}
commit_batch(vault, &staging, &changed)
@@ -818,6 +759,41 @@ fn publish_files(vault: &Path, label: &str, files: &[(&str, &str)]) -> Result<()
result
}
fn read_default_asset(path: &str) -> Result<String, String> {
let bundled = std::env::current_exe().ok().and_then(|path| {
path.parent()?
.parent()
.map(|path| path.join("Resources/dev-brain"))
});
let checkout = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/dev-brain");
let directory = bundled
.filter(|path| path.join("purpose.md").is_file())
.unwrap_or(checkout);
let asset = directory.join(path);
fs::read_to_string(&asset).map_err(|error| {
format!(
"Could not read bundled Dev Brain asset {}: {error}",
asset.display()
)
})
}
fn read_included_skills() -> Result<Vec<(&'static str, String)>, String> {
INCLUDED_SKILLS
.iter()
.map(|path| read_default_asset(path).map(|content| (*path, content)))
.collect()
}
fn publish_skills_index(vault: &Path, projects: &[RegisteredProject]) -> Result<String, String> {
let pages = load_and_validate_pages(vault, vault, projects, false)?;
let skills = render_skills(&pages);
if fs::read_to_string(vault.join("skills.md")).map_or(true, |current| current != skills) {
publish_files(vault, "skills", &[("skills.md", &skills)])?;
}
Ok(skills)
}
pub(crate) fn validate_vault(path: &Path) -> Result<PathBuf, String> {
let vault = path
.canonicalize()
@@ -916,6 +892,7 @@ fn load_and_validate_pages(
}
validate_page_type(&path, &frontmatter.page_type)?;
validate_skill_metadata(&frontmatter, &relative)?;
validate_built_in_skill(&frontmatter, &relative, &content)?;
if !matches!(
frontmatter.status.as_str(),
"verified" | "stale" | "needs-review"
@@ -927,39 +904,44 @@ fn load_and_validate_pages(
}
OffsetDateTime::parse(&frontmatter.verified_at, &Rfc3339)
.map_err(|_| format!("{relative} verified_at must be an RFC 3339 timestamp."))?;
if frontmatter.sources.is_empty() {
return Err(format!("{relative} must cite at least one project source."));
}
if !projects
.iter()
.any(|project| project.name == frontmatter.project)
{
return Err(format!(
"{relative} names an unregistered project: {}",
frontmatter.project
));
}
if !frontmatter
.sources
.iter()
.any(|source| source.project == frontmatter.project)
{
return Err(format!(
"{relative} must cite evidence from its declared project {}.",
frontmatter.project
));
}
let drifted = frontmatter
.sources
.iter()
.map(|source| {
validate_source(source, projects)
.map(|fresh| (!fresh).then(|| format!("{}:{}", source.project, source.path)))
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect::<Vec<_>>();
let drifted = if frontmatter.built_in {
Vec::new()
} else {
if frontmatter.sources.is_empty() {
return Err(format!("{relative} must cite at least one project source."));
}
if !projects
.iter()
.any(|project| project.name == frontmatter.project)
{
return Err(format!(
"{relative} names an unregistered project: {}",
frontmatter.project
));
}
if !frontmatter
.sources
.iter()
.any(|source| source.project == frontmatter.project)
{
return Err(format!(
"{relative} must cite evidence from its declared project {}.",
frontmatter.project
));
}
frontmatter
.sources
.iter()
.map(|source| {
validate_source(source, projects).map(|fresh| {
(!fresh).then(|| format!("{}:{}", source.project, source.path))
})
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect::<Vec<_>>()
};
if publishing && frontmatter.status == "verified" && !drifted.is_empty() {
return Err(format!(
"{relative} cannot be published as verified because its evidence has drifted:\n- {}\nRe-read each listed source and check whether its changes alter the page's documented findings. For a large revision-backed source, use `git diff <recorded-revision> -- path` to focus on what changed, then inspect the necessary current context and the commits affecting that path since the recorded revision. If code disappeared, inspect the full change commits and search the current project, callers, and tests for a rename, replacement, or move to another file before concluding the behavior was removed; update the cited source list when evidence moved. Update the knowledge when needed, then set only that source's revision to `git log -1 --format=%H -- path` (not the repository's overall HEAD), or its hash when required. Re-run dev_brain_validate after every listed source is current.",
@@ -1408,9 +1390,9 @@ fn validate_page_type(path: &Path, page_type: &str) -> Result<(), String> {
fn validate_skill_metadata(frontmatter: &TopicFrontmatter, path: &str) -> Result<(), String> {
if frontmatter.page_type != "skill" {
if frontmatter.name.is_some() || frontmatter.description.is_some() {
if frontmatter.built_in || frontmatter.name.is_some() || frontmatter.description.is_some() {
return Err(format!(
"{path} may use name and description only when type is skill."
"{path} may use built_in, name, and description only when type is skill."
));
}
return Ok(());
@@ -1443,6 +1425,25 @@ fn validate_skill_metadata(frontmatter: &TopicFrontmatter, path: &str) -> Result
Ok(())
}
fn validate_built_in_skill(
frontmatter: &TopicFrontmatter,
path: &str,
content: &str,
) -> Result<(), String> {
if !frontmatter.built_in {
return Ok(());
}
if !INCLUDED_SKILLS.contains(&path) {
return Err(format!("{path} is not an included Dev Brain skill."));
}
if read_default_asset(path)? != content {
return Err(format!(
"{path} differs from the included Dev Brain skill. Restore the built-in guidance from Preferences."
));
}
Ok(())
}
fn normalize_managed_path(value: &str) -> Result<PathBuf, String> {
let path = normalize_relative_path(value)?;
if path.extension().is_none_or(|extension| extension != "md") {
@@ -1776,9 +1777,11 @@ mod tests {
#[test]
fn skill_index_migration_does_not_overwrite_an_unrelated_root_note() {
let fixture = Fixture::new();
let purpose = read_default_asset("purpose.md").unwrap();
let schema = read_default_asset("schema.md").unwrap();
for (path, content) in [
("purpose.md", DEFAULT_PURPOSE),
("schema.md", DEFAULT_SCHEMA),
("purpose.md", purpose.as_str()),
("schema.md", schema.as_str()),
("index.md", DEFAULT_INDEX),
("log.md", DEFAULT_LOG),
] {
@@ -1804,6 +1807,10 @@ mod tests {
fs::read_to_string(fixture.vault.join("skills.md")).unwrap(),
DEFAULT_SKILLS
);
assert_eq!(
fs::read_to_string(fixture.vault.join(INCLUDED_SKILLS[0])).unwrap(),
read_default_asset(INCLUDED_SKILLS[0]).unwrap()
);
drop(brain);
fs::remove_file(fixture.vault.join("skills.md")).unwrap();
let mut brain = fixture.brain();
@@ -1847,9 +1854,11 @@ mod tests {
fs::write(fixture.project.join("source.rs"), "changed\n").unwrap();
let prompt = skills_prompt(&fixture.config(), &fixture.projects()).unwrap();
assert!(!prompt.contains("review-release"));
assert_eq!(
fs::read_to_string(fixture.vault.join("skills.md")).unwrap(),
DEFAULT_SKILLS
assert!(prompt.contains("create-dev-brain-skill"));
assert!(
fs::read_to_string(fixture.vault.join("skills.md"))
.unwrap()
.contains("create-dev-brain-skill")
);
}
@@ -1872,19 +1881,56 @@ mod tests {
assert!(brain.validate().unwrap_err().contains("skill name"));
fs::remove_file(fixture.vault.join("skills/invalid.md")).unwrap();
fs::write(
fixture.vault.join(INCLUDED_SKILLS[0]),
format!("{}\n", read_default_asset(INCLUDED_SKILLS[0]).unwrap()),
)
.unwrap();
assert!(
brain
.validate()
.unwrap_err()
.contains("differs from the included Dev Brain skill")
);
fs::write(fixture.vault.join("purpose.md"), "custom purpose\n").unwrap();
fs::write(fixture.vault.join("schema.md"), "custom schema\n").unwrap();
fs::write(
fixture.vault.join(INCLUDED_SKILLS[0]),
"custom included skill\n",
)
.unwrap();
let custom_skill = fixture.skill(
"verified",
&hash,
"custom-skill",
"Keep custom instructions. Use for custom work.",
"Custom skill",
);
fs::write(fixture.vault.join("skills/custom.md"), &custom_skill).unwrap();
fs::write(fixture.vault.join("log.md"), "preserved log\n").unwrap();
fs::write(fixture.vault.join("index.md"), "preserved index\n").unwrap();
restore_default_guides(&fixture.vault).unwrap();
fs::write(fixture.vault.join("skills.md"), "stale skill index\n").unwrap();
restore_default_guides(&fixture.vault, &fixture.projects()).unwrap();
assert_eq!(
fs::read_to_string(fixture.vault.join("purpose.md")).unwrap(),
DEFAULT_PURPOSE
read_default_asset("purpose.md").unwrap()
);
assert_eq!(
fs::read_to_string(fixture.vault.join("schema.md")).unwrap(),
DEFAULT_SCHEMA
read_default_asset("schema.md").unwrap()
);
assert_eq!(
fs::read_to_string(fixture.vault.join(INCLUDED_SKILLS[0])).unwrap(),
read_default_asset(INCLUDED_SKILLS[0]).unwrap()
);
assert_eq!(
fs::read_to_string(fixture.vault.join("skills/custom.md")).unwrap(),
custom_skill
);
let skills = fs::read_to_string(fixture.vault.join("skills.md")).unwrap();
assert!(skills.contains("create-dev-brain-skill"));
assert!(skills.contains("custom-skill"));
assert_eq!(
fs::read_to_string(fixture.vault.join("log.md")).unwrap(),
"preserved log\n"