@erclx/canon 5.30.0 → 5.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/references/adopted.md +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +4 -16
- package/claude/skills/auto-ship/SKILL.md +13 -58
- package/claude/skills/auto-ship/references/draft-mark.md +2 -2
- package/claude/skills/auto-ship/references/failure-recovery.md +1 -5
- package/claude/skills/auto-ship/references/tier-failures.md +1 -1
- package/claude/skills/auto-ship/references/ui-checklist.md +2 -2
- package/claude/skills/auto-ship/references/verb-absent.md +3 -27
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- package/claude/skills/build-in-slices/REQUIREMENT.md +1 -1
- package/claude/skills/build-in-slices/references/adopted.md +1 -1
- package/claude/skills/canon-cli/REQUIREMENT.md +4 -6
- package/claude/skills/canon-cli/SKILL.md +2 -2
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +10 -13
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
- package/claude/skills/canon-feedback-triage/SKILL.md +50 -29
- package/claude/skills/canvas/REQUIREMENT.md +3 -4
- package/claude/skills/canvas/SKILL.md +5 -5
- package/{standards → claude/skills/canvas/references}/canvas.md +12 -1
- package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
- package/claude/skills/context-fold/REQUIREMENT.md +8 -20
- package/claude/skills/context-fold/SKILL.md +21 -45
- package/claude/skills/context-fold/references/classify.md +7 -8
- package/claude/skills/context-fold/references/completion-report.md +2 -6
- package/claude/skills/context-fold/references/context-refresh.md +5 -15
- package/claude/skills/context-fold/references/promoted-pages.md +3 -3
- package/claude/skills/context-fold/references/receipt-sweep.md +6 -17
- package/claude/skills/context-fold/references/unusable-baseline.md +4 -6
- package/claude/skills/create-rule/REQUIREMENT.md +0 -1
- package/claude/skills/create-skill/REQUIREMENT.md +6 -4
- package/claude/skills/create-skill/SKILL.md +9 -8
- package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
- package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
- package/claude/skills/decision-escalate/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +0 -2
- package/claude/skills/design-taste/SKILL.md +2 -4
- package/claude/skills/design-taste/references/vocabulary.md +1 -1
- package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
- package/claude/skills/docs-sync/SKILL.md +1 -1
- package/claude/skills/document-health/REQUIREMENT.md +2 -2
- package/claude/skills/document-health/SKILL.md +4 -5
- package/claude/skills/document-health/references/axes.md +4 -6
- package/claude/skills/draft-and-pick/REQUIREMENT.md +16 -14
- package/claude/skills/draft-and-pick/SKILL.md +40 -32
- package/claude/skills/draft-and-pick/references/live-arms.md +9 -9
- package/claude/skills/draft-doc/REQUIREMENT.md +5 -5
- package/claude/skills/draft-doc/SKILL.md +10 -11
- package/claude/skills/draft-doc/references/requirements.md +61 -0
- package/claude/skills/draft-doc/references/wiki-kind.md +53 -0
- package/claude/skills/draft-doc/references/wiki.md +50 -30
- package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
- package/claude/skills/draft-figure/SKILL.md +5 -5
- package/claude/skills/draft-figure/references/architecture-set.md +4 -4
- package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +55 -0
- package/claude/skills/draft-figure/references/mermaid.md +43 -34
- package/claude/skills/draft-identity/REQUIREMENT.md +1 -1
- package/claude/skills/draft-identity/SKILL.md +9 -7
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +6 -6
- package/claude/skills/draft-ready/references/assembly.md +1 -1
- package/{standards → claude/skills/draft-ready/references}/ready.md +13 -5
- package/claude/skills/git-issue/SKILL.md +1 -1
- package/claude/skills/git-issue/references/issue.md +3 -3
- package/claude/skills/git-pr/SKILL.md +1 -1
- package/claude/skills/git-ship/REQUIREMENT.md +0 -2
- package/claude/skills/git-ship/SKILL.md +16 -24
- package/claude/skills/git-stage/SKILL.md +1 -1
- package/claude/skills/markdown-craft/REQUIREMENT.md +23 -0
- package/claude/skills/markdown-craft/SKILL.md +17 -0
- package/{standards → claude/skills/markdown-craft/references}/markdown.md +14 -7
- package/claude/skills/plan-feature/REQUIREMENT.md +2 -1
- package/claude/skills/plan-feature/SKILL.md +14 -8
- package/{standards → claude/skills/plan-feature/references}/plan.md +33 -21
- package/claude/skills/plan-groundwork/SKILL.md +5 -6
- package/{standards → claude/skills/plan-groundwork/references}/groundwork.md +17 -4
- package/claude/skills/plan-intake/SKILL.md +2 -3
- package/{standards → claude/skills/plan-intake/references}/intake.md +17 -2
- package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
- package/claude/skills/plan-intake-answer/SKILL.md +1 -1
- package/claude/skills/review-address/SKILL.md +2 -2
- package/claude/skills/review-address/references/post-review-findings.md +1 -1
- package/claude/skills/review-craft/REQUIREMENT.md +3 -3
- package/claude/skills/review-craft/SKILL.md +2 -2
- package/claude/skills/review-craft/references/ui.md +4 -4
- package/claude/skills/review-pr/REQUIREMENT.md +1 -1
- package/claude/skills/review-pr/SKILL.md +5 -7
- package/claude/skills/review-pr/references/post.md +2 -2
- package/claude/skills/review-ui/REQUIREMENT.md +0 -1
- package/claude/skills/review-ui/SKILL.md +1 -1
- package/claude/skills/review-ui/references/post.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +0 -3
- package/claude/skills/role-orchestrator/SKILL.md +8 -8
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +4 -3
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +7 -10
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +0 -3
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +1 -1
- package/claude/skills/role-planner/SKILL.md +2 -2
- package/claude/skills/role-reviewer/SKILL.md +1 -1
- package/claude/skills/role-worker/SKILL.md +2 -2
- package/claude/skills/session-compact/REQUIREMENT.md +1 -4
- package/claude/skills/session-compact/SKILL.md +3 -13
- package/claude/skills/session-compact/references/handoff-note.md +2 -2
- package/claude/skills/session-map/REQUIREMENT.md +1 -9
- package/claude/skills/session-map/SKILL.md +8 -16
- package/{standards → claude/skills/session-map/references}/session.md +17 -11
- package/claude/skills/session-relay/REQUIREMENT.md +1 -1
- package/claude/skills/session-resume/REQUIREMENT.md +4 -6
- package/claude/skills/session-resume/SKILL.md +3 -9
- package/claude/skills/session-worktree/SKILL.md +1 -1
- package/claude/skills/session-worktree/references/main-root-writes.md +2 -2
- package/claude/skills/systematic-debugging/REQUIREMENT.md +0 -1
- package/claude/skills/target-check/REQUIREMENT.md +3 -3
- package/claude/skills/target-check/SKILL.md +1 -1
- package/claude/skills/target-check/references/domains.md +1 -1
- package/claude/skills/target-setup/REQUIREMENT.md +4 -4
- package/claude/skills/target-setup/SKILL.md +3 -3
- package/claude/skills/target-setup/references/detect.md +1 -1
- package/claude/skills/target-setup/references/indexes.md +1 -1
- package/claude/skills/task-board/SKILL.md +4 -4
- package/claude/skills/task-board/references/archive.md +1 -1
- package/{standards → claude/skills/task-board/references}/board.md +9 -0
- package/claude/skills/task-board/references/decline.md +1 -1
- package/{standards → claude/skills/task-board/references}/tasks.md +13 -8
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/revisit.md +1 -1
- package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
- package/claude/skills/test-first/REQUIREMENT.md +1 -1
- package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
- package/claude/skills/ux-audit/SKILL.md +2 -3
- package/claude/skills/write-human/REQUIREMENT.md +0 -1
- package/claude/skills/write-human/SKILL.md +2 -2
- package/docs/agents/audits.md +1 -1
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +6 -10
- package/docs/agents/context-audit-checks.md +10 -24
- package/docs/agents/context-audit.md +7 -15
- package/docs/agents/context-classify.md +5 -5
- package/docs/agents/counts.md +5 -5
- package/docs/agents/drift-surfaces.md +2 -2
- package/docs/agents/index.md +3 -4
- package/docs/agents/install-and-sync.md +6 -6
- package/docs/agents/intake.md +1 -1
- package/docs/agents/markdown-audit.md +1 -1
- package/docs/agents/records-stale.md +5 -5
- package/docs/agents/records-validate.md +1 -1
- package/docs/agents/records.md +2 -2
- package/docs/agents/restated.md +1 -1
- package/docs/agents/routing.md +1 -1
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/skills-audit.md +2 -2
- package/docs/agents/state-scoped-risk.md +2 -2
- package/docs/agents/superseded.md +2 -2
- package/docs/agents/tasks-plans.md +2 -2
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +1 -1
- package/docs/agents/teach.md +1 -1
- package/docs/agents/test-order.md +1 -1
- package/docs/agents/upstream.md +94 -0
- package/docs/target/migrations.md +24 -0
- package/docs/target/projects.md +5 -7
- package/docs/target/scenarios.md +2 -2
- package/docs/target/sync.md +3 -3
- package/docs/workflow/ai-workflow.md +7 -38
- package/docs/workflow/index.md +1 -1
- package/docs/workflow/operating-model.md +8 -8
- package/docs/workflow/parallel-features.md +7 -7
- package/docs/workflow/skill-map-on-request.md +13 -16
- package/docs/workflow/skill-map.md +20 -32
- package/docs/workflow/visual-design-workflow.md +11 -14
- package/governance/renames.toml +0 -23
- package/governance/rules/canon/605-worktrees.md +1 -1
- package/governance/rules/{standards → canon}/plan.md +5 -5
- package/governance/rules/canon/tasks.md +18 -0
- package/governance/rules/writing/markdown.md +23 -0
- package/governance/stacks/base.toml +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +2 -2
- package/scripts/core/regen-web-previews.ts +1 -1
- package/src/audits/catalog.ts +5 -97
- package/src/claude/cases/authoring.ts +5 -18
- package/src/claude/cases/misc.ts +0 -17
- package/src/claude/cases/setup.ts +3 -20
- package/src/claude/cases/workflow.ts +0 -33
- package/src/claude/seeds.ts +1 -7
- package/src/claude/skills-audit.ts +1 -1
- package/src/claude/skills-reach.ts +1 -1
- package/src/cli/help.ts +3 -4
- package/src/cli.ts +2 -4
- package/src/commands/claude.ts +9 -15
- package/src/commands/context.ts +18 -280
- package/src/commands/feedback.ts +22 -91
- package/src/commands/gov.ts +2 -2
- package/src/commands/records.ts +24 -6
- package/src/commands/sync.ts +5 -3
- package/src/commands/upstream.ts +430 -0
- package/src/context/audit.ts +6 -15
- package/src/context/classify/extract.ts +6 -13
- package/src/context/classify/patterns.ts +2 -41
- package/src/context/classify/prompts.ts +2 -6
- package/src/context/classify/run.ts +3 -3
- package/src/context/folders.ts +7 -12
- package/src/context/gate.ts +11 -91
- package/src/context/requirements.ts +13 -4
- package/src/counts/scan.ts +3 -3
- package/src/design/components.ts +2 -2
- package/src/feedback/body.ts +0 -12
- package/src/gate/measures.ts +6 -57
- package/src/gate/stages.ts +0 -1
- package/src/gov/citations.ts +9 -3
- package/src/gov/restated.ts +3 -3
- package/src/markdown/structure.ts +3 -3
- package/src/migrate/record-layout.ts +3 -3
- package/src/records/backup.ts +1 -0
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +2 -1
- package/src/records/stale-canonical.ts +2 -2
- package/src/records/validate.ts +55 -0
- package/src/roots/record.ts +2 -1
- package/src/sandbox/scenario.ts +115 -11
- package/src/shipped/references.ts +3 -3
- package/src/standards/read.ts +52 -1
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/answers.ts +14 -1
- package/src/tasks/reach.ts +2 -2
- package/src/tasks/stack.ts +176 -0
- package/src/tasks/validate.ts +1 -1
- package/src/teach/workspace.ts +1 -1
- package/src/upstream/catalog.ts +112 -0
- package/src/upstream/cursor.ts +120 -0
- package/src/upstream/digest.ts +75 -0
- package/src/upstream/due.ts +128 -0
- package/src/upstream/releases.ts +170 -0
- package/standards/context.md +3 -5
- package/standards/design.md +2 -3
- package/standards/index.md +0 -23
- package/standards/publish.md +1 -1
- package/standards/rule.md +1 -1
- package/standards/skill.md +1 -1
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/reference.md +6 -20
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +1 -1
- package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
- package/tooling/claude/seeds/CLAUDE.md +1 -3
- package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -9
- package/tooling/web/configs/e2e/screenshot.ts +0 -1
- package/claude/skills/auto-ship/references/review-findings.md +0 -20
- package/claude/skills/bash-cli-script/REQUIREMENT.md +0 -43
- package/claude/skills/bash-cli-script/SKILL.md +0 -53
- package/claude/skills/bash-cli-script/references/template.md +0 -19
- package/claude/skills/canon-operator/REQUIREMENT.md +0 -62
- package/claude/skills/canon-operator/SKILL.md +0 -113
- package/claude/skills/canon-rollout/REQUIREMENT.md +0 -59
- package/claude/skills/canon-rollout/SKILL.md +0 -140
- package/claude/skills/canon-rollout/references/failure-recovery.md +0 -20
- package/claude/skills/context-fold/references/anchor-sweep.md +0 -58
- package/claude/skills/context-fold/references/wireframe-sweep.md +0 -45
- package/claude/skills/create-standard/REQUIREMENT.md +0 -33
- package/claude/skills/create-standard/SKILL.md +0 -31
- package/claude/skills/design-extract/REQUIREMENT.md +0 -42
- package/claude/skills/design-extract/SKILL.md +0 -108
- package/claude/skills/draft-and-pick/references/frame.html +0 -118
- package/claude/skills/draft-doc/references/wireframes.md +0 -48
- package/claude/skills/index-lookup/REQUIREMENT.md +0 -35
- package/claude/skills/index-lookup/SKILL.md +0 -44
- package/claude/skills/markdown-propose/REQUIREMENT.md +0 -47
- package/claude/skills/markdown-propose/SKILL.md +0 -122
- package/claude/skills/markdown-propose/references/format.md +0 -118
- package/claude/skills/memory-capture/REQUIREMENT.md +0 -50
- package/claude/skills/memory-capture/SKILL.md +0 -117
- package/claude/skills/memory-review/REQUIREMENT.md +0 -52
- package/claude/skills/memory-review/SKILL.md +0 -129
- package/claude/skills/memory-review/references/later-phases.md +0 -118
- package/claude/skills/memory-review/references/receipt-format.md +0 -64
- package/claude/skills/project-commands/REQUIREMENT.md +0 -42
- package/claude/skills/project-commands/SKILL.md +0 -87
- package/claude/skills/repo-metadata/REQUIREMENT.md +0 -37
- package/claude/skills/repo-metadata/SKILL.md +0 -54
- package/claude/skills/restate-plainly/REQUIREMENT.md +0 -41
- package/claude/skills/restate-plainly/SKILL.md +0 -41
- package/claude/skills/review-branch/REQUIREMENT.md +0 -40
- package/claude/skills/review-branch/SKILL.md +0 -137
- package/claude/skills/seed-sync/REQUIREMENT.md +0 -45
- package/claude/skills/seed-sync/SKILL.md +0 -158
- package/claude/skills/sketch-design/REQUIREMENT.md +0 -38
- package/claude/skills/sketch-design/SKILL.md +0 -94
- package/claude/skills/ux-walkthrough/REQUIREMENT.md +0 -56
- package/claude/skills/ux-walkthrough/SKILL.md +0 -53
- package/claude/skills/ux-walkthrough/references/builds.md +0 -15
- package/claude/skills/ux-walkthrough/references/candidate-pages.md +0 -11
- package/claude/skills/ux-walkthrough/references/measuring.md +0 -22
- package/claude/skills/ux-walkthrough/references/record.md +0 -36
- package/claude/skills/ux-walkthrough/references/relay.md +0 -25
- package/docs/agents/architecture-record.md +0 -36
- package/docs/agents/review-classification.md +0 -76
- package/governance/rules/canon/601-indexes.md +0 -9
- package/governance/rules/canon/603-memory.md +0 -11
- package/governance/rules/claude/565-behavior.md +0 -27
- package/governance/rules/claude/567-planning.md +0 -16
- package/governance/rules/claude/575-hooks.md +0 -16
- package/governance/rules/claude/576-settings.md +0 -13
- package/governance/rules/claude/592-claude-md.md +0 -19
- package/governance/rules/lang/120-bash.md +0 -18
- package/governance/rules/standards/architecture.md +0 -13
- package/governance/rules/standards/groundwork.md +0 -13
- package/governance/rules/standards/intake.md +0 -12
- package/governance/rules/standards/markdown.md +0 -16
- package/governance/rules/standards/memory.md +0 -12
- package/governance/rules/standards/ready.md +0 -13
- package/governance/rules/standards/requirements.md +0 -12
- package/governance/rules/standards/session.md +0 -13
- package/governance/rules/standards/tasks.md +0 -15
- package/governance/rules/standards/teach.md +0 -14
- package/governance/rules/standards/wiki.md +0 -13
- package/governance/rules/standards/wireframes.md +0 -13
- package/governance/rules/writing/800-prose.md +0 -15
- package/src/autoship/classify.ts +0 -75
- package/src/autoship/paths.ts +0 -50
- package/src/commands/autoship.ts +0 -129
- package/src/commands/repo.ts +0 -393
- package/src/context/architecture.ts +0 -654
- package/src/context/wireframe-states.ts +0 -238
- package/src/repo/metadata.ts +0 -206
- package/standards/architecture.md +0 -127
- package/standards/memory.md +0 -158
- package/standards/mermaid.md +0 -64
- package/standards/requirements.md +0 -79
- package/standards/wiki.md +0 -77
- package/standards/wireframes.md +0 -143
- package/tooling/claude/seeds/.claude/memory/index.md +0 -8
- package/tooling/claude/seeds/canon/ARCHITECTURE.md +0 -23
- package/tooling/claude/seeds/canon/wireframes/index.md +0 -8
- /package/{standards → claude/skills/create-skill/references}/skill-practice.md +0 -0
- /package/{standards → claude/skills/create-skill/references}/skill-requirement.md +0 -0
- /package/{standards → claude/skills/plan-feature/references}/plan-lifecycle.md +0 -0
- /package/{standards → claude/skills/plan-groundwork/references}/groundwork-spikes.md +0 -0
- /package/{standards → claude/skills/task-board/references}/tasks-decline.md +0 -0
- /package/{standards → claude/skills/teach-workspace/references}/glossary.md +0 -0
package/src/gov/citations.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { isMarked } from '@/sweep/exempt-marker'
|
|
|
6
6
|
import { gitEnv } from '@/git/env'
|
|
7
7
|
import { listRuleFiles } from '@/gov/payload'
|
|
8
8
|
import { parseFrontmatter } from '@/indexes/frontmatter'
|
|
9
|
+
import { movedStandardPath } from '@/standards/read'
|
|
9
10
|
import { findBacktickSpans } from '@/markdown/backticks'
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -177,8 +178,9 @@ export function classifySpan(span: string): CitationForm | undefined {
|
|
|
177
178
|
*
|
|
178
179
|
* A standard name takes the authoring root alone, matching `standardRoots` in
|
|
179
180
|
* `@/standards/read`, which reads `standards/` at the working root and then the
|
|
180
|
-
* package corpus
|
|
181
|
-
* only where those two roots
|
|
181
|
+
* package corpus, plus the skill reference a retired name moved to. This verb
|
|
182
|
+
* refuses a tree holding no rule corpus, so it runs only where those two roots
|
|
183
|
+
* are one directory. `internal/standards/` is
|
|
182
184
|
* deliberately absent: `canon standards <name>` never reaches it, so admitting it
|
|
183
185
|
* here would pass a citation that refuses for the session opening it, which is a
|
|
184
186
|
* gate failing open.
|
|
@@ -191,7 +193,11 @@ function candidatesFor(
|
|
|
191
193
|
cited: string,
|
|
192
194
|
ruleFile: string,
|
|
193
195
|
): string[] {
|
|
194
|
-
if (form === 'standard')
|
|
196
|
+
if (form === 'standard') {
|
|
197
|
+
const moved = movedStandardPath(cited)
|
|
198
|
+
|
|
199
|
+
return [join('standards', `${cited}.md`), ...(moved ? [moved] : [])]
|
|
200
|
+
}
|
|
195
201
|
if (form === 'sibling') return [join(dirname(ruleFile), cited)]
|
|
196
202
|
return [cited]
|
|
197
203
|
}
|
package/src/gov/restated.ts
CHANGED
|
@@ -36,8 +36,8 @@ const SKILL_ROOTS: readonly string[] = [SHIPPED_SKILLS_REL, PROJECT_SKILLS_REL]
|
|
|
36
36
|
/**
|
|
37
37
|
* Path pairs whose duplication is deliberate and already recorded.
|
|
38
38
|
*
|
|
39
|
-
* The seed is authored from the always-loaded file and
|
|
40
|
-
*
|
|
39
|
+
* The seed is authored from the always-loaded file and a hand merge
|
|
40
|
+
* reconciles the two, so a bullet appearing in both is the design
|
|
41
41
|
* rather than a defect. Excluding by pair rather than by content is what the
|
|
42
42
|
* plan settled on: the duplication is a location fact this repository already
|
|
43
43
|
* records, and a content test would have to rediscover it on every run.
|
|
@@ -611,7 +611,7 @@ function authorityFor(
|
|
|
611
611
|
if (candidate.kind === 'seed') {
|
|
612
612
|
return {
|
|
613
613
|
authority: 'claude-md',
|
|
614
|
-
reason: `${INSTRUCTIONS_REL} is authored first and the seed carries it to a target, so an edit starts there and reaches the seed through
|
|
614
|
+
reason: `${INSTRUCTIONS_REL} is authored first and the seed carries it to a target, so an edit starts there and reaches the seed through a hand merge`,
|
|
615
615
|
}
|
|
616
616
|
}
|
|
617
617
|
|
|
@@ -6,7 +6,7 @@ const HEADING = /^#{1,6}\s/
|
|
|
6
6
|
* A section marker holding its line alone, which is the signpost a document
|
|
7
7
|
* uses where its own template asks for bold rather than a heading.
|
|
8
8
|
*
|
|
9
|
-
* `
|
|
9
|
+
* `claude/skills/plan-feature/references/plan.md` gives `## Summary` a heading and marks the four sections
|
|
10
10
|
* below it this way, so a conforming plan read as one run from its first line
|
|
11
11
|
* to its last and all seven live plans reported past the depth checkpoint, at
|
|
12
12
|
* 106 to 166 rendered lines. A measure firing on a whole corpus says nothing
|
|
@@ -92,7 +92,7 @@ const SENTENCE_END = /[.!?]["'’”)\]]*(?=\s+(?:["'“(\[]*[A-Z]|`)|\s*$)/g
|
|
|
92
92
|
* Checkpoints the structural measures run against.
|
|
93
93
|
*
|
|
94
94
|
* These are the definition rather than a fallback. Reading each number out of
|
|
95
|
-
* the sentence `
|
|
95
|
+
* the sentence `claude/skills/markdown-craft/references/markdown.md` states it in was the alternative, and it
|
|
96
96
|
* put a parser contract on a document authored for people, where a rewording
|
|
97
97
|
* degraded a number and the report had to carry a legend saying which one. The
|
|
98
98
|
* standard still states every number for a reader, and moving one is an edit to
|
|
@@ -659,7 +659,7 @@ function measureParagraph(block: readonly BodyLine[]): CadenceFinding {
|
|
|
659
659
|
*
|
|
660
660
|
* `compromise` reported 2 percent. It tags a fronted past participle as a
|
|
661
661
|
* finite past-tense verb, so all twelve of those sentences in
|
|
662
|
-
*
|
|
662
|
+
* the repository's architecture record at that commit read as carrying one, eleven opening `Measured at`
|
|
663
663
|
* and one `Overturned by`, which is the exact shape the measure exists to
|
|
664
664
|
* catch. It is not even consistent with itself there: `Measured at` tags a verb
|
|
665
665
|
* and `Measured against` tags an adjective.
|
|
@@ -346,9 +346,9 @@ function entriesOf(dir: string, isDirectory: boolean): string[] {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
/**
|
|
349
|
-
* What `draft-and-pick` Step 6
|
|
350
|
-
*
|
|
351
|
-
*
|
|
349
|
+
* What `draft-and-pick` Step 6 writes into the folder it archives a choice to:
|
|
350
|
+
* a capture per canvas frame, named `arm-<id>`. A design handoff marks a pick
|
|
351
|
+
* folder an earlier skill wrote. A folder carrying neither is evidence.
|
|
352
352
|
*/
|
|
353
353
|
const PICK_FILE = /^(?:arm-[^.]+\..+|design-handoff\.md)$/
|
|
354
354
|
|
package/src/records/backup.ts
CHANGED
package/src/records/ordinal.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { recordDir } from '@/roots/record'
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The two record folders that share one ordinal sequence, per
|
|
7
|
-
* `
|
|
7
|
+
* `claude/skills/plan-intake/references/intake.md` and `claude/skills/plan-groundwork/references/groundwork.md`. A folder opened as
|
|
8
8
|
* either kind blocks the same number for the other, so the two read together
|
|
9
9
|
* rather than each keeping a count of its own.
|
|
10
10
|
*/
|
package/src/records/prune.ts
CHANGED
|
@@ -35,7 +35,8 @@ const LEGACY_MOVES: ReadonlyMap<string, string> = new Map([
|
|
|
35
35
|
* Scratch-root names a different migration moves out of scratch for good,
|
|
36
36
|
* derived from that migration's own table rather than duplicated in a second
|
|
37
37
|
* hand-kept list. `RECORD_LAYOUT_MOVES` names `memory-archive`, the
|
|
38
|
-
* retired-entry archive
|
|
38
|
+
* retired-entry archive of the memory kind, which is never offered for deletion,
|
|
39
|
+
* and `PROMOTED_FOLDERS`
|
|
39
40
|
* names an evidence folder a durable record cites by name. A project that has
|
|
40
41
|
* not run either migration still carries these at the scratch root, where an
|
|
41
42
|
* ordinary slug's own age test would eventually offer them.
|
|
@@ -6,8 +6,8 @@ import { gitEnv } from '@/git/env'
|
|
|
6
6
|
import { readReviewed } from '@/records/stale'
|
|
7
7
|
import { surfaceDir } from '@/roots/surface'
|
|
8
8
|
|
|
9
|
-
/** The
|
|
10
|
-
const CANONICAL_DOCS = ['REQUIREMENTS.md'
|
|
9
|
+
/** The record whose drift is identity rather than a stale path. */
|
|
10
|
+
const CANONICAL_DOCS = ['REQUIREMENTS.md'] as const
|
|
11
11
|
|
|
12
12
|
export interface CanonicalDoc {
|
|
13
13
|
/** Relative to the root, at whichever surface root carries the doc. */
|
package/src/records/validate.ts
CHANGED
|
@@ -75,6 +75,8 @@ export const FINDING_KINDS = [
|
|
|
75
75
|
'category-mismatch',
|
|
76
76
|
'operator-call-phrasing',
|
|
77
77
|
'batch-unsplit',
|
|
78
|
+
'source-unquoted',
|
|
79
|
+
'stack-foldable',
|
|
78
80
|
] as const
|
|
79
81
|
|
|
80
82
|
export type FindingKind = (typeof FINDING_KINDS)[number]
|
|
@@ -276,6 +278,7 @@ const PLAN_SECTIONS = [
|
|
|
276
278
|
'Risks',
|
|
277
279
|
'Review focus',
|
|
278
280
|
'Questions',
|
|
281
|
+
'Sources',
|
|
279
282
|
] as const
|
|
280
283
|
|
|
281
284
|
type PlanSection = (typeof PLAN_SECTIONS)[number]
|
|
@@ -425,6 +428,57 @@ function checkQuestionContract(name: string, lines: string[]): Finding[] {
|
|
|
425
428
|
return findings
|
|
426
429
|
}
|
|
427
430
|
|
|
431
|
+
const SOURCE_LINK = /\[[^\]]+\]\(https?:\/\/[^)\s]+\)|https?:\/\/\S+/
|
|
432
|
+
const SOURCE_QUOTE = /["“][^"“”]+["”]/
|
|
433
|
+
const SOURCE_UNVERIFIED = /\bunverified\b/i
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* A bullet opens an entry only at the section's shallowest indent, so a nested
|
|
437
|
+
* bullet and a wrapped line both belong to the entry above. Joining them first
|
|
438
|
+
* lets a passage wrap across lines and still count as quoted.
|
|
439
|
+
*/
|
|
440
|
+
function readSourceEntries(lines: readonly string[]): string[] {
|
|
441
|
+
const entries: string[] = []
|
|
442
|
+
let depth: number | undefined
|
|
443
|
+
|
|
444
|
+
for (const line of lines) {
|
|
445
|
+
const trimmed = line.trim()
|
|
446
|
+
if (trimmed === '') continue
|
|
447
|
+
|
|
448
|
+
const indent = line.length - line.trimStart().length
|
|
449
|
+
const opensEntry =
|
|
450
|
+
trimmed.startsWith('- ') && (depth === undefined || indent <= depth)
|
|
451
|
+
|
|
452
|
+
if (opensEntry) {
|
|
453
|
+
depth = indent
|
|
454
|
+
entries.push(trimmed)
|
|
455
|
+
continue
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (entries.length > 0) entries[entries.length - 1] += ` ${trimmed}`
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return entries
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function checkSources(name: string, lines: readonly string[]): Finding[] {
|
|
465
|
+
return readSourceEntries(lines)
|
|
466
|
+
.filter((entry) => entry !== `- ${NONE_IDENTIFIED}`)
|
|
467
|
+
.filter(
|
|
468
|
+
(entry) =>
|
|
469
|
+
!SOURCE_LINK.test(entry) ||
|
|
470
|
+
!(SOURCE_QUOTE.test(entry) || SOURCE_UNVERIFIED.test(entry)),
|
|
471
|
+
)
|
|
472
|
+
.map((entry) =>
|
|
473
|
+
finding(
|
|
474
|
+
'source-unquoted',
|
|
475
|
+
name,
|
|
476
|
+
shorten(entry),
|
|
477
|
+
'carries no link, or a link with neither a double-quoted passage nor the word unverified.',
|
|
478
|
+
),
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
428
482
|
export function checkPlan(name: string, text: string): Finding[] {
|
|
429
483
|
const findings: Finding[] = []
|
|
430
484
|
|
|
@@ -480,6 +534,7 @@ export function checkPlan(name: string, text: string): Finding[] {
|
|
|
480
534
|
}
|
|
481
535
|
|
|
482
536
|
findings.push(...checkQuestionContract(name, sections.get('Questions') ?? []))
|
|
537
|
+
findings.push(...checkSources(name, sections.get('Sources') ?? []))
|
|
483
538
|
|
|
484
539
|
if (hasStagedBatches(text)) {
|
|
485
540
|
const staged = linesOutsideFences(text).find((line) =>
|
package/src/roots/record.ts
CHANGED
|
@@ -56,7 +56,7 @@ const CANON_SCRATCH = 'tmp'
|
|
|
56
56
|
* Everything absent from this list is committed and never lands under the
|
|
57
57
|
* record root, which is the rule the move ran on. `rules`, `skills`, and
|
|
58
58
|
* `hooks` stay under `.claude/` because the vendor reads them there, while
|
|
59
|
-
* `context
|
|
59
|
+
* `context` and the loose documents are tracked surfaces that
|
|
60
60
|
* `surface.ts` resolves under `canon/` instead. A seed and a
|
|
61
61
|
* superseded-layout report each ask this rather than assuming a root.
|
|
62
62
|
*/
|
|
@@ -80,6 +80,7 @@ export const RECORD_ENTRIES: readonly string[] = [
|
|
|
80
80
|
'tasks',
|
|
81
81
|
'teach',
|
|
82
82
|
'transcripts',
|
|
83
|
+
'upstream',
|
|
83
84
|
'walkthroughs',
|
|
84
85
|
]
|
|
85
86
|
|
package/src/sandbox/scenario.ts
CHANGED
|
@@ -8,10 +8,19 @@ import {
|
|
|
8
8
|
readFileSync,
|
|
9
9
|
statSync,
|
|
10
10
|
writeFileSync,
|
|
11
|
+
writeSync,
|
|
11
12
|
} from 'node:fs'
|
|
12
13
|
import { createRequire } from 'node:module'
|
|
13
14
|
import { dirname, join, relative } from 'node:path'
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
logError,
|
|
17
|
+
logInfo,
|
|
18
|
+
logStep,
|
|
19
|
+
logWarn,
|
|
20
|
+
palette,
|
|
21
|
+
pipeOutput,
|
|
22
|
+
select,
|
|
23
|
+
} from '@/cli/ui'
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
26
|
* The TypeScript form of a sandbox scenario: `sandbox/<category>/<command>.ts`,
|
|
@@ -33,7 +42,7 @@ export const FIXTURE_SUFFIX = '.fixture'
|
|
|
33
42
|
/** A scenario whose only arm carries this name routes none, as a bash file with no picker did. */
|
|
34
43
|
export const DEFAULT_ARM_NAME = 'default'
|
|
35
44
|
|
|
36
|
-
/**
|
|
45
|
+
/** What a bash scenario passed to `select_or_route_scenario` unless it named another. */
|
|
37
46
|
const ROUTE_PROMPT = 'Which scenario?'
|
|
38
47
|
|
|
39
48
|
/** Every scenario that sets the anchor pushes to this one throwaway repository. */
|
|
@@ -50,6 +59,14 @@ export interface ScenarioDefinition {
|
|
|
50
59
|
readonly config?: Readonly<Record<string, string>>
|
|
51
60
|
/** Stages the tree from the anchor fixture and names the throwaway remote. */
|
|
52
61
|
readonly anchor?: boolean
|
|
62
|
+
/**
|
|
63
|
+
* Stages and narrates what the bash form did ahead of `select_or_route_scenario`,
|
|
64
|
+
* so the picker's echo lands after it on the frame. Runs before the arm is
|
|
65
|
+
* routed, which leaves `ctx.arm` undefined.
|
|
66
|
+
*/
|
|
67
|
+
readonly prepare?: ArmSetup
|
|
68
|
+
/** The picker's message, for a bash scenario that passed something other than the default. */
|
|
69
|
+
readonly prompt?: string
|
|
53
70
|
/** In routing order, so the first arm is the one a headless caller gets. */
|
|
54
71
|
readonly arms: Readonly<Record<string, ArmSetup>>
|
|
55
72
|
}
|
|
@@ -144,7 +161,7 @@ export function loadScenario(file: string): ScenarioDefinition {
|
|
|
144
161
|
if (!isRecord(definition))
|
|
145
162
|
throw new Error(`${file} has no default export from scenario()`)
|
|
146
163
|
|
|
147
|
-
const { arms, config, anchor } = definition
|
|
164
|
+
const { arms, config, anchor, prepare, prompt } = definition
|
|
148
165
|
if (!isRecord(arms) || Object.keys(arms).length === 0)
|
|
149
166
|
throw new Error(`${file} declares no arms`)
|
|
150
167
|
for (const [name, setup] of Object.entries(arms))
|
|
@@ -158,6 +175,10 @@ export function loadScenario(file: string): ScenarioDefinition {
|
|
|
158
175
|
throw new Error(`${file} config holds a value that is not a string`)
|
|
159
176
|
if (anchor !== undefined && typeof anchor !== 'boolean')
|
|
160
177
|
throw new Error(`${file} anchor is not a boolean`)
|
|
178
|
+
if (prepare !== undefined && typeof prepare !== 'function')
|
|
179
|
+
throw new Error(`${file} prepare is not a function`)
|
|
180
|
+
if (prompt !== undefined && typeof prompt !== 'string')
|
|
181
|
+
throw new Error(`${file} prompt is not a string`)
|
|
161
182
|
|
|
162
183
|
return definition as unknown as ScenarioDefinition
|
|
163
184
|
}
|
|
@@ -225,6 +246,24 @@ export interface RunOptions {
|
|
|
225
246
|
readonly stderr?: 'inherit' | 'ignore'
|
|
226
247
|
/** Returns a failing status rather than stopping the arm, the `|| true` of the bash form. */
|
|
227
248
|
readonly allowFailure?: boolean
|
|
249
|
+
/** Set for this command alone, the `NAME=value command` prefix of the bash form. */
|
|
250
|
+
readonly env?: Readonly<Record<string, string>>
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface CaptureOptions {
|
|
254
|
+
/** Relative to the tree, the `cd` of a bash subshell. */
|
|
255
|
+
readonly cwd?: string
|
|
256
|
+
/** Fed to the command's stdin, the `<file` or `<<<` of the bash form. */
|
|
257
|
+
readonly input?: string
|
|
258
|
+
readonly env?: Readonly<Record<string, string>>
|
|
259
|
+
/** `inherit` leaves the stream on the terminal, where `pipe` hands it back for a file. */
|
|
260
|
+
readonly stderr?: 'pipe' | 'inherit' | 'ignore'
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface Captured {
|
|
264
|
+
readonly status: number
|
|
265
|
+
readonly stdout: string
|
|
266
|
+
readonly stderr: string
|
|
228
267
|
}
|
|
229
268
|
|
|
230
269
|
export interface StageContext {
|
|
@@ -240,6 +279,8 @@ export interface StageContext {
|
|
|
240
279
|
warn(message: string): void
|
|
241
280
|
/** A bare frame line, the `echo -e "${GREY}│${NC}"` of the bash form. */
|
|
242
281
|
bar(): void
|
|
282
|
+
/** Indents borrowed output inside the frame, the `pipe_output` of the bash form. */
|
|
283
|
+
pipe(text: string): void
|
|
243
284
|
}
|
|
244
285
|
fixtures(category: string, command: string, arm: string, stage: string): void
|
|
245
286
|
git(...args: string[]): void
|
|
@@ -248,6 +289,18 @@ export interface StageContext {
|
|
|
248
289
|
run(command: string, args: readonly string[], options?: RunOptions): number
|
|
249
290
|
/** Runs a command and returns its stdout, stopping the arm on a failure. */
|
|
250
291
|
read(command: string, args: readonly string[], options?: RunOptions): string
|
|
292
|
+
/**
|
|
293
|
+
* Runs a command and returns both streams with its status, never stopping the
|
|
294
|
+
* arm. For the `>file 2>file || status=$?` shape, where the outcome is the
|
|
295
|
+
* record the arm exists to hold.
|
|
296
|
+
*/
|
|
297
|
+
capture(
|
|
298
|
+
command: string,
|
|
299
|
+
args: readonly string[],
|
|
300
|
+
options?: CaptureOptions,
|
|
301
|
+
): Captured
|
|
302
|
+
/** Writes to a stream the way a bare `cat` or `echo` did, in order with the commands around it. */
|
|
303
|
+
print(text: string, stream?: 'stdout' | 'stderr'): void
|
|
251
304
|
write(path: string, content: string): void
|
|
252
305
|
append(path: string, content: string): void
|
|
253
306
|
mkdir(path: string): void
|
|
@@ -291,14 +344,24 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
291
344
|
args: readonly string[],
|
|
292
345
|
stdout: 'inherit' | 'ignore' | 'pipe',
|
|
293
346
|
stderr: 'inherit' | 'ignore' | 'pipe',
|
|
347
|
+
extra: {
|
|
348
|
+
cwd?: string
|
|
349
|
+
input?: string
|
|
350
|
+
env?: Readonly<Record<string, string>>
|
|
351
|
+
} = {},
|
|
294
352
|
) => {
|
|
295
|
-
const stdio: StdioOptions = [
|
|
353
|
+
const stdio: StdioOptions = [
|
|
354
|
+
extra.input === undefined ? 'ignore' : 'pipe',
|
|
355
|
+
stdout,
|
|
356
|
+
stderr,
|
|
357
|
+
]
|
|
296
358
|
|
|
297
359
|
return spawnSync(command, [...args], {
|
|
298
|
-
cwd: dir,
|
|
299
|
-
env,
|
|
360
|
+
cwd: extra.cwd === undefined ? dir : join(dir, extra.cwd),
|
|
361
|
+
env: extra.env === undefined ? env : { ...env, ...extra.env },
|
|
300
362
|
encoding: 'utf8',
|
|
301
363
|
stdio,
|
|
364
|
+
input: extra.input,
|
|
302
365
|
})
|
|
303
366
|
}
|
|
304
367
|
|
|
@@ -312,6 +375,7 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
312
375
|
args,
|
|
313
376
|
runOptions.stdout ?? 'inherit',
|
|
314
377
|
runOptions.stderr ?? 'inherit',
|
|
378
|
+
{ env: runOptions.env },
|
|
315
379
|
)
|
|
316
380
|
const status = result.status ?? 1
|
|
317
381
|
if (status !== 0 && runOptions.allowFailure !== true)
|
|
@@ -325,7 +389,13 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
325
389
|
args: readonly string[],
|
|
326
390
|
runOptions: RunOptions = {},
|
|
327
391
|
): string => {
|
|
328
|
-
const result = spawn(
|
|
392
|
+
const result = spawn(
|
|
393
|
+
command,
|
|
394
|
+
args,
|
|
395
|
+
'pipe',
|
|
396
|
+
runOptions.stderr ?? 'inherit',
|
|
397
|
+
{ env: runOptions.env },
|
|
398
|
+
)
|
|
329
399
|
const status = result.status ?? 1
|
|
330
400
|
if (status !== 0 && runOptions.allowFailure !== true)
|
|
331
401
|
throw new ScenarioStop(status, 'exited')
|
|
@@ -333,6 +403,26 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
333
403
|
return status === 0 ? result.stdout : ''
|
|
334
404
|
}
|
|
335
405
|
|
|
406
|
+
const capture = (
|
|
407
|
+
command: string,
|
|
408
|
+
args: readonly string[],
|
|
409
|
+
captureOptions: CaptureOptions = {},
|
|
410
|
+
): Captured => {
|
|
411
|
+
const result = spawn(
|
|
412
|
+
command,
|
|
413
|
+
args,
|
|
414
|
+
'pipe',
|
|
415
|
+
captureOptions.stderr ?? 'pipe',
|
|
416
|
+
captureOptions,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
status: result.status ?? 1,
|
|
421
|
+
stdout: result.stdout ?? '',
|
|
422
|
+
stderr: result.stderr ?? '',
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
336
426
|
const git = (...args: string[]): void => {
|
|
337
427
|
run('git', args)
|
|
338
428
|
}
|
|
@@ -476,12 +566,20 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
476
566
|
const { GREY, NC } = palette(process.stderr)
|
|
477
567
|
process.stderr.write(`${GREY}│${NC}\n`)
|
|
478
568
|
},
|
|
569
|
+
// `pipe_output` read lines, so an empty body printed nothing at all.
|
|
570
|
+
pipe: (text) => {
|
|
571
|
+
if (text !== '') pipeOutput(text)
|
|
572
|
+
},
|
|
479
573
|
},
|
|
480
574
|
fixtures,
|
|
481
575
|
git,
|
|
482
576
|
commit: (subject) => git('commit', '-m', subject, '--no-verify', '-q'),
|
|
483
577
|
run,
|
|
484
578
|
read,
|
|
579
|
+
capture,
|
|
580
|
+
print: (text, stream = 'stdout') => {
|
|
581
|
+
writeSync(stream === 'stdout' ? 1 : 2, text)
|
|
582
|
+
},
|
|
485
583
|
write: (path, content) => {
|
|
486
584
|
mkdirSync(dirname(inTree(path)), { recursive: true })
|
|
487
585
|
writeFileSync(inTree(path), content)
|
|
@@ -510,11 +608,12 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
510
608
|
async function routeArm(
|
|
511
609
|
names: readonly string[],
|
|
512
610
|
env: NodeJS.ProcessEnv,
|
|
611
|
+
prompt: string,
|
|
513
612
|
): Promise<string> {
|
|
514
613
|
const echo = (name: string): string => {
|
|
515
614
|
const { GREY, NC, WHITE } = palette(process.stderr)
|
|
516
615
|
process.stderr.write(
|
|
517
|
-
`${GREY}│${NC}\n${GREY}◇${NC} ${
|
|
616
|
+
`${GREY}│${NC}\n${GREY}◇${NC} ${prompt} ${WHITE}${name}${NC}\n`,
|
|
518
617
|
)
|
|
519
618
|
|
|
520
619
|
return name
|
|
@@ -533,13 +632,13 @@ async function routeArm(
|
|
|
533
632
|
|
|
534
633
|
if (!process.stdin.isTTY) {
|
|
535
634
|
logError(
|
|
536
|
-
`${
|
|
635
|
+
`${prompt} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.`,
|
|
537
636
|
)
|
|
538
637
|
throw new ScenarioStop(1, 'exited')
|
|
539
638
|
}
|
|
540
639
|
|
|
541
640
|
return select({
|
|
542
|
-
message:
|
|
641
|
+
message: prompt,
|
|
543
642
|
options: names.map((name) => ({ value: name, label: name })),
|
|
544
643
|
})
|
|
545
644
|
}
|
|
@@ -560,8 +659,13 @@ export async function stageScenarioInProcess(
|
|
|
560
659
|
): Promise<StageOutcome> {
|
|
561
660
|
try {
|
|
562
661
|
const names = armNames(definition)
|
|
662
|
+
await definition.prepare?.(
|
|
663
|
+
createStageContext({ ...options, arm: undefined }),
|
|
664
|
+
)
|
|
563
665
|
const name =
|
|
564
|
-
names.length === 0
|
|
666
|
+
names.length === 0
|
|
667
|
+
? DEFAULT_ARM_NAME
|
|
668
|
+
: await routeArm(names, options.env, definition.prompt ?? ROUTE_PROMPT)
|
|
565
669
|
const setup = Object.hasOwn(definition.arms, name)
|
|
566
670
|
? definition.arms[name]
|
|
567
671
|
: undefined
|
|
@@ -168,8 +168,8 @@ const DOCS_PATH = /(?<![\w./-])docs\/[^\s`)\]]*\.md\b/g
|
|
|
168
168
|
* Reported unless `isStandardsPathReportable` reads the match as a
|
|
169
169
|
* deliberate placeholder rather than a real citation. A body illustrating
|
|
170
170
|
* the shape a project's own `standards/<name>.md` takes writes a bracketed
|
|
171
|
-
* token this pattern also matches, such as `standards/<slug>.md`
|
|
172
|
-
*
|
|
171
|
+
* token this pattern also matches, such as `standards/<slug>.md`
|
|
172
|
+
* in a skill that drafts one, which names no real file. A bare,
|
|
173
173
|
* non-bracketed match now reports whether or not the file exists, unlike
|
|
174
174
|
* `DOCS_PATH`, which still gates on existence: a same-repository citation
|
|
175
175
|
* under this corpus names no target project's own tree the way a `docs/`
|
|
@@ -206,7 +206,7 @@ const PHASE_LABEL = /\bv\d+\.\d+(?!\.\d)\b/g
|
|
|
206
206
|
*
|
|
207
207
|
* Anchored on the trailing `\d{3}-[\w-]+\.md` rather than on the bare
|
|
208
208
|
* `.claude/rules/` prefix, which is what keeps a folder mention carrying no
|
|
209
|
-
* number, such as `create-rule`, `
|
|
209
|
+
* number, such as `create-rule`, `target-check`, and `target-setup` already
|
|
210
210
|
* write correctly, from matching. The segment group between `rules/` and the
|
|
211
211
|
* numbered file admits both a governance-namespace path
|
|
212
212
|
* (`canon/canon/604-scratch.md`) and a project-namespace one
|
package/src/standards/read.ts
CHANGED
|
@@ -53,6 +53,46 @@ export function standardRoots(root: string): StandardRoot[] {
|
|
|
53
53
|
]
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Standards retired into the skill reference that took them, keyed by name with
|
|
58
|
+
* a path relative to the package root. Each retirement adds one line, and the
|
|
59
|
+
* map goes with the verb in the last slice of the retirement.
|
|
60
|
+
*/
|
|
61
|
+
const MOVED_STANDARDS: Readonly<Record<string, string>> = {
|
|
62
|
+
board: 'claude/skills/task-board/references/board.md',
|
|
63
|
+
markdown: 'claude/skills/markdown-craft/references/markdown.md',
|
|
64
|
+
canvas: 'claude/skills/canvas/references/canvas.md',
|
|
65
|
+
glossary: 'claude/skills/teach-workspace/references/glossary.md',
|
|
66
|
+
groundwork: 'claude/skills/plan-groundwork/references/groundwork.md',
|
|
67
|
+
'groundwork-spikes':
|
|
68
|
+
'claude/skills/plan-groundwork/references/groundwork-spikes.md',
|
|
69
|
+
intake: 'claude/skills/plan-intake/references/intake.md',
|
|
70
|
+
mermaid: 'claude/skills/draft-figure/references/mermaid.md',
|
|
71
|
+
plan: 'claude/skills/plan-feature/references/plan.md',
|
|
72
|
+
'plan-lifecycle': 'claude/skills/plan-feature/references/plan-lifecycle.md',
|
|
73
|
+
ready: 'claude/skills/draft-ready/references/ready.md',
|
|
74
|
+
requirements: 'claude/skills/draft-doc/references/requirements.md',
|
|
75
|
+
'skill-paths': 'claude/skills/create-skill/references/skill-paths.md',
|
|
76
|
+
'skill-practice': 'claude/skills/create-skill/references/skill-practice.md',
|
|
77
|
+
session: 'claude/skills/session-map/references/session.md',
|
|
78
|
+
'skill-requirement':
|
|
79
|
+
'claude/skills/create-skill/references/skill-requirement.md',
|
|
80
|
+
tasks: 'claude/skills/task-board/references/tasks.md',
|
|
81
|
+
'tasks-decline': 'claude/skills/task-board/references/tasks-decline.md',
|
|
82
|
+
teach: 'claude/skills/teach-workspace/references/teach.md',
|
|
83
|
+
wiki: 'claude/skills/draft-doc/references/wiki.md',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The package-relative path a retired standard now lives at, so a caller that
|
|
88
|
+
* resolves against a root of its own tests the same map the verb reads.
|
|
89
|
+
*/
|
|
90
|
+
export function movedStandardPath(name: string): string | undefined {
|
|
91
|
+
if (!Object.hasOwn(MOVED_STANDARDS, name)) return undefined
|
|
92
|
+
|
|
93
|
+
return MOVED_STANDARDS[name]
|
|
94
|
+
}
|
|
95
|
+
|
|
56
96
|
function filename(name: string): string {
|
|
57
97
|
return name.endsWith('.md') ? name : `${name}.md`
|
|
58
98
|
}
|
|
@@ -69,7 +109,16 @@ export function resolveStandard(
|
|
|
69
109
|
if (existsSync(path)) return { path, source: join(label, file) }
|
|
70
110
|
}
|
|
71
111
|
|
|
72
|
-
|
|
112
|
+
const movedName = basename(file, '.md')
|
|
113
|
+
if (!Object.hasOwn(MOVED_STANDARDS, movedName)) return undefined
|
|
114
|
+
|
|
115
|
+
const moved = MOVED_STANDARDS[movedName]
|
|
116
|
+
if (!moved) return undefined
|
|
117
|
+
|
|
118
|
+
const path = join(PROJECT_ROOT, moved)
|
|
119
|
+
if (!existsSync(path)) return undefined
|
|
120
|
+
|
|
121
|
+
return { path, source: join(PACKAGE_LABEL, moved) }
|
|
73
122
|
}
|
|
74
123
|
|
|
75
124
|
/**
|
|
@@ -92,6 +141,8 @@ export function listStandards(root: string): string[] {
|
|
|
92
141
|
}
|
|
93
142
|
}
|
|
94
143
|
|
|
144
|
+
for (const name of Object.keys(MOVED_STANDARDS)) names.add(name)
|
|
145
|
+
|
|
95
146
|
return [...names].sort()
|
|
96
147
|
}
|
|
97
148
|
|
package/src/sync/seeds-report.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface SeedsReport {
|
|
|
34
34
|
* Classifies every seed the toolkit ships against the target's copy, and never
|
|
35
35
|
* returns a change. Seeds are copy-once files a project is expected to edit, so
|
|
36
36
|
* the engine's copy path would overwrite `CLAUDE.md` wholesale. Reporting alone
|
|
37
|
-
* is what lets
|
|
37
|
+
* is what lets an operator merge one section at a time by hand instead.
|
|
38
38
|
*
|
|
39
39
|
* Attribution reuses the history reader rather than the engine's own recovery
|
|
40
40
|
* pass, which is private and takes a `SyncAdapter` seeds have no way to supply.
|
package/src/tasks/answers.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
|
-
import { isAbsolute, relative, resolve } from 'node:path'
|
|
3
|
+
import { basename, isAbsolute, relative, resolve } from 'node:path'
|
|
4
4
|
import { isUnder } from '@/tasks/paths'
|
|
5
|
+
import { JUDGED_APART_REMEDY, readLivePlans, stackPairs } from '@/tasks/stack'
|
|
5
6
|
import { recordDir, recordDirs } from '@/roots/record'
|
|
6
7
|
import {
|
|
7
8
|
hasStagedBatches,
|
|
@@ -231,6 +232,18 @@ export async function planAnswers(
|
|
|
231
232
|
})
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
const stem = basename(resolved.path, '.md')
|
|
236
|
+
const pairs = stackPairs(await readLivePlans(root))
|
|
237
|
+
|
|
238
|
+
for (const { child, parent, shared } of pairs) {
|
|
239
|
+
if (child !== stem) continue
|
|
240
|
+
|
|
241
|
+
open.push({
|
|
242
|
+
label: 'Stacked sibling',
|
|
243
|
+
why: `stacks on \`${parent}\` and shares ${shared} declared files with it. ${JUDGED_APART_REMEDY}`,
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
234
247
|
return {
|
|
235
248
|
ok: true,
|
|
236
249
|
plan: resolved.plan,
|
package/src/tasks/reach.ts
CHANGED
|
@@ -11,7 +11,7 @@ const PLANS = 'plans'
|
|
|
11
11
|
const MARKDOWN = '.md'
|
|
12
12
|
const NONE_IDENTIFIED = 'None identified.'
|
|
13
13
|
|
|
14
|
-
/** The one group a dispatch reads, per `
|
|
14
|
+
/** The one group a dispatch reads, per `claude/skills/task-board/references/board.md`. */
|
|
15
15
|
const DISPATCH_GROUP = 'Run now'
|
|
16
16
|
|
|
17
17
|
export const REACH_REFUSALS = [
|
|
@@ -146,7 +146,7 @@ function namesFile(span: string): boolean {
|
|
|
146
146
|
* An entry carrying no colon at all is taken whole. The standard requires a
|
|
147
147
|
* reason rather than the punctuation introducing it, so the alternative is
|
|
148
148
|
* reading such an entry as declaring nothing, which reports every path it
|
|
149
|
-
* names as undeclared. `
|
|
149
|
+
* names as undeclared. `claude/skills/plan-feature/references/plan.md` fixes the colon form, so the
|
|
150
150
|
* conforming entry never reaches this fallback.
|
|
151
151
|
*
|
|
152
152
|
* A rename declares both sides, since both are paths the branch writes and
|