Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/maintainer-authoring-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/intent': patch
---

Record developer tasks at registration with `maintainer add --task`, retire a registered skill with `maintainer remove <name>` without deleting its guidance, and include the recording contract (allowed outcomes and required fields) in `review --json` reports.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Run the maintainer workflow

Use the repository's Intent command for all six actions. These commands perform bookkeeping; the maintainer or coding agent still supplies task knowledge, source-backed guidance, and review conclusions.
Use the repository's Intent command for these actions. `intent maintainer --help` lists them in order with what each one writes; `intent maintainer <action> --help` lists one action's options. These commands perform bookkeeping; the maintainer or coding agent still supplies task knowledge, source-backed guidance, and review conclusions.

1. Run `intent maintainer setup` once. It installs repository guidance and creates missing planning records, preserving existing documents. A monorepo uses one shared record and package-owned skill directories. If several record locations exist, select the established one with `--artifacts <repository-relative-directory>`; do not merge them by guessing. Read [repository distribution](distribution.md) and explain the option to the maintainer during setup. Save the selected skills or opt-out with the setup command; do not infer a public selection from directory placement or repeat a recorded decision.
2. For a new task, run `intent maintainer add <name> --domain <slug> --description <activation-text> --source <path>`. In a monorepo, pass `--package packages/<owner>`. Repeat `--source` or `--requires` for multiple entries. Source paths are relative to the owning package; `owner/repo:path` is relative to the repository. Use `--path <package-relative-path>/SKILL.md` for an established custom layout. To register an existing skill, supply its name, domain, package, and path; its frontmatter supplies the other fields.
3. Author the skill and reconcile all three records using the procedures in this skill. The command creates a skeleton and a domain-map entry with unassessed task coverage. Write that coverage and the spec's decisions/history. Remove `<!-- intent:needs-authoring -->` only after authoring the corresponding document. Do not remove it simply to make a check pass.
2. For a new task, run `intent maintainer add <name> --domain <slug> --description <activation-text> --source <path> --task <developer-task>`. Repeat `--source`, `--requires`, and `--task` for multiple entries; each `--task` becomes an assessed developer task in the domain map. In a monorepo, run the command from the owning package directory or pass `--package packages/<owner>` relative to the repository root. Source paths are relative to the owning package; `owner/repo:path` is relative to the repository. Use `--path <package-relative-path>/SKILL.md` for an established custom layout. To register an existing skill, supply its name, domain, package, and path; its frontmatter supplies the other fields. The command prints every file it wrote.
3. Author the skill and reconcile all three records using the procedures in this skill. The command creates a skeleton and a domain-map entry; add any tasks not supplied on the command line. Write the spec's decisions/history. Remove `<!-- intent:needs-authoring -->` only after authoring the corresponding document. Do not remove it simply to make a check pass. To retire a registered skill, run `intent maintainer remove <name>`; it marks the tree entry `retired` and notes it in the spec without deleting the file, and refuses while the skill is selected for distribution or required by another skill.
4. Run `intent maintainer status` to see missing work, stale metadata, and pending reviews. `--json` includes the full source-review report. For a supplied PR base, use `--base <ref>`.
5. Run `intent maintainer sync` after edits. It copies descriptions, purpose, sources, and prerequisites from registered skills into the tree, repairs the tree's record links, and includes the skill directories in existing package `files` allowlists. It preserves authored map/spec content and other manifest fields. It does not change version claims or run a package release. An absent `files` allowlist stays absent so npm's default contents are preserved; check the actual packed archive as part of the package's release checks.
6. Follow [source review](source-review.md) with `intent maintainer review --json`, supply justified outcomes, and record them with `intent maintainer review --record .intent/review.json`. The command retains the existing revision and content-fingerprint checks. Run `intent maintainer check` after recording; it exits nonzero for incomplete authoring, stale generated metadata, invalid skills, missing local prerequisites, or pending reviews. Use the same check in CI, passing the actual PR base.
6. Follow [source review](source-review.md) with `intent maintainer review --json`, supply justified outcomes, and record them with `intent maintainer review --record .intent/review.json`. A maintainer working in a terminal can do the same with `intent maintainer review --interactive`. The command retains the existing revision and content-fingerprint checks. Run `intent maintainer check` after recording; it exits nonzero for incomplete authoring, stale generated metadata, invalid skills, missing local prerequisites, or pending reviews. Use the same check in CI, passing the actual PR base.

