@erclx/canon 5.31.0 → 5.33.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/auto-ship/references/worktree-name.md +1 -1
- package/claude/skills/backlog-triage/REQUIREMENT.md +2 -2
- package/claude/skills/backlog-triage/SKILL.md +2 -2
- package/claude/skills/canon-cli/REQUIREMENT.md +19 -18
- package/claude/skills/canon-cli/SKILL.md +75 -32
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +11 -14
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
- package/claude/skills/canon-feedback-triage/SKILL.md +49 -30
- package/claude/skills/{canvas → canvas-board}/REQUIREMENT.md +5 -6
- package/claude/skills/{canvas → canvas-board}/SKILL.md +7 -7
- package/{standards → claude/skills/canvas-board/references}/canvas.md +12 -1
- package/{standards → claude/skills/canvas-board/references}/design.md +13 -5
- package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
- package/claude/skills/context-fold/REQUIREMENT.md +28 -48
- package/claude/skills/context-fold/SKILL.md +26 -95
- package/claude/skills/context-fold/references/classify.md +7 -8
- package/claude/skills/context-fold/references/completion-report.md +4 -8
- package/claude/skills/context-fold/references/context-refresh.md +7 -17
- package/{standards → claude/skills/context-fold/references}/context.md +19 -10
- package/claude/skills/context-fold/references/unusable-baseline.md +4 -8
- package/claude/skills/create-rule/REQUIREMENT.md +0 -1
- package/claude/skills/create-rule/SKILL.md +2 -2
- package/{standards → claude/skills/create-rule/references}/rule.md +12 -5
- package/claude/skills/create-skill/REQUIREMENT.md +6 -4
- package/claude/skills/create-skill/SKILL.md +10 -9
- package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
- package/{standards → claude/skills/create-skill/references}/skill.md +18 -15
- 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 +3 -5
- 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 +3 -3
- package/{standards → claude/skills/docs-sync/references}/docs.md +11 -0
- package/{standards → claude/skills/docs-sync/references}/readme.md +9 -2
- 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 +9 -10
- 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 +2 -2
- 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 +3 -3
- package/claude/skills/draft-identity/SKILL.md +10 -8
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +7 -7
- 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-branch/SKILL.md +1 -1
- package/{standards → claude/skills/git-branch/references}/branch.md +1 -1
- package/claude/skills/git-commit/SKILL.md +2 -2
- package/claude/skills/git-followup/REQUIREMENT.md +1 -1
- package/claude/skills/git-followup/SKILL.md +1 -1
- 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 +13 -13
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +38 -14
- package/claude/skills/git-pr/references/labels.md +5 -1
- package/{standards → claude/skills/git-pr/references}/pr.md +19 -9
- package/{standards → claude/skills/git-pr/references}/publish.md +5 -3
- package/claude/skills/git-pr/references/task-number.md +1 -1
- package/claude/skills/git-ship/REQUIREMENT.md +0 -2
- package/claude/skills/git-ship/SKILL.md +5 -12
- package/claude/skills/git-split/SKILL.md +3 -3
- package/claude/skills/git-stage/SKILL.md +1 -1
- package/claude/skills/plan-feature/REQUIREMENT.md +1 -1
- package/claude/skills/plan-feature/SKILL.md +2 -6
- package/{standards → claude/skills/plan-feature/references}/plan.md +9 -10
- 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 +2 -2
- package/claude/skills/review-pr/SKILL.md +8 -10
- package/claude/skills/review-pr/references/body-variants.md +2 -2
- package/claude/skills/review-pr/references/key-changes.md +3 -3
- package/claude/skills/review-pr/references/post.md +4 -2
- package/claude/skills/review-ui/REQUIREMENT.md +1 -2
- package/claude/skills/review-ui/SKILL.md +2 -2
- package/claude/skills/review-ui/references/post.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +3 -3
- package/claude/skills/role-orchestrator/SKILL.md +10 -10
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +3 -3
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +9 -9
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +23 -1
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +2 -5
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +4 -1
- package/claude/skills/role-planner/REQUIREMENT.md +2 -1
- package/claude/skills/role-planner/SKILL.md +15 -4
- package/claude/skills/role-reviewer/SKILL.md +2 -2
- package/claude/skills/role-worker/REQUIREMENT.md +1 -0
- package/claude/skills/role-worker/SKILL.md +11 -3
- 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 +9 -17
- 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 +6 -8
- package/claude/skills/session-resume/SKILL.md +3 -9
- package/claude/skills/session-worktree/SKILL.md +6 -6
- 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 +2 -2
- package/claude/skills/task-board/REQUIREMENT.md +7 -12
- package/claude/skills/task-board/SKILL.md +9 -21
- 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 +15 -10
- package/claude/skills/task-complete/REQUIREMENT.md +47 -0
- package/claude/skills/task-complete/SKILL.md +86 -0
- package/claude/skills/{task-board → task-complete}/references/archive.md +5 -5
- package/claude/skills/task-complete/references/receipt-sweep.md +32 -0
- package/claude/skills/task-complete/references/unusable-baseline.md +19 -0
- package/claude/skills/teach-workspace/REQUIREMENT.md +6 -2
- package/claude/skills/teach-workspace/SKILL.md +9 -5
- package/claude/skills/teach-workspace/references/lesson-craft.md +1 -1
- package/claude/skills/teach-workspace/references/promotion-fold.md +29 -0
- package/claude/skills/teach-workspace/references/promotion.md +3 -3
- package/claude/skills/teach-workspace/references/revisit.md +1 -1
- package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
- package/claude/skills/ux-audit/SKILL.md +3 -4
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/claude/skills/write-human/REQUIREMENT.md +0 -1
- package/claude/skills/write-human/SKILL.md +1 -1
- package/docs/agents/audits.md +1 -1
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +4 -8
- package/docs/agents/context-audit-checks.md +11 -25
- 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 +5 -7
- package/docs/agents/indexes.md +1 -1
- package/docs/agents/install-and-sync.md +6 -11
- package/docs/agents/intake.md +1 -1
- package/docs/agents/key-changes.md +5 -5
- package/docs/agents/label-coverage.md +11 -0
- package/docs/agents/migrate.md +1 -1
- package/docs/agents/records-push.md +2 -2
- package/docs/agents/records-stale.md +13 -48
- package/docs/agents/records-validate.md +4 -8
- package/docs/agents/records.md +6 -28
- package/docs/agents/restated.md +1 -1
- package/docs/agents/rule-citations.md +2 -2
- package/docs/agents/sandbox.md +4 -4
- 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 +1 -1
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +3 -3
- package/docs/agents/teach.md +1 -1
- package/docs/agents/upstream.md +26 -2
- package/docs/target/migrations.md +32 -0
- package/docs/target/projects.md +5 -7
- package/docs/target/scenarios.md +2 -2
- package/docs/target/sync.md +4 -4
- package/docs/workflow/ai-workflow.md +7 -38
- package/docs/workflow/index.md +1 -1
- package/docs/workflow/operating-model.md +10 -10
- package/docs/workflow/parallel-features.md +12 -12
- package/docs/workflow/skill-map-on-request.md +21 -24
- package/docs/workflow/skill-map.md +23 -34
- package/docs/workflow/visual-design-workflow.md +13 -16
- package/governance/renames.toml +0 -30
- package/governance/rules/canon/605-worktrees.md +1 -1
- package/governance/rules/canon/context.md +13 -0
- package/governance/rules/{standards → canon}/plan.md +4 -5
- package/governance/rules/canon/tasks.md +18 -0
- package/governance/rules/claude/skill.md +17 -0
- package/governance/stacks/base.toml +2 -4
- package/package.json +4 -4
- package/scripts/core/install-check.sh +4 -4
- package/scripts/core/regen-web-previews.ts +1 -1
- package/scripts/lib/ui.sh +0 -159
- package/src/audits/catalog.ts +5 -98
- package/src/claude/cases/authoring.ts +0 -18
- package/src/claude/cases/misc.ts +1 -14
- package/src/claude/cases/setup.ts +7 -20
- package/src/claude/cases/workflow.ts +6 -34
- package/src/claude/seeds.ts +1 -7
- package/src/claude/skills-audit.ts +3 -3
- package/src/cli/help.ts +4 -5
- package/src/cli/ui.ts +2 -3
- package/src/cli.ts +0 -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 -15
- package/src/commands/labels.ts +326 -7
- package/src/commands/migrate.ts +6 -1
- package/src/commands/pr.ts +1 -1
- package/src/commands/records.ts +17 -403
- package/src/commands/sandbox.ts +3 -6
- package/src/commands/sync.ts +5 -3
- package/src/commands/tasks.ts +3 -3
- package/src/commands/upstream.ts +135 -0
- package/src/context/audit.ts +10 -19
- 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 +8 -13
- package/src/context/gate.ts +11 -91
- package/src/context/requirements.ts +13 -4
- package/src/counts/scan.ts +3 -3
- package/src/design/budget.ts +1 -1
- package/src/design/components.ts +3 -3
- package/src/design/document.ts +1 -1
- package/src/design/regen.ts +1 -1
- package/src/design/render.ts +1 -1
- package/src/feedback/body.ts +0 -12
- package/src/gate/measures.ts +7 -58
- package/src/gate/sequencer.ts +56 -1
- package/src/gate/stages.ts +1 -7
- package/src/gov/citations.ts +9 -3
- package/src/gov/install.ts +2 -3
- package/src/gov/restated.ts +3 -3
- package/src/gov/stacks.ts +1 -1
- package/src/hooks/post-merge.ts +1 -1
- package/src/labels/apply.ts +39 -0
- package/src/labels/format.ts +3 -3
- package/src/labels/phase.ts +3 -3
- package/src/labels/sections.ts +43 -0
- package/src/markdown/structure.ts +2 -2
- package/src/migrate/record-layout.ts +3 -3
- package/src/migrate/skill-names.ts +20 -1
- package/src/pr/paths.ts +4 -4
- package/src/records/backup.ts +16 -6
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +2 -1
- package/src/records/stale-canonical.ts +33 -3
- package/src/records/validate.ts +2 -188
- package/src/roots/record.ts +1 -1
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/coverage.ts +2 -6
- package/src/sandbox/equivalence.ts +5 -44
- package/src/sandbox/expect.ts +4 -4
- package/src/sandbox/provision.ts +13 -36
- package/src/sandbox/scenario.ts +137 -55
- package/src/sandbox/tree.ts +2 -6
- package/src/shipped/references.ts +3 -3
- package/src/standards/read.ts +44 -0
- package/src/sync/git.ts +1 -1
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/archive.ts +5 -5
- package/src/tasks/branch.ts +3 -3
- package/src/tasks/label.ts +1 -1
- package/src/tasks/reach.ts +2 -2
- package/src/tasks/record.ts +2 -2
- package/src/tasks/validate.ts +2 -2
- package/src/teach/workspace.ts +1 -1
- package/src/upstream/cursor.ts +1 -1
- package/src/upstream/due.ts +128 -0
- package/src/upstream/releases.ts +2 -1
- package/standards/index.md +0 -35
- package/standards/standard.md +1 -7
- package/tooling/base/reference.md +2 -2
- package/tooling/base/seeds/.github/pull_request_template.md +2 -2
- package/tooling/claude/reference.md +7 -21
- package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +9 -1
- package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
- package/tooling/claude/seeds/.claude/hooks/review-post-scan.sh +171 -0
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
- package/tooling/claude/seeds/.claude/settings.json +9 -0
- 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/promoted-pages.md +0 -29
- package/claude/skills/context-fold/references/receipt-sweep.md +0 -43
- 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/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/603-memory.md +0 -11
- package/governance/rules/lang/120-bash.md +0 -18
- package/governance/rules/standards/architecture.md +0 -13
- package/governance/rules/standards/context.md +0 -14
- package/governance/rules/standards/design.md +0 -12
- package/governance/rules/standards/groundwork.md +0 -13
- package/governance/rules/standards/intake.md +0 -12
- package/governance/rules/standards/memory.md +0 -12
- package/governance/rules/standards/readme.md +0 -12
- package/governance/rules/standards/ready.md +0 -13
- package/governance/rules/standards/requirements.md +0 -12
- package/governance/rules/standards/rule.md +0 -13
- package/governance/rules/standards/session.md +0 -13
- package/governance/rules/standards/skill.md +0 -21
- package/governance/rules/standards/standard.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/scripts/config.sh +0 -11
- package/scripts/lib/gov.sh +0 -14
- package/scripts/lib/sandbox-fixtures.sh +0 -191
- package/scripts/lib/sandbox-git.sh +0 -125
- package/scripts/lib/sandbox-path.sh +0 -50
- package/scripts/sandbox-hook.sh +0 -99
- 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/gov/standard-rules.ts +0 -179
- package/src/records/migrate.ts +0 -78
- package/src/records/stale.ts +0 -243
- package/src/repo/metadata.ts +0 -206
- package/src/sandbox/hooks.ts +0 -128
- 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/draft-figure/references}/figures.md +0 -0
- /package/{standards → claude/skills/git-branch/references}/slug.md +0 -0
- /package/{standards → claude/skills/git-commit/references}/commit.md +0 -0
- /package/{standards → claude/skills/git-commit/references}/versioning.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/sandbox/provision.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from 'node:fs'
|
|
10
10
|
import { basename, dirname, join, relative } from 'node:path'
|
|
11
|
-
import { probeScenario, stageScenario } from '@/sandbox/hooks'
|
|
12
11
|
import {
|
|
13
12
|
FIXTURE_SUFFIX,
|
|
14
13
|
FIXTURES_DIR,
|
|
@@ -34,9 +33,8 @@ import {
|
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
35
|
* Provisions one scenario into the sandbox tree, and resets or cleans that tree.
|
|
37
|
-
* A
|
|
38
|
-
*
|
|
39
|
-
* here, in the order the retired bash dispatcher held.
|
|
36
|
+
* A scenario's arm runs in process through `scenario.ts`, and everything
|
|
37
|
+
* around it runs here, in the order the retired bash dispatcher held.
|
|
40
38
|
*
|
|
41
39
|
* Narration is part of the contract. `canon sandbox equivalence` compares the
|
|
42
40
|
* log line for line against the bash harness, so every message, its frame
|
|
@@ -403,36 +401,22 @@ interface Scenario {
|
|
|
403
401
|
|
|
404
402
|
/**
|
|
405
403
|
* Reads what a scenario declares ahead of provisioning: its config exports and
|
|
406
|
-
* whether it stages from the anchor
|
|
407
|
-
*
|
|
404
|
+
* whether it stages from the anchor, from its module, which is also what its
|
|
405
|
+
* stage runs.
|
|
408
406
|
*/
|
|
409
407
|
function declareScenario(
|
|
410
408
|
root: string,
|
|
411
409
|
scenario: Scenario,
|
|
412
410
|
env: NodeJS.ProcessEnv,
|
|
413
|
-
):
|
|
414
|
-
| { kind: 'sh'; file: string; isAnchor: boolean }
|
|
415
|
-
| { kind: 'ts'; definition: ScenarioDefinition; isAnchor: boolean } {
|
|
411
|
+
): { definition: ScenarioDefinition; isAnchor: boolean } {
|
|
416
412
|
const name = `${scenario.category}/${scenario.command}`
|
|
417
413
|
const resolved = resolveScenarioFile(
|
|
418
414
|
join(root, 'sandbox'),
|
|
419
415
|
scenario.category,
|
|
420
416
|
scenario.command,
|
|
421
417
|
)
|
|
422
|
-
if (!resolved.ok && resolved.reason === 'ambiguous')
|
|
423
|
-
fail(
|
|
424
|
-
`Sandbox script ${name} exists as both .sh and .ts. Delete the one the port replaced.`,
|
|
425
|
-
)
|
|
426
418
|
if (!resolved.ok) fail(`Sandbox script not found: ${name}`)
|
|
427
419
|
|
|
428
|
-
if (resolved.kind === 'sh') {
|
|
429
|
-
const probe = probeScenario(resolved.file, env)
|
|
430
|
-
if (!probe.ok) throw new Halt(probe.status)
|
|
431
|
-
Object.assign(env, probe.exports)
|
|
432
|
-
|
|
433
|
-
return { kind: 'sh', file: resolved.file, isAnchor: probe.isAnchor }
|
|
434
|
-
}
|
|
435
|
-
|
|
436
420
|
let definition: ScenarioDefinition
|
|
437
421
|
try {
|
|
438
422
|
definition = loadScenario(resolved.file)
|
|
@@ -443,7 +427,7 @@ function declareScenario(
|
|
|
443
427
|
}
|
|
444
428
|
Object.assign(env, scenarioExports(definition, root, env))
|
|
445
429
|
|
|
446
|
-
return {
|
|
430
|
+
return { definition, isAnchor: definition.anchor === true }
|
|
447
431
|
}
|
|
448
432
|
|
|
449
433
|
async function provisionScenario(
|
|
@@ -481,21 +465,14 @@ async function provisionScenario(
|
|
|
481
465
|
// A scenario that execs a verb as its last step hands the run to that verb,
|
|
482
466
|
// whose own frame is the last one written, so nothing follows it here. An
|
|
483
467
|
// in-process arm writes its exports straight into `env`.
|
|
484
|
-
const staged =
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
dir: sandbox,
|
|
491
|
-
env,
|
|
492
|
-
arm: scenario.arm,
|
|
493
|
-
})),
|
|
494
|
-
exports: {},
|
|
495
|
-
}
|
|
468
|
+
const staged = await stageScenarioInProcess(declared.definition, {
|
|
469
|
+
root,
|
|
470
|
+
dir: sandbox,
|
|
471
|
+
env,
|
|
472
|
+
arm: scenario.arm,
|
|
473
|
+
})
|
|
496
474
|
if (staged.ending === 'replaced') throw new Halt(staged.status, false)
|
|
497
475
|
if (staged.ending === 'exited') throw new Halt(staged.status)
|
|
498
|
-
Object.assign(env, staged.exports)
|
|
499
476
|
|
|
500
477
|
const isSkipAutoCommit = (env.SANDBOX_SKIP_AUTO_COMMIT ?? '') !== ''
|
|
501
478
|
const isSkipInject = (env.SANDBOX_SKIP_SKILL_INJECT ?? '') !== ''
|
|
@@ -602,7 +579,7 @@ function parseTarget(target: string): { category: string; command: string } {
|
|
|
602
579
|
}
|
|
603
580
|
|
|
604
581
|
export interface SandboxRequest {
|
|
605
|
-
/** False where a caller already opened the frame, as `run
|
|
582
|
+
/** False where a caller already opened the frame, as `canon sandbox run` and the picker do. */
|
|
606
583
|
readonly isHeader: boolean
|
|
607
584
|
/** `<category>:<command>`, `reset`, or `clean`. */
|
|
608
585
|
readonly target: string
|
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,15 +42,13 @@ 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
|
+
/** The picker's message unless a scenario names another. */
|
|
37
46
|
const ROUTE_PROMPT = 'Which scenario?'
|
|
38
47
|
|
|
39
48
|
/** Every scenario that sets the anchor pushes to this one throwaway repository. */
|
|
40
49
|
export const SANDBOX_ANCHOR_REPO = 'canon-sandbox'
|
|
41
50
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
export type ScenarioKind = 'ts' | 'sh'
|
|
51
|
+
const EXTENSION = '.ts'
|
|
45
52
|
|
|
46
53
|
export type ArmSetup = (ctx: StageContext) => void | Promise<void>
|
|
47
54
|
|
|
@@ -50,6 +57,14 @@ export interface ScenarioDefinition {
|
|
|
50
57
|
readonly config?: Readonly<Record<string, string>>
|
|
51
58
|
/** Stages the tree from the anchor fixture and names the throwaway remote. */
|
|
52
59
|
readonly anchor?: boolean
|
|
60
|
+
/**
|
|
61
|
+
* Stages and narrates ahead of the picker, so the picker's echo lands after
|
|
62
|
+
* it on the frame. Runs before the arm is
|
|
63
|
+
* routed, which leaves `ctx.arm` undefined.
|
|
64
|
+
*/
|
|
65
|
+
readonly prepare?: ArmSetup
|
|
66
|
+
/** The picker's message, for a scenario that asks something other than the default. */
|
|
67
|
+
readonly prompt?: string
|
|
53
68
|
/** In routing order, so the first arm is the one a headless caller gets. */
|
|
54
69
|
readonly arms: Readonly<Record<string, ArmSetup>>
|
|
55
70
|
}
|
|
@@ -61,43 +76,27 @@ export function scenario(definition: ScenarioDefinition): ScenarioDefinition {
|
|
|
61
76
|
export interface ScenarioFile {
|
|
62
77
|
readonly command: string
|
|
63
78
|
readonly file: string
|
|
64
|
-
readonly kind: ScenarioKind
|
|
65
79
|
}
|
|
66
80
|
|
|
67
81
|
/**
|
|
68
|
-
* Lists the scenarios in one category folder, one per stem. A
|
|
69
|
-
*
|
|
70
|
-
*
|
|
82
|
+
* Lists the scenarios in one category folder, one per `.ts` stem. A file in any
|
|
83
|
+
* other form is ignored rather than refused, so a hand-written leftover does not
|
|
84
|
+
* break the listing.
|
|
71
85
|
*/
|
|
72
86
|
export function listScenarioFiles(categoryDir: string): {
|
|
73
87
|
scenarios: ScenarioFile[]
|
|
74
|
-
ambiguous: string[]
|
|
75
88
|
} {
|
|
76
|
-
const
|
|
89
|
+
const scenarios: ScenarioFile[] = []
|
|
77
90
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
78
|
-
if (!entry.isFile()) continue
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const command = entry.name.slice(0, -kind.length)
|
|
82
|
-
const found = byStem.get(command) ?? []
|
|
83
|
-
found.push({
|
|
84
|
-
command,
|
|
91
|
+
if (!entry.isFile() || !entry.name.endsWith(EXTENSION)) continue
|
|
92
|
+
scenarios.push({
|
|
93
|
+
command: entry.name.slice(0, -EXTENSION.length),
|
|
85
94
|
file: join(categoryDir, entry.name),
|
|
86
|
-
kind: kind === '.ts' ? 'ts' : 'sh',
|
|
87
95
|
})
|
|
88
|
-
byStem.set(command, found)
|
|
89
96
|
}
|
|
97
|
+
scenarios.sort((a, b) => a.command.localeCompare(b.command))
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
const ambiguous: string[] = []
|
|
93
|
-
for (const [command, found] of [...byStem].sort(([a], [b]) =>
|
|
94
|
-
a.localeCompare(b),
|
|
95
|
-
)) {
|
|
96
|
-
if (found.length > 1) ambiguous.push(command)
|
|
97
|
-
else if (found[0] !== undefined) scenarios.push(found[0])
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return { scenarios, ambiguous }
|
|
99
|
+
return { scenarios }
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
/** Every category folder under `sandbox/`, fixtures excluded. */
|
|
@@ -112,21 +111,18 @@ export function listCategories(sandboxDir: string): string[] {
|
|
|
112
111
|
|
|
113
112
|
export type ResolvedScenario =
|
|
114
113
|
| ({ readonly ok: true } & ScenarioFile)
|
|
115
|
-
| { readonly ok: false; readonly reason: 'missing'
|
|
114
|
+
| { readonly ok: false; readonly reason: 'missing' }
|
|
116
115
|
|
|
117
116
|
export function resolveScenarioFile(
|
|
118
117
|
sandboxDir: string,
|
|
119
118
|
category: string,
|
|
120
119
|
command: string,
|
|
121
120
|
): ResolvedScenario {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (file === undefined) return { ok: false, reason: 'missing' }
|
|
128
|
-
|
|
129
|
-
return { ok: true, command, file, kind: file.endsWith('.ts') ? 'ts' : 'sh' }
|
|
121
|
+
const file = join(sandboxDir, category, `${command}${EXTENSION}`)
|
|
122
|
+
if (!existsSync(file) || !statSync(file).isFile())
|
|
123
|
+
return { ok: false, reason: 'missing' }
|
|
124
|
+
|
|
125
|
+
return { ok: true, command, file }
|
|
130
126
|
}
|
|
131
127
|
|
|
132
128
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -144,7 +140,7 @@ export function loadScenario(file: string): ScenarioDefinition {
|
|
|
144
140
|
if (!isRecord(definition))
|
|
145
141
|
throw new Error(`${file} has no default export from scenario()`)
|
|
146
142
|
|
|
147
|
-
const { arms, config, anchor } = definition
|
|
143
|
+
const { arms, config, anchor, prepare, prompt } = definition
|
|
148
144
|
if (!isRecord(arms) || Object.keys(arms).length === 0)
|
|
149
145
|
throw new Error(`${file} declares no arms`)
|
|
150
146
|
for (const [name, setup] of Object.entries(arms))
|
|
@@ -158,6 +154,10 @@ export function loadScenario(file: string): ScenarioDefinition {
|
|
|
158
154
|
throw new Error(`${file} config holds a value that is not a string`)
|
|
159
155
|
if (anchor !== undefined && typeof anchor !== 'boolean')
|
|
160
156
|
throw new Error(`${file} anchor is not a boolean`)
|
|
157
|
+
if (prepare !== undefined && typeof prepare !== 'function')
|
|
158
|
+
throw new Error(`${file} prepare is not a function`)
|
|
159
|
+
if (prompt !== undefined && typeof prompt !== 'string')
|
|
160
|
+
throw new Error(`${file} prompt is not a string`)
|
|
161
161
|
|
|
162
162
|
return definition as unknown as ScenarioDefinition
|
|
163
163
|
}
|
|
@@ -169,7 +169,7 @@ export function armNames(definition: ScenarioDefinition): string[] {
|
|
|
169
169
|
return names.length === 1 && names[0] === DEFAULT_ARM_NAME ? [] : names
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
/**
|
|
172
|
+
/** Derives the organization from the toolkit's own remote unless `GITHUB_ORG` names one. */
|
|
173
173
|
function deriveGithubOrg(root: string, env: NodeJS.ProcessEnv): string {
|
|
174
174
|
if ((env.GITHUB_ORG ?? '') !== '') return env.GITHUB_ORG ?? ''
|
|
175
175
|
|
|
@@ -188,8 +188,8 @@ function deriveGithubOrg(root: string, env: NodeJS.ProcessEnv): string {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
/**
|
|
191
|
-
* What
|
|
192
|
-
*
|
|
191
|
+
* What provisioning reads off a scenario: its config, the derived organization,
|
|
192
|
+
* and the anchor's repository name. These land in the one
|
|
193
193
|
* environment provisioning passes to every child, the seed and gov installs
|
|
194
194
|
* included.
|
|
195
195
|
*/
|
|
@@ -225,6 +225,24 @@ export interface RunOptions {
|
|
|
225
225
|
readonly stderr?: 'inherit' | 'ignore'
|
|
226
226
|
/** Returns a failing status rather than stopping the arm, the `|| true` of the bash form. */
|
|
227
227
|
readonly allowFailure?: boolean
|
|
228
|
+
/** Set for this command alone, the `NAME=value command` prefix of the bash form. */
|
|
229
|
+
readonly env?: Readonly<Record<string, string>>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface CaptureOptions {
|
|
233
|
+
/** Relative to the tree, the `cd` of a bash subshell. */
|
|
234
|
+
readonly cwd?: string
|
|
235
|
+
/** Fed to the command's stdin, the `<file` or `<<<` of the bash form. */
|
|
236
|
+
readonly input?: string
|
|
237
|
+
readonly env?: Readonly<Record<string, string>>
|
|
238
|
+
/** `inherit` leaves the stream on the terminal, where `pipe` hands it back for a file. */
|
|
239
|
+
readonly stderr?: 'pipe' | 'inherit' | 'ignore'
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface Captured {
|
|
243
|
+
readonly status: number
|
|
244
|
+
readonly stdout: string
|
|
245
|
+
readonly stderr: string
|
|
228
246
|
}
|
|
229
247
|
|
|
230
248
|
export interface StageContext {
|
|
@@ -240,6 +258,8 @@ export interface StageContext {
|
|
|
240
258
|
warn(message: string): void
|
|
241
259
|
/** A bare frame line, the `echo -e "${GREY}│${NC}"` of the bash form. */
|
|
242
260
|
bar(): void
|
|
261
|
+
/** Indents borrowed output inside the frame, the `pipe_output` of the bash form. */
|
|
262
|
+
pipe(text: string): void
|
|
243
263
|
}
|
|
244
264
|
fixtures(category: string, command: string, arm: string, stage: string): void
|
|
245
265
|
git(...args: string[]): void
|
|
@@ -248,6 +268,18 @@ export interface StageContext {
|
|
|
248
268
|
run(command: string, args: readonly string[], options?: RunOptions): number
|
|
249
269
|
/** Runs a command and returns its stdout, stopping the arm on a failure. */
|
|
250
270
|
read(command: string, args: readonly string[], options?: RunOptions): string
|
|
271
|
+
/**
|
|
272
|
+
* Runs a command and returns both streams with its status, never stopping the
|
|
273
|
+
* arm. For the `>file 2>file || status=$?` shape, where the outcome is the
|
|
274
|
+
* record the arm exists to hold.
|
|
275
|
+
*/
|
|
276
|
+
capture(
|
|
277
|
+
command: string,
|
|
278
|
+
args: readonly string[],
|
|
279
|
+
options?: CaptureOptions,
|
|
280
|
+
): Captured
|
|
281
|
+
/** Writes to a stream the way a bare `cat` or `echo` did, in order with the commands around it. */
|
|
282
|
+
print(text: string, stream?: 'stdout' | 'stderr'): void
|
|
251
283
|
write(path: string, content: string): void
|
|
252
284
|
append(path: string, content: string): void
|
|
253
285
|
mkdir(path: string): void
|
|
@@ -291,14 +323,24 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
291
323
|
args: readonly string[],
|
|
292
324
|
stdout: 'inherit' | 'ignore' | 'pipe',
|
|
293
325
|
stderr: 'inherit' | 'ignore' | 'pipe',
|
|
326
|
+
extra: {
|
|
327
|
+
cwd?: string
|
|
328
|
+
input?: string
|
|
329
|
+
env?: Readonly<Record<string, string>>
|
|
330
|
+
} = {},
|
|
294
331
|
) => {
|
|
295
|
-
const stdio: StdioOptions = [
|
|
332
|
+
const stdio: StdioOptions = [
|
|
333
|
+
extra.input === undefined ? 'ignore' : 'pipe',
|
|
334
|
+
stdout,
|
|
335
|
+
stderr,
|
|
336
|
+
]
|
|
296
337
|
|
|
297
338
|
return spawnSync(command, [...args], {
|
|
298
|
-
cwd: dir,
|
|
299
|
-
env,
|
|
339
|
+
cwd: extra.cwd === undefined ? dir : join(dir, extra.cwd),
|
|
340
|
+
env: extra.env === undefined ? env : { ...env, ...extra.env },
|
|
300
341
|
encoding: 'utf8',
|
|
301
342
|
stdio,
|
|
343
|
+
input: extra.input,
|
|
302
344
|
})
|
|
303
345
|
}
|
|
304
346
|
|
|
@@ -312,6 +354,7 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
312
354
|
args,
|
|
313
355
|
runOptions.stdout ?? 'inherit',
|
|
314
356
|
runOptions.stderr ?? 'inherit',
|
|
357
|
+
{ env: runOptions.env },
|
|
315
358
|
)
|
|
316
359
|
const status = result.status ?? 1
|
|
317
360
|
if (status !== 0 && runOptions.allowFailure !== true)
|
|
@@ -325,7 +368,13 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
325
368
|
args: readonly string[],
|
|
326
369
|
runOptions: RunOptions = {},
|
|
327
370
|
): string => {
|
|
328
|
-
const result = spawn(
|
|
371
|
+
const result = spawn(
|
|
372
|
+
command,
|
|
373
|
+
args,
|
|
374
|
+
'pipe',
|
|
375
|
+
runOptions.stderr ?? 'inherit',
|
|
376
|
+
{ env: runOptions.env },
|
|
377
|
+
)
|
|
329
378
|
const status = result.status ?? 1
|
|
330
379
|
if (status !== 0 && runOptions.allowFailure !== true)
|
|
331
380
|
throw new ScenarioStop(status, 'exited')
|
|
@@ -333,6 +382,26 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
333
382
|
return status === 0 ? result.stdout : ''
|
|
334
383
|
}
|
|
335
384
|
|
|
385
|
+
const capture = (
|
|
386
|
+
command: string,
|
|
387
|
+
args: readonly string[],
|
|
388
|
+
captureOptions: CaptureOptions = {},
|
|
389
|
+
): Captured => {
|
|
390
|
+
const result = spawn(
|
|
391
|
+
command,
|
|
392
|
+
args,
|
|
393
|
+
'pipe',
|
|
394
|
+
captureOptions.stderr ?? 'pipe',
|
|
395
|
+
captureOptions,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
status: result.status ?? 1,
|
|
400
|
+
stdout: result.stdout ?? '',
|
|
401
|
+
stderr: result.stderr ?? '',
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
336
405
|
const git = (...args: string[]): void => {
|
|
337
406
|
run('git', args)
|
|
338
407
|
}
|
|
@@ -357,8 +426,7 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
357
426
|
|
|
358
427
|
/**
|
|
359
428
|
* `gh` answers an absent repository and an unreachable host with one status,
|
|
360
|
-
* so the 404 alone separates them. Creating is the opt-in
|
|
361
|
-
* `ensure_sandbox_anchor_repo` in `scripts/lib/sandbox-git.sh` records.
|
|
429
|
+
* so the 404 alone separates them. Creating is the opt-in.
|
|
362
430
|
*/
|
|
363
431
|
const ensureAnchorRepo = (org: string, repo: string): void => {
|
|
364
432
|
const probe = spawn(
|
|
@@ -413,7 +481,7 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
413
481
|
git('remote', 'add', 'origin', `https://github.com/${org}/${repo}.git`)
|
|
414
482
|
}
|
|
415
483
|
|
|
416
|
-
/**
|
|
484
|
+
/** Copies a fixture folder into the tree. */
|
|
417
485
|
const fixtures = (
|
|
418
486
|
category: string,
|
|
419
487
|
command: string,
|
|
@@ -476,12 +544,20 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
476
544
|
const { GREY, NC } = palette(process.stderr)
|
|
477
545
|
process.stderr.write(`${GREY}│${NC}\n`)
|
|
478
546
|
},
|
|
547
|
+
// `pipe_output` read lines, so an empty body printed nothing at all.
|
|
548
|
+
pipe: (text) => {
|
|
549
|
+
if (text !== '') pipeOutput(text)
|
|
550
|
+
},
|
|
479
551
|
},
|
|
480
552
|
fixtures,
|
|
481
553
|
git,
|
|
482
554
|
commit: (subject) => git('commit', '-m', subject, '--no-verify', '-q'),
|
|
483
555
|
run,
|
|
484
556
|
read,
|
|
557
|
+
capture,
|
|
558
|
+
print: (text, stream = 'stdout') => {
|
|
559
|
+
writeSync(stream === 'stdout' ? 1 : 2, text)
|
|
560
|
+
},
|
|
485
561
|
write: (path, content) => {
|
|
486
562
|
mkdirSync(dirname(inTree(path)), { recursive: true })
|
|
487
563
|
writeFileSync(inTree(path), content)
|
|
@@ -504,17 +580,18 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
504
580
|
}
|
|
505
581
|
|
|
506
582
|
/**
|
|
507
|
-
* Picks the arm
|
|
583
|
+
* Picks the arm: a routed name first,
|
|
508
584
|
* echoed on the frame, then the first arm for a headless caller, then a prompt.
|
|
509
585
|
*/
|
|
510
586
|
async function routeArm(
|
|
511
587
|
names: readonly string[],
|
|
512
588
|
env: NodeJS.ProcessEnv,
|
|
589
|
+
prompt: string,
|
|
513
590
|
): Promise<string> {
|
|
514
591
|
const echo = (name: string): string => {
|
|
515
592
|
const { GREY, NC, WHITE } = palette(process.stderr)
|
|
516
593
|
process.stderr.write(
|
|
517
|
-
`${GREY}│${NC}\n${GREY}◇${NC} ${
|
|
594
|
+
`${GREY}│${NC}\n${GREY}◇${NC} ${prompt} ${WHITE}${name}${NC}\n`,
|
|
518
595
|
)
|
|
519
596
|
|
|
520
597
|
return name
|
|
@@ -533,13 +610,13 @@ async function routeArm(
|
|
|
533
610
|
|
|
534
611
|
if (!process.stdin.isTTY) {
|
|
535
612
|
logError(
|
|
536
|
-
`${
|
|
613
|
+
`${prompt} requires a TTY. Pass an argument or set CANON_NON_INTERACTIVE=1.`,
|
|
537
614
|
)
|
|
538
615
|
throw new ScenarioStop(1, 'exited')
|
|
539
616
|
}
|
|
540
617
|
|
|
541
618
|
return select({
|
|
542
|
-
message:
|
|
619
|
+
message: prompt,
|
|
543
620
|
options: names.map((name) => ({ value: name, label: name })),
|
|
544
621
|
})
|
|
545
622
|
}
|
|
@@ -560,8 +637,13 @@ export async function stageScenarioInProcess(
|
|
|
560
637
|
): Promise<StageOutcome> {
|
|
561
638
|
try {
|
|
562
639
|
const names = armNames(definition)
|
|
640
|
+
await definition.prepare?.(
|
|
641
|
+
createStageContext({ ...options, arm: undefined }),
|
|
642
|
+
)
|
|
563
643
|
const name =
|
|
564
|
-
names.length === 0
|
|
644
|
+
names.length === 0
|
|
645
|
+
? DEFAULT_ARM_NAME
|
|
646
|
+
: await routeArm(names, options.env, definition.prompt ?? ROUTE_PROMPT)
|
|
565
647
|
const setup = Object.hasOwn(definition.arms, name)
|
|
566
648
|
? definition.arms[name]
|
|
567
649
|
: undefined
|
package/src/sandbox/tree.ts
CHANGED
|
@@ -8,11 +8,9 @@ import { join } from 'node:path'
|
|
|
8
8
|
* in `CANON_SANDBOX_RUN_ID` for the rest of this process, so a script that
|
|
9
9
|
* spawns a child inheriting `process.env` — `canon sandbox run` calling
|
|
10
10
|
* `canon sandbox` to provision and then `canon sandbox check` — resolves the same tree in every one
|
|
11
|
-
* of them, as do the
|
|
11
|
+
* of them, as do the installer children provisioning spawns. A process
|
|
12
12
|
* that already carries the variable, inherited from such a parent, reuses it
|
|
13
13
|
* rather than minting a new one.
|
|
14
|
-
*
|
|
15
|
-
* Twin of `mint_sandbox_run_id` in `scripts/lib/sandbox-path.sh`.
|
|
16
14
|
*/
|
|
17
15
|
export function mintSandboxRunId(): string {
|
|
18
16
|
const existing = process.env.CANON_SANDBOX_RUN_ID
|
|
@@ -28,9 +26,7 @@ export function mintSandboxRunId(): string {
|
|
|
28
26
|
* per-run default is unit-testable on its own rather than only through the
|
|
29
27
|
* command's registration.
|
|
30
28
|
*
|
|
31
|
-
*
|
|
32
|
-
* boundary rules out a shared constant, so a change to the default lands on
|
|
33
|
-
* both sides. The fall-through used to be one path per machine, so two
|
|
29
|
+
* The fall-through used to be one path per machine, so two
|
|
34
30
|
* sessions each resolving the default at once provisioned over each other
|
|
35
31
|
* with neither told; `mintSandboxRunId` gives the path a per-run component
|
|
36
32
|
* instead, which is what makes two such sessions land on two different trees.
|
|
@@ -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
|
@@ -59,7 +59,51 @@ export function standardRoots(root: string): StandardRoot[] {
|
|
|
59
59
|
* map goes with the verb in the last slice of the retirement.
|
|
60
60
|
*/
|
|
61
61
|
const MOVED_STANDARDS: Readonly<Record<string, string>> = {
|
|
62
|
+
board: 'claude/skills/task-board/references/board.md',
|
|
63
|
+
context: 'claude/skills/context-fold/references/context.md',
|
|
64
|
+
design: 'claude/skills/canvas-board/references/design.md',
|
|
65
|
+
docs: 'claude/skills/docs-sync/references/docs.md',
|
|
66
|
+
figures: 'claude/skills/draft-figure/references/figures.md',
|
|
67
|
+
readme: 'claude/skills/docs-sync/references/readme.md',
|
|
68
|
+
rule: 'claude/skills/create-rule/references/rule.md',
|
|
69
|
+
skill: 'claude/skills/create-skill/references/skill.md',
|
|
70
|
+
branch: 'claude/skills/git-branch/references/branch.md',
|
|
71
|
+
commit: 'claude/skills/git-commit/references/commit.md',
|
|
72
|
+
pr: 'claude/skills/git-pr/references/pr.md',
|
|
73
|
+
publish: 'claude/skills/git-pr/references/publish.md',
|
|
74
|
+
slug: 'claude/skills/git-branch/references/slug.md',
|
|
75
|
+
versioning: 'claude/skills/git-commit/references/versioning.md',
|
|
62
76
|
markdown: 'claude/skills/markdown-craft/references/markdown.md',
|
|
77
|
+
canvas: 'claude/skills/canvas-board/references/canvas.md',
|
|
78
|
+
glossary: 'claude/skills/teach-workspace/references/glossary.md',
|
|
79
|
+
groundwork: 'claude/skills/plan-groundwork/references/groundwork.md',
|
|
80
|
+
'groundwork-spikes':
|
|
81
|
+
'claude/skills/plan-groundwork/references/groundwork-spikes.md',
|
|
82
|
+
intake: 'claude/skills/plan-intake/references/intake.md',
|
|
83
|
+
mermaid: 'claude/skills/draft-figure/references/mermaid.md',
|
|
84
|
+
plan: 'claude/skills/plan-feature/references/plan.md',
|
|
85
|
+
'plan-lifecycle': 'claude/skills/plan-feature/references/plan-lifecycle.md',
|
|
86
|
+
ready: 'claude/skills/draft-ready/references/ready.md',
|
|
87
|
+
requirements: 'claude/skills/draft-doc/references/requirements.md',
|
|
88
|
+
'skill-paths': 'claude/skills/create-skill/references/skill-paths.md',
|
|
89
|
+
'skill-practice': 'claude/skills/create-skill/references/skill-practice.md',
|
|
90
|
+
session: 'claude/skills/session-map/references/session.md',
|
|
91
|
+
'skill-requirement':
|
|
92
|
+
'claude/skills/create-skill/references/skill-requirement.md',
|
|
93
|
+
tasks: 'claude/skills/task-board/references/tasks.md',
|
|
94
|
+
'tasks-decline': 'claude/skills/task-board/references/tasks-decline.md',
|
|
95
|
+
teach: 'claude/skills/teach-workspace/references/teach.md',
|
|
96
|
+
wiki: 'claude/skills/draft-doc/references/wiki.md',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The package-relative path a retired standard now lives at, so a caller that
|
|
101
|
+
* resolves against a root of its own tests the same map the verb reads.
|
|
102
|
+
*/
|
|
103
|
+
export function movedStandardPath(name: string): string | undefined {
|
|
104
|
+
if (!Object.hasOwn(MOVED_STANDARDS, name)) return undefined
|
|
105
|
+
|
|
106
|
+
return MOVED_STANDARDS[name]
|
|
63
107
|
}
|
|
64
108
|
|
|
65
109
|
function filename(name: string): string {
|
package/src/sync/git.ts
CHANGED
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/archive.ts
CHANGED
|
@@ -578,7 +578,7 @@ function isBulletFor(line: string, target: string): boolean {
|
|
|
578
578
|
|
|
579
579
|
/**
|
|
580
580
|
* Resolves the `Plan:` target against the board and against the project root
|
|
581
|
-
* both, which is how `
|
|
581
|
+
* both, which is how `task-complete` reads the same line. It accepts `../plans/x.md`
|
|
582
582
|
* and `.canon/plans/x.md` as one file, so a gate reading only the first form
|
|
583
583
|
* would pass the second and strand the plan this exists to protect.
|
|
584
584
|
*
|
|
@@ -633,7 +633,7 @@ function atOneRoot(path: string, plans: string[], root: string): string {
|
|
|
633
633
|
/**
|
|
634
634
|
* Names the other live tasks whose `Plan:` line lands on the same file through
|
|
635
635
|
* any of its links. This is
|
|
636
|
-
* the rule `
|
|
636
|
+
* the rule the `post-merge` hook applies before it archives a plan, held here so one
|
|
637
637
|
* question has one implementation: a plan another live task still cites is a
|
|
638
638
|
* plan the sweep is correct to leave, and a guard that read the folder instead
|
|
639
639
|
* refused every task sharing one plan and deadlocked the board against the
|
|
@@ -697,7 +697,7 @@ export type CitationOutcome = PlanCitations | ArchiveRefused
|
|
|
697
697
|
|
|
698
698
|
/**
|
|
699
699
|
* Answers where one task's plan sits and who else holds it, which is the whole
|
|
700
|
-
* of the last-live-citation rule.
|
|
700
|
+
* of the last-live-citation rule. The archive reads this rather than scanning
|
|
701
701
|
* the board itself, so the sweep that moves a plan and the gate that refuses a
|
|
702
702
|
* task archive cannot drift into disagreeing about which plan is free.
|
|
703
703
|
*
|
|
@@ -897,7 +897,7 @@ async function resolveStem(
|
|
|
897
897
|
|
|
898
898
|
/**
|
|
899
899
|
* Archives one task as a single unit: the move, the ordering-row removal, and
|
|
900
|
-
* the index regen. The hook and `task-
|
|
900
|
+
* the index regen. The hook and `task-complete` both call this, so every gate
|
|
901
901
|
* refuses rather than reports. A caller with nobody watching cannot act on a
|
|
902
902
|
* warning, and two callers gating differently is the drift this exists to stop.
|
|
903
903
|
*/
|
|
@@ -918,7 +918,7 @@ export async function archiveTask(
|
|
|
918
918
|
const from = join(dir, `${stem}.md`)
|
|
919
919
|
const text = await readFile(from, 'utf8')
|
|
920
920
|
|
|
921
|
-
// `
|
|
921
|
+
// `task-complete` ticks outcomes at ship time, before the merge, so an earlier
|
|
922
922
|
// slice merging after the last slice shipped would find every box ticked
|
|
923
923
|
// while the last slice is still open. Only the last number closes the task.
|
|
924
924
|
const last = readPullRequest(text).at(-1)
|