Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A portable seed for a repo-local `.agents` control plane.

Use it as a seed: install the template into a target repo, review the diff, and localize it there.

Treat this repo as a quarry: take the control-plane pieces that fit, but do not make `agents-kit` a runtime dependency. After install, the target repo owns local doctrine; `.agents/skills/agents-kit/**` stays seed-managed unless deliberately forked.
Treat this repo as a quarry: take the control-plane pieces that fit, but do not make `agents-kit` a runtime dependency. After install, the target repo owns local doctrine; `.agents/skills/manifest.json` declares skill ownership, and `.agents/skills/agents-kit/**` stays seed-managed unless deliberately forked.

For a visual walkthrough of the harness, open [dot-agents-system.html](dot-agents-system.html).

Expand All @@ -15,7 +15,9 @@ For a visual walkthrough of the harness, open [dot-agents-system.html](dot-agent
- `AGENTS.md` points to the control plane.
- `.agents/router.md` dispatches task shape to resolver, gate, and skill.
- `.agents/resolvers/*` scope reads, writes, owners, and non-goals.
- `.agents/gates/*` verify done with observable checks.
- `.agents/gates/*` name observable done checks.
- `.agents/commands/*` collapse repeated workflow hops.
- `.agents/checks/*` prove objective agent-process or repo-ownership invariants.
- `.agents/skills/*` teach repeatable technique.
- `.agents/logs/*` orient handoff.
- `history/*` preserves dated evidence.
Expand Down Expand Up @@ -58,7 +60,7 @@ Use `adopt` when a repo already has local agent files.
npx github:callumflack/agents-kit adopt --target /path/to/repo
```

`adopt` creates missing seed files only. Existing local files are reported as `keep local`; differing files get review diffs. Merge useful seed doctrine manually.
`adopt` creates missing seed files only. Existing local files are reported as `keep local`; differing files get review diffs. Merge useful seed doctrine manually. If an older target has imported or materialized skills but no `.agents/skills/manifest.json`, `adopt` refuses to invent owners or materialization hashes; create and verify the manifest explicitly first.

### Update

Expand All @@ -68,7 +70,7 @@ Use `update` to roll an existing installation forward from this seed.
npx github:callumflack/agents-kit update --target /path/to/repo
```

`update` requires a clean target git worktree before writing. It creates missing files, keeps local doctrine by default, and prints review diffs for changed files.
`update` requires a clean target git worktree before writing. It creates missing files, keeps local doctrine by default, and prints review diffs for changed files. The same fail-closed manifest preflight protects older skill inventories.

Replace changed non-doctrine seed files:

Expand Down Expand Up @@ -105,19 +107,23 @@ skills-lock.json
README.md
agent-tooling.md
factory-failure.md
rule-rinse.md
gates/
README.md
agent-tooling.md
factory-failure.md
rule-rinse.md
commands/
README.md
checks/
README.md
skills/
README.md
manifest.json
agents-kit/
SKILL.md
scripts/
check-agents-kit-health.py
check-skill-frontmatter.py
hash-materialized-skill.mjs
logs/
README.md
.scratch/
Expand All @@ -137,10 +143,13 @@ During `update`, these files are review-only and are not overwritten:
```text
AGENTS.md
skills-lock.json
.agents/skills/manifest.json
.agents/AGENT-CONTROL-PLANE.md
.agents/router.md
.agents/resolvers/*
.agents/gates/*
.agents/commands/*
.agents/checks/*
.agents/logs/*
history/*
.scratch/*
Expand All @@ -156,8 +165,9 @@ After install:
2. Replace placeholder router rows only after live repo evidence exists.
3. Add repo-specific resolvers for recurring task lanes.
4. Add gates with concrete checks, not vague verification language.
5. Keep logs for handoff context, not live law.
6. Keep dated evidence in `history/`.
5. Add commands only for repeated hop-collapsers; add checks only for objective oracles the repo can observe.
6. Keep logs for handoff context, not live law.
7. Keep dated evidence in `history/`.

## Verify

Expand All @@ -173,4 +183,4 @@ In an installed target repo:
python3 .agents/skills/agents-kit/scripts/check-agents-kit-health.py
```

The source verifier checks the transported template file list and runs the installed health check against `templates/default`.
The source verifier self-tests prove clean installation and template health, legacy-upgrade refusal, conflict-marker rejection, shipped seed ownership, generic installed ownership, path confinement, and materialized-import hashes. The verifier then checks the transported template file list and runs the installed health check against `templates/default`. Pass `--root <template-root>` to verify a copied template through the same entrypoint.
46 changes: 46 additions & 0 deletions bin/agents-kit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,13 @@ function parseArgs(argv) {
function isReviewOnly(file) {
return file === "AGENTS.md" ||
file === "skills-lock.json" ||
file === ".agents/skills/manifest.json" ||
file === ".agents/AGENT-CONTROL-PLANE.md" ||
file === ".agents/router.md" ||
file.startsWith(".agents/resolvers/") ||
file.startsWith(".agents/gates/") ||
file.startsWith(".agents/commands/") ||
file.startsWith(".agents/checks/") ||
file.startsWith(".agents/logs/") ||
file.startsWith("history/") ||
file.startsWith(".scratch/")
Expand Down Expand Up @@ -123,6 +126,47 @@ async function sameFile(source, target) {
}
}

async function assertManifestUpgradeSafe(targetRoot) {
const manifestPath = path.join(targetRoot, ".agents", "skills", "manifest.json")
if (await exists(manifestPath)) return

const lockPath = path.join(targetRoot, "skills-lock.json")
let lockedNames = []
if (await exists(lockPath)) {
let lock
try {
lock = JSON.parse(await fs.readFile(lockPath, "utf8"))
} catch (error) {
throw new Error(`cannot inspect legacy skills-lock.json: ${error.message}`)
}
if (!lock?.skills || typeof lock.skills !== "object" || Array.isArray(lock.skills)) {
throw new Error("cannot inspect legacy skills-lock.json: missing skills object")
}
lockedNames = Object.keys(lock.skills)
}

const skillsRoot = path.join(targetRoot, ".agents", "skills")
let materializedNames = []
try {
const entries = await fs.readdir(skillsRoot, { withFileTypes: true })
materializedNames = entries
.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && entry.name !== "agents-kit")
.map((entry) => entry.name)
} catch (error) {
if (error.code !== "ENOENT") throw error
}

if (lockedNames.length === 0 && materializedNames.length === 0) return

const details = []
if (lockedNames.length > 0) details.push(`locked imports: ${lockedNames.sort().join(", ")}`)
if (materializedNames.length > 0) details.push(`materialized skill directories: ${materializedNames.sort().join(", ")}`)
throw new Error(
`refusing to create .agents/skills/manifest.json in an existing skill inventory (${details.join("; ")}). ` +
"The installer will not infer ownership or materialization hashes. Create and verify the manifest explicitly, then rerun.",
)
}

function printDiff(source, target, file) {
const diff = spawnSync("diff", [
"-u",
Expand Down Expand Up @@ -246,6 +290,7 @@ async function adopt(options) {
}

const targetRoot = path.resolve(options.target)
await assertManifestUpgradeSafe(targetRoot)
const files = await listFiles(templateRoot)
const skipped = []

Expand Down Expand Up @@ -292,6 +337,7 @@ async function adopt(options) {
async function update(options) {
const targetRoot = path.resolve(options.target)
assertCleanWorktree(targetRoot, options)
await assertManifestUpgradeSafe(targetRoot)

const files = await listFiles(templateRoot)
const changed = []
Expand Down
3 changes: 3 additions & 0 deletions docs/agents-kit-maintainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ It may mention:
- `.agents/resolvers/*`
- `.agents/gates/*`
- `.agents/skills/*`
- `.agents/skills/manifest.json`
- `.agents/logs/*`
- `history/*`
- `skills-lock.json`
- `python3 .agents/skills/agents-kit/scripts/check-skill-frontmatter.py "$PWD/.agents/skills/<name>/SKILL.md"`
- `python3 .agents/skills/agents-kit/scripts/check-agents-kit-health.py`

It should stay procedural and point to the installed router, control-plane doctrine, resolver, and gate rather than repeat their placement maps.

It must not mention:

- `templates/default/**`
Expand Down
Loading