Keep unimplemented future skills in the tree with `status: planned` and retired entries with `status: retired`. They remain part of the cumulative record but do not count as implemented skills or enter package publishing configuration. An active entry with a missing file is an error to resolve, not an invitation to delete the entry. Local prerequisite slugs are checked against implemented tree entries; verify external package prerequisites and the developer task through the task-quality procedure.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,28 @@ Run `intent maintainer setup` to create missing record skeletons without replaci

## Use the existing formats

Read [the domain map and skill spec formats](../../domain-discovery/references/artifacts.md) for those two documents and [the skill tree format](../../tree-generator/SKILL.md#scaffold-flow-output) for the third. Use the formats without entering full-library discovery or its interviews. The batch’s existing source research supplies discoverable facts; ask only for unresolved maintainer decisions.
Read [the domain map and skill spec formats](../../domain-discovery/references/artifacts.md) for those two documents. The skill tree uses the entry format below; `maintainer add` writes the required fields and `maintainer sync` keeps `description`, `purpose`, `sources`, and `requires` aligned with the skill's frontmatter. Read [the full tree format](../../tree-generator/SKILL.md#scaffold-flow-output) only for a full-library scaffold. Use the formats without entering full-library discovery or its interviews. The batch’s existing source research supplies discoverable facts; ask only for unresolved maintainer decisions.

```yaml
library: { name: '[package-name]', version: '[version]' }
generated_from:
domain_map: skills/_artifacts/domain_map.yaml
skill_spec: skills/_artifacts/skill_spec.md
review:
ignore: ['[git glob]'] # optional: paths never reported as unmapped changes
distribution: { mode: none } # or { mode: repo, repository: owner/repo, name: plugin, skills: [slug] }
skills:
- name: '[task-focused skill name]'
slug: '[kebab-case]'
domain: '[domain slug]'
package: '[package directory]' # monorepo only; path is then relative to it
path: skills/[slug]/SKILL.md
status: planned | retired # omit for an implemented skill
description: '[activation guidance; synchronized from SKILL.md]'
purpose: '[descriptive explanation; synchronized from metadata.purpose]'
requires: ['[other skill slugs]'] # omit if none
sources: ['src/[path].ts', 'owner/repo:docs/[path].md']
```

- `domain_map.yaml` owns the domain/task relationships, supported failure modes, cross-references, tensions, and knowledge gaps. Keep task slugs and package ownership aligned with the skills.
- `skill_spec.md` is the human-readable coverage and decision record. Retain the existing inventories and add a **Coverage and batch history** section recording the assessed scope, each completed batch or behavior change, its source revision/version, consequential decisions and reasons, check outcomes, and remaining work. Keep entries concise; do not store transcripts. Distinguish implemented guidance from planned work and unassessed areas.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ For a new batch, review the selected skills. Other items remain pending unless t

Read [the planning record procedure](planning-records.md), then review the actual diff, affected source, skill, references, relevant tests, and all three planning documents. Create missing records and reconcile changed coverage, recommendations, dependencies, and maintainer decisions before handoff. Preserve unrelated record entries and future work. `changedFiles` can be empty for an initial review. A recorded content snapshot detects edits even if the earlier review happened before a commit; it does not retain the earlier source text. Use an available Git diff or explain when historical text is unavailable. Missing sources, foreign repositories, conflicts, or unavailable history remain unknown until the evidence is resolved.

Plain `sources` paths are relative to the skill's nearest owning package, or the Git root when no package exists. `owner/repository:path` paths are relative to the Git root and must match the local repository's origin or root package repository metadata. Git glob syntax supports `*`, `?`, character classes and `**`; brace expansion and extglobs are unsupported. Keep evidence paths accurate. The command reviews Git-visible `SKILL.md` files under `skills/`, custom skill directories containing `_artifacts/`, and paths declared in an existing `skill_tree.yaml`. Previously recorded skills remain covered. Shared repository/package planning directories do not select every agent instruction in the package; list custom skill paths in the tree, using its `package` field for monorepo ownership. Installed dependencies under `node_modules` are excluded. Ignored files and external repositories still require explicit manual review.
Agent instruction files, generated plugin metadata, the generated CI workflow, `.intent/`, package manifests, and lockfiles are never reported as unmapped changes; a skill that lists one of them in `sources` still tracks it. Add repository-specific patterns under `review.ignore` in `skill_tree.yaml`. Plain `sources` paths are relative to the skill's nearest owning package, or the Git root when no package exists. `owner/repository:path` paths are relative to the Git root and must match the local repository's origin or root package repository metadata. Git glob syntax supports `*`, `?`, character classes and `**`; brace expansion and extglobs are unsupported. Keep evidence paths accurate. The command reviews Git-visible `SKILL.md` files under `skills/`, custom skill directories containing `_artifacts/`, and paths declared in an existing `skill_tree.yaml`. Previously recorded skills remain covered. Shared repository/package planning directories do not select every agent instruction in the package; list custom skill paths in the tree, using its `package` field for monorepo ownership. Installed dependencies under `node_modules` are excluded. Ignored files and external repositories still require explicit manual review.

## Record only completed reviews

After guidance edits and task checks, regenerate the JSON report. Save it outside tracked source paths, such as `.intent/review.json` after creating `.intent/`, so it cannot become its own review input. Include the `planning` item when the batch changes source or guidance, even when all three documents remain accurate. Annotate the selected items with:
After guidance edits and task checks, regenerate the JSON report. Save it outside tracked source paths, such as `.intent/review.json`, so it cannot become its own review input. The report's `recording` block lists the allowed outcomes and required fields. Include the `planning` item when the batch changes source or guidance, even when all three documents remain accurate. Annotate the selected items with:

- `outcome`: `updated`, `no-change`, `out-of-scope`, or `unresolved`.
- `reason`: the concrete behavior comparison and why that outcome follows.
- `evidence`: source paths/revisions and actual check results. For behavior-changing guidance, include structural validation, executable task checks, and fresh-consumer evidence or its explicit limitation.

Preserve the report's identity, base and fingerprints. Run `intent maintainer review --record .intent/review.json`. For planning items, use `updated` or an evidence-backed `no-change` covering all three documents. The command rejects stale fingerprints and unresolved source mappings or planning files. It writes completed outcomes to `.intent/review-state.json`; unresolved or unannotated items stay pending. Do not invent passing checks, use a generic reason, or mark unrelated items complete just to empty the report.
Preserve the report's identity, base and fingerprints. Run `intent maintainer review --record .intent/review.json`. For planning items, use `updated` or an evidence-backed `no-change` covering all three documents. The command rejects a report that annotates nothing, stale fingerprints, and unresolved source mappings or planning files. It writes completed outcomes to `.intent/review-state.json`; unresolved or unannotated items stay pending. Do not invent passing checks, use a generic reason, or mark unrelated items complete just to empty the report.

Keep the state file with the source/skill change for maintainer review. It contains content hashes, revisions, outcomes and evidence, not source contents. Record operations do not commit or publish. An identical content snapshot suppresses repeated reminders, including a justified no-op; another source or guidance change reopens review. This records an evidence-backed decision, not independent proof that the decision is correct.

Expand Down
7 changes: 6 additions & 1 deletion packages/intent/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function createCli(
'Set up, author, synchronize, and check library skills',
)
.usage(
'maintainer <setup|adopt|add|status|sync|review|check> [name] [options]',
'maintainer <setup|adopt|add|remove|status|sync|review|check> [name] [options]',
)
.option(
'--artifacts <directory>',
Expand Down Expand Up @@ -238,6 +238,10 @@ function createCli(
'--requires <name>',
'Prerequisite skill; repeat for multiple skills',
)
.option(
'--task <text>',
'Developer task a new skill covers; repeat for multiple tasks',
)
.option('--base <ref>', 'Git revision to review against')
.option(
'--interactive',
Expand All @@ -255,6 +259,7 @@ function createCli(
.example(
'maintainer add caching --domain queries --description "Use when caching queries." --source "src/**"',
)
.example('maintainer remove caching')
.example('maintainer status --json')
.example('maintainer sync')
.example('maintainer review --json')
Expand Down
28 changes: 25 additions & 3 deletions packages/intent/src/commands/maintainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
setupRecords,
} from '../maintainer/project.js'
import { addSkill } from '../maintainer/add.js'
import { retireSkill } from '../maintainer/remove.js'
import { createAdoptionPlan, planAdoptionChanges } from '../maintainer/adopt.js'
import { planMaintainerSync } from '../maintainer/sync.js'
import { withMaintainerLock, writeChanges } from '../maintainer/files.js'
Expand Down Expand Up @@ -75,6 +76,7 @@ const optionHelp: Record<string, [flag: string, description: string]> = {
],
source: ['--source <path>', 'Source evidence path; repeat for more'],
requires: ['--requires <name>', 'Prerequisite skill; repeat for more'],
task: ['--task <text>', 'Developer task the skill covers; repeat for more'],
base: ['--base <ref>', 'Git revision to review against'],
interactive: ['--interactive', 'Inspect and record outcomes in a terminal'],
json: ['--json', 'Print JSON instead of text'],
Expand Down Expand Up @@ -121,8 +123,16 @@ export const maintainerActions: Record<string, MaintainerAction> = {
'description',
'source',
'requires',
'task',
].map((key) => optionHelp[key]!),
},
remove: {
usage: 'maintainer remove <name>',
summary: 'Retire a registered skill without deleting its guidance.',
writes:
'The entry status in skill_tree.yaml and a note in skill_spec.md. Delete the SKILL.md yourself once its guidance is no longer needed.',
options: ['artifacts'].map((key) => optionHelp[key]!),
},
status: {
usage: 'maintainer status [--json] [--base <ref>]',
summary: 'Report authoring gaps, files to sync, and pending reviews.',
Expand Down Expand Up @@ -197,6 +207,7 @@ export interface MaintainerCommandOptions extends DistributionOptions {
description?: string
source?: string | Array<string>
requires?: string | Array<string>
task?: string | Array<string>
base?: string
json?: boolean
record?: string
Expand Down Expand Up @@ -236,17 +247,19 @@ export async function runMaintainerCommand(
'description',
'source',
'requires',
'task',
],
remove: ['artifacts'],
status: ['artifacts', 'base', 'json'],
sync: ['artifacts'],
review: ['base', 'json', 'record', 'interactive'],
check: ['artifacts', 'base'],
}
if (!allowed[action])
fail(
`Unknown maintainer action: ${action}. Expected setup, adopt, add, status, sync, review, or check.`,
`Unknown maintainer action: ${action}. Expected setup, adopt, add, remove, status, sync, review, or check.`,
)
if (name !== undefined && action !== 'add')
if (name !== undefined && action !== 'add' && action !== 'remove')
fail(`maintainer ${action} does not take a skill name.`)
for (const key of Object.keys(options)) {
if (key !== '--' && !allowed[action].includes(key))
Expand Down Expand Up @@ -336,7 +349,7 @@ export async function runMaintainerCommand(
})
return
}
if (['setup', 'add', 'sync'].includes(action)) {
if (['setup', 'add', 'remove', 'sync'].includes(action)) {
await withMaintainerLock(project.root, () => {
if (action === 'setup') {
const created = setupRecords(project)
Expand Down Expand Up @@ -373,6 +386,15 @@ export async function runMaintainerCommand(
console.log(
`Next: author the guidance with intent meta generate-skill, record its developer tasks in ${project.artifacts}/domain_map.yaml, then run intent maintainer sync, intent maintainer review, and intent maintainer check.`,
)
} else if (action === 'remove') {
const retired = retireSkill(project, name)
console.log(`Retired ${name}.`)
console.log(`Updated: ${retired.files.join(', ')}`)
console.log(
retired.exists
? `Delete ${retired.path} when its guidance is no longer needed, then run intent maintainer sync and intent maintainer review.`
: 'Run intent maintainer sync and intent maintainer review.',
)
} else {
const plan = planMaintainerSync(project)
writeChanges(project.root, plan.changes)
Expand Down
Loading
Loading