@isaacriehm/cairn-core 0.1.10 → 0.3.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/dist/.tsbuildinfo +1 -1
- package/dist/claude/runner.d.ts +1 -1
- package/dist/claude/runner.js +62 -1
- package/dist/claude/runner.js.map +1 -1
- package/dist/claude/types.d.ts +14 -7
- package/dist/claude/types.js +5 -7
- package/dist/claude/types.js.map +1 -1
- package/dist/context/index.d.ts +4 -4
- package/dist/context/index.js +3 -4
- package/dist/context/index.js.map +1 -1
- package/dist/context/spec-delta.js +3 -4
- package/dist/context/spec-delta.js.map +1 -1
- package/dist/context/task-summary.d.ts +18 -0
- package/dist/context/task-summary.js +89 -0
- package/dist/context/task-summary.js.map +1 -0
- package/dist/decision-capture/id.d.ts +21 -4
- package/dist/decision-capture/id.js +56 -5
- package/dist/decision-capture/id.js.map +1 -1
- package/dist/decision-capture/index.d.ts +7 -22
- package/dist/decision-capture/index.js +7 -17
- package/dist/decision-capture/index.js.map +1 -1
- package/dist/doctor/index.d.ts +2 -2
- package/dist/doctor/index.js +5 -60
- package/dist/doctor/index.js.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +1 -1
- package/dist/events/writer.d.ts +1 -1
- package/dist/events/writer.js +1 -1
- package/dist/gc/canary.d.ts +13 -23
- package/dist/gc/canary.js +36 -92
- package/dist/gc/canary.js.map +1 -1
- package/dist/gc/citation-integrity.d.ts +6 -6
- package/dist/gc/citation-integrity.js +64 -20
- package/dist/gc/citation-integrity.js.map +1 -1
- package/dist/gc/frontmatter.js +1 -1
- package/dist/gc/generator-drift.js +1 -1
- package/dist/gc/generator-drift.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.js +1 -1
- package/dist/gc/index.js.map +1 -1
- package/dist/gc/quality-update.d.ts +4 -4
- package/dist/gc/quality-update.js +5 -5
- package/dist/gc/quality-update.js.map +1 -1
- package/dist/gc/types.d.ts +1 -30
- package/dist/ground/index.d.ts +2 -2
- package/dist/ground/index.js +1 -1
- package/dist/ground/index.js.map +1 -1
- package/dist/ground/ledgers.d.ts +0 -4
- package/dist/ground/ledgers.js +10 -8
- package/dist/ground/ledgers.js.map +1 -1
- package/dist/ground/manifest.js +0 -4
- package/dist/ground/manifest.js.map +1 -1
- package/dist/ground/paths.js +0 -1
- package/dist/ground/paths.js.map +1 -1
- package/dist/ground/quality-grades.js +3 -1
- package/dist/ground/quality-grades.js.map +1 -1
- package/dist/ground/schemas.d.ts +2 -7
- package/dist/ground/schemas.js +7 -2
- package/dist/ground/schemas.js.map +1 -1
- package/dist/ground/scope-index.d.ts +62 -14
- package/dist/ground/scope-index.js +215 -45
- package/dist/ground/scope-index.js.map +1 -1
- package/dist/hooks/bypass-detection.js +14 -17
- package/dist/hooks/bypass-detection.js.map +1 -1
- package/dist/hooks/defer.d.ts +50 -0
- package/dist/hooks/defer.js +87 -0
- package/dist/hooks/defer.js.map +1 -0
- package/dist/hooks/post-tool-use/citation-scanner.d.ts +7 -5
- package/dist/hooks/post-tool-use/citation-scanner.js +15 -9
- package/dist/hooks/post-tool-use/citation-scanner.js.map +1 -1
- package/dist/hooks/post-tool-use/copy-scanner.js +1 -1
- package/dist/hooks/post-tool-use/copy-scanner.js.map +1 -1
- package/dist/hooks/post-tool-use/ledger-cache.d.ts +10 -2
- package/dist/hooks/post-tool-use/ledger-cache.js +58 -4
- package/dist/hooks/post-tool-use/ledger-cache.js.map +1 -1
- package/dist/hooks/post-tool-use/legend-builder.d.ts +3 -3
- package/dist/hooks/post-tool-use/legend-builder.js +30 -8
- package/dist/hooks/post-tool-use/legend-builder.js.map +1 -1
- package/dist/hooks/post-tool-use/read-enricher.d.ts +1 -1
- package/dist/hooks/post-tool-use/read-enricher.js +68 -21
- package/dist/hooks/post-tool-use/read-enricher.js.map +1 -1
- package/dist/hooks/post-tool-use/write-guardian.js +148 -18
- package/dist/hooks/post-tool-use/write-guardian.js.map +1 -1
- package/dist/hooks/runners/index.d.ts +3 -0
- package/dist/hooks/runners/index.js +2 -0
- package/dist/hooks/runners/index.js.map +1 -1
- package/dist/hooks/runners/payload.d.ts +1 -1
- package/dist/hooks/runners/payload.js +27 -12
- package/dist/hooks/runners/payload.js.map +1 -1
- package/dist/hooks/runners/session-start.js +250 -75
- package/dist/hooks/runners/session-start.js.map +1 -1
- package/dist/hooks/runners/stop.d.ts +9 -12
- package/dist/hooks/runners/stop.js +129 -41
- package/dist/hooks/runners/stop.js.map +1 -1
- package/dist/hooks/runners/user-prompt-submit.d.ts +15 -0
- package/dist/hooks/runners/user-prompt-submit.js +149 -0
- package/dist/hooks/runners/user-prompt-submit.js.map +1 -0
- package/dist/hooks/seed-attested.d.ts +25 -0
- package/dist/hooks/seed-attested.js +84 -0
- package/dist/hooks/seed-attested.js.map +1 -0
- package/dist/hooks/user-prompt-submit.d.ts +7 -0
- package/dist/hooks/user-prompt-submit.js +12 -0
- package/dist/hooks/user-prompt-submit.js.map +1 -0
- package/dist/index.d.ts +3 -6
- package/dist/index.js +14 -12
- package/dist/index.js.map +1 -1
- package/dist/init/baseline-audit.d.ts +5 -1
- package/dist/init/baseline-audit.js +25 -2
- package/dist/init/baseline-audit.js.map +1 -1
- package/dist/init/glob-inference.d.ts +16 -0
- package/dist/init/glob-inference.js +108 -0
- package/dist/init/glob-inference.js.map +1 -0
- package/dist/init/index.d.ts +4 -0
- package/dist/init/index.js +2 -0
- package/dist/init/index.js.map +1 -1
- package/dist/init/ingest-docs.d.ts +1 -1
- package/dist/init/ingest-docs.js +19 -6
- package/dist/init/ingest-docs.js.map +1 -1
- package/dist/init/init.js +17 -94
- package/dist/init/init.js.map +1 -1
- package/dist/init/mapper-merge.d.ts +8 -2
- package/dist/init/mapper-merge.js +50 -32
- package/dist/init/mapper-merge.js.map +1 -1
- package/dist/init/mapper-parallel.d.ts +2 -3
- package/dist/init/mapper-parallel.js +9 -38
- package/dist/init/mapper-parallel.js.map +1 -1
- package/dist/init/mapper-prompts.js +1 -1
- package/dist/init/mapper-prompts.js.map +1 -1
- package/dist/init/mapper.d.ts +7 -128
- package/dist/init/mapper.js +30 -7
- package/dist/init/mapper.js.map +1 -1
- package/dist/init/module-slicer.d.ts +1 -1
- package/dist/init/module-slicer.js +1 -1
- package/dist/init/multi-dev/install.d.ts +9 -10
- package/dist/init/multi-dev/install.js +15 -84
- package/dist/init/multi-dev/install.js.map +1 -1
- package/dist/init/overlay.d.ts +14 -0
- package/dist/init/overlay.js +65 -0
- package/dist/init/overlay.js.map +1 -0
- package/dist/init/phases/1-detect.d.ts +10 -0
- package/dist/init/phases/1-detect.js +36 -0
- package/dist/init/phases/1-detect.js.map +1 -0
- package/dist/init/phases/10-strip.d.ts +11 -0
- package/dist/init/phases/10-strip.js +79 -0
- package/dist/init/phases/10-strip.js.map +1 -0
- package/dist/init/phases/12-multidev.d.ts +6 -0
- package/dist/init/phases/12-multidev.js +34 -0
- package/dist/init/phases/12-multidev.js.map +1 -0
- package/dist/init/phases/2-walker.d.ts +8 -0
- package/dist/init/phases/2-walker.js +34 -0
- package/dist/init/phases/2-walker.js.map +1 -0
- package/dist/init/phases/3-mapper.d.ts +14 -0
- package/dist/init/phases/3-mapper.js +56 -0
- package/dist/init/phases/3-mapper.js.map +1 -0
- package/dist/init/phases/3b-seed.d.ts +18 -0
- package/dist/init/phases/3b-seed.js +140 -0
- package/dist/init/phases/3b-seed.js.map +1 -0
- package/dist/init/phases/4-pilot.d.ts +10 -0
- package/dist/init/phases/4-pilot.js +108 -0
- package/dist/init/phases/4-pilot.js.map +1 -0
- package/dist/init/phases/5-brand.d.ts +10 -0
- package/dist/init/phases/5-brand.js +66 -0
- package/dist/init/phases/5-brand.js.map +1 -0
- package/dist/init/phases/6-docs-ingest.d.ts +11 -0
- package/dist/init/phases/6-docs-ingest.js +39 -0
- package/dist/init/phases/6-docs-ingest.js.map +1 -0
- package/dist/init/phases/7b-source-comments.d.ts +6 -0
- package/dist/init/phases/7b-source-comments.js +34 -0
- package/dist/init/phases/7b-source-comments.js.map +1 -0
- package/dist/init/phases/7c-rules-merge.d.ts +6 -0
- package/dist/init/phases/7c-rules-merge.js +34 -0
- package/dist/init/phases/7c-rules-merge.js.map +1 -0
- package/dist/init/phases/8-baseline.d.ts +10 -0
- package/dist/init/phases/8-baseline.js +52 -0
- package/dist/init/phases/8-baseline.js.map +1 -0
- package/dist/init/phases/index.d.ts +15 -0
- package/dist/init/phases/index.js +16 -0
- package/dist/init/phases/index.js.map +1 -0
- package/dist/init/phases/orchestrator.d.ts +49 -0
- package/dist/init/phases/orchestrator.js +85 -0
- package/dist/init/phases/orchestrator.js.map +1 -0
- package/dist/init/phases/state-io.d.ts +19 -0
- package/dist/init/phases/state-io.js +73 -0
- package/dist/init/phases/state-io.js.map +1 -0
- package/dist/init/phases/types.d.ts +93 -0
- package/dist/init/phases/types.js +30 -0
- package/dist/init/phases/types.js.map +1 -0
- package/dist/init/prompts.d.ts +6 -8
- package/dist/init/prompts.js +3 -6
- package/dist/init/prompts.js.map +1 -1
- package/dist/init/rules-merge/ingest.js +1 -1
- package/dist/init/seed.js +11 -2
- package/dist/init/seed.js.map +1 -1
- package/dist/init/source-comments/classify.d.ts +1 -41
- package/dist/init/source-comments/classify.js +68 -37
- package/dist/init/source-comments/classify.js.map +1 -1
- package/dist/init/source-comments/index.d.ts +2 -2
- package/dist/init/source-comments/index.js +2 -2
- package/dist/init/source-comments/index.js.map +1 -1
- package/dist/init/source-comments/ingest.d.ts +38 -2
- package/dist/init/source-comments/ingest.js +237 -4
- package/dist/init/source-comments/ingest.js.map +1 -1
- package/dist/init/source-comments/strip-replace.d.ts +10 -19
- package/dist/init/source-comments/strip-replace.js +26 -2
- package/dist/init/source-comments/strip-replace.js.map +1 -1
- package/dist/init/source-comments/walker.d.ts +9 -2
- package/dist/init/source-comments/walker.js +38 -19
- package/dist/init/source-comments/walker.js.map +1 -1
- package/dist/init/submodules.d.ts +3 -2
- package/dist/init/types.d.ts +3 -5
- package/dist/init/types.js +3 -5
- package/dist/init/types.js.map +1 -1
- package/dist/init/visual.d.ts +4 -9
- package/dist/init/visual.js +1 -16
- package/dist/init/visual.js.map +1 -1
- package/dist/init/walker.d.ts +0 -6
- package/dist/init/walker.js +2 -3
- package/dist/init/walker.js.map +1 -1
- package/dist/join/index.js +49 -2
- package/dist/join/index.js.map +1 -1
- package/dist/logger.d.ts +1 -2
- package/dist/logger.js +5 -6
- package/dist/logger.js.map +1 -1
- package/dist/mcp/context.d.ts +2 -3
- package/dist/mcp/context.js.map +1 -1
- package/dist/mcp/errors.d.ts +1 -1
- package/dist/mcp/errors.js.map +1 -1
- package/dist/mcp/history/prompt.d.ts +2 -1
- package/dist/mcp/history/walker.js +1 -1
- package/dist/mcp/history/walker.js.map +1 -1
- package/dist/mcp/path-allowlist.d.ts +0 -6
- package/dist/mcp/path-allowlist.js +0 -10
- package/dist/mcp/path-allowlist.js.map +1 -1
- package/dist/mcp/schemas.d.ts +34 -72
- package/dist/mcp/schemas.js +40 -70
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/serve.d.ts +1 -1
- package/dist/mcp/serve.js +2 -5
- package/dist/mcp/serve.js.map +1 -1
- package/dist/mcp/server.js +10 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/telemetry.d.ts +8 -1
- package/dist/mcp/telemetry.js +16 -0
- package/dist/mcp/telemetry.js.map +1 -1
- package/dist/mcp/tools/decision-get.js +37 -29
- package/dist/mcp/tools/decision-get.js.map +1 -1
- package/dist/mcp/tools/decisions-in-scope.js +22 -3
- package/dist/mcp/tools/decisions-in-scope.js.map +1 -1
- package/dist/mcp/tools/ground-get.js +2 -2
- package/dist/mcp/tools/ground-get.js.map +1 -1
- package/dist/mcp/tools/index.js +6 -2
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/init-phases.d.ts +24 -0
- package/dist/mcp/tools/init-phases.js +140 -0
- package/dist/mcp/tools/init-phases.js.map +1 -0
- package/dist/mcp/tools/invariant-get.js +1 -1
- package/dist/mcp/tools/invariant-get.js.map +1 -1
- package/dist/mcp/tools/invariants-in-scope.js +26 -7
- package/dist/mcp/tools/invariants-in-scope.js.map +1 -1
- package/dist/mcp/tools/query-history.js +3 -3
- package/dist/mcp/tools/query-history.js.map +1 -1
- package/dist/mcp/tools/resolve-attention.d.ts +6 -4
- package/dist/mcp/tools/resolve-attention.js +253 -24
- package/dist/mcp/tools/resolve-attention.js.map +1 -1
- package/dist/mcp/tools/search.js +3 -4
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/task-create.d.ts +15 -0
- package/dist/mcp/tools/task-create.js +96 -0
- package/dist/mcp/tools/task-create.js.map +1 -0
- package/dist/mcp/tools/timeline.js +3 -7
- package/dist/mcp/tools/timeline.js.map +1 -1
- package/dist/profiles/types.d.ts +2 -2
- package/dist/profiles/types.js +1 -1
- package/dist/sensors/catalog.js +8 -3
- package/dist/sensors/catalog.js.map +1 -1
- package/dist/session/id.d.ts +1 -3
- package/dist/session/id.js +1 -1
- package/dist/session/id.js.map +1 -1
- package/dist/session-start/build.d.ts +7 -11
- package/dist/session-start/build.js +62 -34
- package/dist/session-start/build.js.map +1 -1
- package/dist/session-start/index.d.ts +2 -2
- package/dist/session-start/index.js +2 -2
- package/dist/session-start/index.js.map +1 -1
- package/dist/session-start/templates.d.ts +20 -4
- package/dist/session-start/templates.js +45 -35
- package/dist/session-start/templates.js.map +1 -1
- package/dist/status-line/format.d.ts +20 -9
- package/dist/status-line/format.js +45 -35
- package/dist/status-line/format.js.map +1 -1
- package/dist/status-line/index.d.ts +3 -4
- package/dist/status-line/index.js +1 -1
- package/dist/status-line/index.js.map +1 -1
- package/dist/status-line/reader.d.ts +9 -6
- package/dist/status-line/reader.js +50 -18
- package/dist/status-line/reader.js.map +1 -1
- package/dist/status-line/writer.d.ts +6 -9
- package/dist/status-line/writer.js +10 -12
- package/dist/status-line/writer.js.map +1 -1
- package/dist/trace/index.d.ts +44 -0
- package/dist/trace/index.js +51 -0
- package/dist/trace/index.js.map +1 -0
- package/package.json +2 -3
- package/templates/.archive/README.md +3 -3
- package/templates/.cairn/JOIN.md +3 -9
- package/templates/.cairn/config/sensors.yaml +16 -19
- package/templates/.cairn/config/stub-patterns.yaml +4 -4
- package/templates/.cairn/config/trust-policy.yaml +3 -35
- package/templates/.cairn/config/workflow.md +13 -194
- package/templates/.cairn/git-hooks/commit-msg +15 -4
- package/templates/.cairn/git-hooks/pre-commit +22 -13
- package/templates/.cairn/ground/canonical-map/topics.yaml +3 -26
- package/templates/.cairn/ground/capabilities/snippets.yaml +0 -1
- package/templates/.cairn/ground/manifest.yaml +5 -8
- package/templates/.cairn/ground/product/personas.yaml +0 -1
- package/dist/context/checkpoint.d.ts +0 -10
- package/dist/context/checkpoint.js +0 -29
- package/dist/context/checkpoint.js.map +0 -1
- package/dist/decision-capture/capture.d.ts +0 -57
- package/dist/decision-capture/capture.js +0 -186
- package/dist/decision-capture/capture.js.map +0 -1
- package/dist/decision-capture/extractor.d.ts +0 -20
- package/dist/decision-capture/extractor.js +0 -103
- package/dist/decision-capture/extractor.js.map +0 -1
- package/dist/decision-capture/prompt.d.ts +0 -15
- package/dist/decision-capture/prompt.js +0 -68
- package/dist/decision-capture/prompt.js.map +0 -1
- package/dist/decision-capture/refinement-prompt.d.ts +0 -25
- package/dist/decision-capture/refinement-prompt.js +0 -146
- package/dist/decision-capture/refinement-prompt.js.map +0 -1
- package/dist/decision-capture/refinement-schema.d.ts +0 -52
- package/dist/decision-capture/refinement-schema.js +0 -61
- package/dist/decision-capture/refinement-schema.js.map +0 -1
- package/dist/decision-capture/refinement.d.ts +0 -60
- package/dist/decision-capture/refinement.js +0 -439
- package/dist/decision-capture/refinement.js.map +0 -1
- package/dist/decision-capture/schema.d.ts +0 -70
- package/dist/decision-capture/schema.js +0 -71
- package/dist/decision-capture/schema.js.map +0 -1
- package/dist/decision-capture/types.d.ts +0 -201
- package/dist/decision-capture/types.js +0 -20
- package/dist/decision-capture/types.js.map +0 -1
- package/dist/decision-capture/writer.d.ts +0 -90
- package/dist/decision-capture/writer.js +0 -267
- package/dist/decision-capture/writer.js.map +0 -1
- package/dist/frontend-types.d.ts +0 -243
- package/dist/frontend-types.js +0 -15
- package/dist/frontend-types.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -12
- package/dist/hooks/index.js +0 -13
- package/dist/hooks/index.js.map +0 -1
- package/dist/inbox.d.ts +0 -17
- package/dist/inbox.js +0 -30
- package/dist/inbox.js.map +0 -1
- package/dist/mcp/tools/append-run-note.d.ts +0 -18
- package/dist/mcp/tools/append-run-note.js +0 -47
- package/dist/mcp/tools/append-run-note.js.map +0 -1
- package/dist/mcp/tools/append.d.ts +0 -8
- package/dist/mcp/tools/append.js +0 -37
- package/dist/mcp/tools/append.js.map +0 -1
- package/dist/mcp/tools/ask-operator.d.ts +0 -34
- package/dist/mcp/tools/ask-operator.js +0 -97
- package/dist/mcp/tools/ask-operator.js.map +0 -1
- package/dist/mcp/tools/drop-task.d.ts +0 -12
- package/dist/mcp/tools/drop-task.js +0 -68
- package/dist/mcp/tools/drop-task.js.map +0 -1
- package/dist/mcp/tools/record-run-event.d.ts +0 -10
- package/dist/mcp/tools/record-run-event.js +0 -32
- package/dist/mcp/tools/record-run-event.js.map +0 -1
- package/dist/prompt.d.ts +0 -19
- package/dist/prompt.js +0 -50
- package/dist/prompt.js.map +0 -1
- package/dist/tier0/classify.d.ts +0 -10
- package/dist/tier0/classify.js +0 -110
- package/dist/tier0/classify.js.map +0 -1
- package/dist/tier0/index.d.ts +0 -2
- package/dist/tier0/index.js +0 -2
- package/dist/tier0/index.js.map +0 -1
- package/dist/tier0/types.d.ts +0 -24
- package/dist/tier0/types.js +0 -9
- package/dist/tier0/types.js.map +0 -1
- package/dist/tightener/index.d.ts +0 -4
- package/dist/tightener/index.js +0 -4
- package/dist/tightener/index.js.map +0 -1
- package/dist/tightener/prompt.d.ts +0 -3
- package/dist/tightener/prompt.js +0 -67
- package/dist/tightener/prompt.js.map +0 -1
- package/dist/tightener/schema.d.ts +0 -68
- package/dist/tightener/schema.js +0 -44
- package/dist/tightener/schema.js.map +0 -1
- package/dist/tightener/tighten.d.ts +0 -2
- package/dist/tightener/tighten.js +0 -66
- package/dist/tightener/tighten.js.map +0 -1
- package/dist/tightener/types.d.ts +0 -74
- package/dist/tightener/types.js +0 -6
- package/dist/tightener/types.js.map +0 -1
- package/templates/.claude/settings.json +0 -57
- package/templates/.mcp.json +0 -8
- package/templates/README.md +0 -24
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `Stop` hook runner — fires when the assistant turn ends.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* •
|
|
6
|
-
*
|
|
7
|
-
* •
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* reviewer subagent on the next assistant turn.
|
|
11
|
-
* • Patch the per-session status.json `updated_at` (heartbeat).
|
|
4
|
+
* Per PLUGIN_ARCHITECTURE §10:
|
|
5
|
+
* • Drains `.cairn/events/` since the per-session marker.
|
|
6
|
+
* • Scans `.cairn/tasks/active/<id>/` for tasks missing attestation.yaml.
|
|
7
|
+
* • Compares HEAD's last 5 commits against `.cairn/.attested-commits`;
|
|
8
|
+
* surfaces bypass hint for `--no-verify` commits.
|
|
9
|
+
* • Patches per-session status.json `updated_at` (heartbeat).
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* surface backfill prompt for `--no-verify` bypasses.
|
|
11
|
+
* When context to surface: emits `{ decision: "block", reason: "..." }` so
|
|
12
|
+
* Claude reads the hint and invokes cairn-attention on the next turn.
|
|
13
|
+
* When nothing: emits `{ continue: true }` and Claude stops normally.
|
|
17
14
|
*/
|
|
18
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
15
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
19
16
|
import { join } from "node:path";
|
|
17
|
+
import { readActiveTaskSummary } from "../../context/index.js";
|
|
20
18
|
import { eventsSince, } from "../../events/index.js";
|
|
21
19
|
import { renderBypassHint, scanBypassedCommits, } from "../bypass-detection.js";
|
|
20
|
+
import { isDeferActive, readDeferState } from "../defer.js";
|
|
22
21
|
import { resolveRepoRoot } from "../../session-start/index.js";
|
|
23
22
|
import { readEventsMarker, stampEventsPoll, } from "../../session/index.js";
|
|
24
23
|
import { writeStatusJson } from "../../status-line/index.js";
|
|
25
24
|
import { emitShapeB, parseHookPayload, readHookStdin, recordHookTelemetry, } from "./payload.js";
|
|
25
|
+
/** Init in progress means `.cairn/init-state.json` exists at repoRoot. */
|
|
26
|
+
function isInitInProgress(repoRoot) {
|
|
27
|
+
return existsSync(join(repoRoot, ".cairn", "init-state.json"));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cap the reason text that flows back to Claude Code via decision:block.
|
|
31
|
+
* Both the reviewer hint and the bypass hint can fire on the same Stop
|
|
32
|
+
* tick; 4 KB is generous for two A/B/C strips but small enough that
|
|
33
|
+
* overflow is structurally impossible.
|
|
34
|
+
*/
|
|
35
|
+
const MAX_REASON_CHARS = 4_000;
|
|
36
|
+
function clampReason(body) {
|
|
37
|
+
if (body.length <= MAX_REASON_CHARS)
|
|
38
|
+
return body;
|
|
39
|
+
const head = body.slice(0, MAX_REASON_CHARS - 80);
|
|
40
|
+
return `${head}\n\n…(truncated; resolve via cairn-attention)`;
|
|
41
|
+
}
|
|
26
42
|
export async function runStopHook() {
|
|
27
43
|
const startedAt = Date.now();
|
|
28
44
|
const raw = await readHookStdin();
|
|
@@ -34,7 +50,7 @@ export async function runStopHook() {
|
|
|
34
50
|
let drained = [];
|
|
35
51
|
let pendingReviews = [];
|
|
36
52
|
let bypassed = [];
|
|
37
|
-
let
|
|
53
|
+
let reason = "";
|
|
38
54
|
if (repoRoot !== null && sessionId !== null && sessionId.length > 0) {
|
|
39
55
|
try {
|
|
40
56
|
const marker = readEventsMarker(repoRoot, sessionId);
|
|
@@ -49,42 +65,83 @@ export async function runStopHook() {
|
|
|
49
65
|
catch (err) {
|
|
50
66
|
warnings.push(`events_poll_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
67
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
const now = new Date();
|
|
69
|
+
// Suppress reviewer + bypass surfaces while init is mid-flight —
|
|
70
|
+
// `.cairn/.attested-commits` may not yet be seeded, and the
|
|
71
|
+
// adoption skill owns the operator's attention until phase 12
|
|
72
|
+
// returns nextPhase=null. The MCP init-phases tool clears the
|
|
73
|
+
// state file as soon as the final phase completes, after which
|
|
74
|
+
// the next Stop tick scans normally.
|
|
75
|
+
const initInProgress = isInitInProgress(repoRoot);
|
|
76
|
+
if (initInProgress) {
|
|
77
|
+
warnings.push("init_in_progress:scans_suppressed");
|
|
57
78
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
if (!initInProgress) {
|
|
80
|
+
try {
|
|
81
|
+
pendingReviews = scanPendingReviews(repoRoot);
|
|
82
|
+
if (pendingReviews.length > 0) {
|
|
83
|
+
const reviewDefer = readDeferState(repoRoot, "review");
|
|
84
|
+
const suppressed = reviewDefer !== null &&
|
|
85
|
+
isDeferActive(reviewDefer, now, {
|
|
86
|
+
kind: "task_ids",
|
|
87
|
+
values: pendingReviews.map((p) => p.task_id),
|
|
88
|
+
});
|
|
89
|
+
if (suppressed) {
|
|
90
|
+
warnings.push(`review_suppressed_until:${reviewDefer.deferred_at}`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
reason = renderReviewerHint(pendingReviews);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
warnings.push(`pending_review_scan_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const bypassResult = scanBypassedCommits(repoRoot);
|
|
102
|
+
bypassed = bypassResult.bypassed;
|
|
103
|
+
if (bypassed.length > 0) {
|
|
104
|
+
const bypassDefer = readDeferState(repoRoot, "bypass");
|
|
105
|
+
const suppressed = bypassDefer !== null &&
|
|
106
|
+
isDeferActive(bypassDefer, now, {
|
|
107
|
+
kind: "shas",
|
|
108
|
+
values: bypassed.map((b) => b.sha),
|
|
109
|
+
});
|
|
110
|
+
if (suppressed) {
|
|
111
|
+
warnings.push(`bypass_suppressed_until:${bypassDefer.deferred_at}`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const hint = renderBypassHint(bypassed);
|
|
115
|
+
reason = reason.length > 0 ? `${reason}\n\n${hint}` : hint;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
warnings.push(`bypass_scan_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
69
121
|
}
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
warnings.push(`bypass_scan_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
73
122
|
}
|
|
74
123
|
try {
|
|
124
|
+
const active = readActiveTaskSummary(repoRoot);
|
|
75
125
|
writeStatusJson(repoRoot, sessionId, {
|
|
76
126
|
updated_at: new Date().toISOString(),
|
|
127
|
+
task_state: active?.taskState ?? "idle",
|
|
128
|
+
task_id: active?.taskId ?? null,
|
|
129
|
+
task_module: active?.taskModule ?? null,
|
|
130
|
+
bypass_count: bypassed.length,
|
|
77
131
|
});
|
|
78
132
|
}
|
|
79
133
|
catch (err) {
|
|
80
134
|
warnings.push(`status_heartbeat_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
135
|
}
|
|
82
136
|
}
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
// Stop hook uses decision:block+reason to inject context into Claude
|
|
138
|
+
// (Stop does not support hookSpecificOutput.additionalContext — that
|
|
139
|
+
// field is silently ignored by Claude Code for Stop events). When
|
|
140
|
+
// reason is non-empty, blocking keeps the session alive so Claude
|
|
141
|
+
// reads the reason and acts on it (e.g. invokes cairn-attention).
|
|
142
|
+
const out = reason.length > 0
|
|
143
|
+
? { decision: "block", reason: clampReason(reason) }
|
|
144
|
+
: { continue: true };
|
|
88
145
|
emitShapeB(out);
|
|
89
146
|
recordHookTelemetry({
|
|
90
147
|
hook: "stop",
|
|
@@ -97,6 +154,8 @@ export async function runStopHook() {
|
|
|
97
154
|
events_drained: drained.length,
|
|
98
155
|
pending_reviews: pendingReviews.length,
|
|
99
156
|
bypassed_commits: bypassed.length,
|
|
157
|
+
decision: reason.length > 0 ? "block" : "continue",
|
|
158
|
+
...(reason.length > 0 ? { reason } : {}),
|
|
100
159
|
},
|
|
101
160
|
});
|
|
102
161
|
}
|
|
@@ -109,6 +168,30 @@ export async function runStopHook() {
|
|
|
109
168
|
* Older orphans are stale; the operator deals with them via attention
|
|
110
169
|
* rather than spawning reviewers blindly.
|
|
111
170
|
*/
|
|
171
|
+
// Phases a task moves through. The reviewer-attestation prompt only
|
|
172
|
+
// fires for tasks that are explicitly ready for review — a fresh
|
|
173
|
+
// `running` task has work in flight, no reviewer needed yet.
|
|
174
|
+
const REVIEW_READY_PHASES = new Set(["ready_for_review", "awaiting_attestation"]);
|
|
175
|
+
function readTaskPhase(taskDir) {
|
|
176
|
+
const statusPath = join(taskDir, "status.yaml");
|
|
177
|
+
if (!existsSync(statusPath))
|
|
178
|
+
return null;
|
|
179
|
+
let raw;
|
|
180
|
+
try {
|
|
181
|
+
raw = readFileSync(statusPath, "utf8");
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
// Cheap line scan — `phase: <value>` at top-level. Avoid a full
|
|
187
|
+
// YAML parse (this runs on every Stop tick).
|
|
188
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
189
|
+
const m = line.match(/^phase:\s*(\S+)/);
|
|
190
|
+
if (m && m[1] !== undefined)
|
|
191
|
+
return m[1].replace(/['"]/g, "");
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
112
195
|
function scanPendingReviews(repoRoot) {
|
|
113
196
|
const activeDir = join(repoRoot, ".cairn", "tasks", "active");
|
|
114
197
|
if (!existsSync(activeDir))
|
|
@@ -133,6 +216,10 @@ function scanPendingReviews(repoRoot) {
|
|
|
133
216
|
const attestation = join(taskDir, "attestation.yaml");
|
|
134
217
|
if (existsSync(attestation))
|
|
135
218
|
continue;
|
|
219
|
+
// Phase gate — `running` / `tightening` / etc. are not review-ready.
|
|
220
|
+
const phase = readTaskPhase(taskDir);
|
|
221
|
+
if (phase !== null && !REVIEW_READY_PHASES.has(phase))
|
|
222
|
+
continue;
|
|
136
223
|
let mtime = 0;
|
|
137
224
|
try {
|
|
138
225
|
mtime = statSync(tightenedSpec).mtimeMs;
|
|
@@ -151,13 +238,14 @@ function scanPendingReviews(repoRoot) {
|
|
|
151
238
|
}
|
|
152
239
|
function renderReviewerHint(pending) {
|
|
153
240
|
const lines = [];
|
|
154
|
-
|
|
241
|
+
const noun = pending.length === 1 ? "task" : "tasks";
|
|
242
|
+
lines.push(`## Cairn — ${pending.length} ${noun} awaiting review attestation`);
|
|
155
243
|
lines.push("");
|
|
156
244
|
for (const p of pending) {
|
|
157
|
-
lines.push(`-
|
|
245
|
+
lines.push(`- \`${p.task_id}\``);
|
|
158
246
|
}
|
|
159
247
|
lines.push("");
|
|
160
|
-
lines.push("
|
|
248
|
+
lines.push("Invoke the `cairn-attention` skill on the next turn so the operator can pick run review / skip / defer through `AskUserQuestion`.");
|
|
161
249
|
return lines.join("\n");
|
|
162
250
|
}
|
|
163
251
|
//# sourceMappingURL=stop.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../src/hooks/runners/stop.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../src/hooks/runners/stop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cairn hook user-prompt-submit` — UserPromptSubmit hook.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's `@<path>` shorthand attaches file content to the
|
|
5
|
+
* prompt directly, BYPASSING the Read tool — so the
|
|
6
|
+
* PostToolUse(Read) read-enricher hook never fires for those files.
|
|
7
|
+
* This hook fills the gap: it parses `@<path>` patterns from the
|
|
8
|
+
* raw prompt, reads each file, scans for §INV-/§DEC-/TODO(TSK-)
|
|
9
|
+
* citations, builds the same legend the read-enricher would have,
|
|
10
|
+
* and emits it as `additionalContext`. Result: the model sees
|
|
11
|
+
* the resolved citations alongside the attached file content,
|
|
12
|
+
* matching the documented "bare symbols always resolve on Read"
|
|
13
|
+
* contract regardless of attachment path.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runUserPromptSubmitHook(): Promise<void>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cairn hook user-prompt-submit` — UserPromptSubmit hook.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's `@<path>` shorthand attaches file content to the
|
|
5
|
+
* prompt directly, BYPASSING the Read tool — so the
|
|
6
|
+
* PostToolUse(Read) read-enricher hook never fires for those files.
|
|
7
|
+
* This hook fills the gap: it parses `@<path>` patterns from the
|
|
8
|
+
* raw prompt, reads each file, scans for §INV-/§DEC-/TODO(TSK-)
|
|
9
|
+
* citations, builds the same legend the read-enricher would have,
|
|
10
|
+
* and emits it as `additionalContext`. Result: the model sees
|
|
11
|
+
* the resolved citations alongside the attached file content,
|
|
12
|
+
* matching the documented "bare symbols always resolve on Read"
|
|
13
|
+
* contract regardless of attachment path.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
17
|
+
import { resolveRepoRoot } from "../../session-start/index.js";
|
|
18
|
+
import { scanCitations } from "../post-tool-use/citation-scanner.js";
|
|
19
|
+
import { getDecisionsLedger, getInvariantsLedger, getScopeIndexEntry, lookupTask, } from "../post-tool-use/ledger-cache.js";
|
|
20
|
+
import { buildLegend } from "../post-tool-use/legend-builder.js";
|
|
21
|
+
import { readHookStdin } from "./payload.js";
|
|
22
|
+
const MAX_FILE_BYTES = 512_000;
|
|
23
|
+
// Match `@<path>` only when `@` follows whitespace or is at start of
|
|
24
|
+
// prompt — filters out emails, twitter-style mentions, etc. Path
|
|
25
|
+
// chars: word, dot, slash, hyphen.
|
|
26
|
+
const AT_PATH_RE = /(?:^|\s)@([\w./-]+)/g;
|
|
27
|
+
function parsePayload(text) {
|
|
28
|
+
if (text.trim().length === 0)
|
|
29
|
+
return {};
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(text);
|
|
32
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function emitShapeB(additionalContext) {
|
|
39
|
+
const out = {
|
|
40
|
+
continue: true,
|
|
41
|
+
hookSpecificOutput: {
|
|
42
|
+
hookEventName: "UserPromptSubmit",
|
|
43
|
+
additionalContext,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
process.stdout.write(JSON.stringify(out));
|
|
47
|
+
process.stdout.write("\n");
|
|
48
|
+
}
|
|
49
|
+
function extractAttachedPaths(prompt) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const m of prompt.matchAll(AT_PATH_RE)) {
|
|
52
|
+
const p = m[1];
|
|
53
|
+
if (typeof p === "string" && p.length > 0)
|
|
54
|
+
out.push(p);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function safeRead(absPath) {
|
|
59
|
+
if (!existsSync(absPath))
|
|
60
|
+
return null;
|
|
61
|
+
try {
|
|
62
|
+
const buf = readFileSync(absPath, "utf8");
|
|
63
|
+
if (buf.length > MAX_FILE_BYTES)
|
|
64
|
+
return buf.slice(0, MAX_FILE_BYTES);
|
|
65
|
+
return buf;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export async function runUserPromptSubmitHook() {
|
|
72
|
+
try {
|
|
73
|
+
const raw = await readHookStdin();
|
|
74
|
+
const payload = parsePayload(raw);
|
|
75
|
+
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
76
|
+
const cwd = typeof payload.cwd === "string" ? payload.cwd : process.cwd();
|
|
77
|
+
// Fast-path: no `@` token in prompt → no work.
|
|
78
|
+
if (prompt.length === 0 || !prompt.includes("@")) {
|
|
79
|
+
emitShapeB("");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const repoRoot = resolveRepoRoot(cwd);
|
|
83
|
+
if (repoRoot === null) {
|
|
84
|
+
emitShapeB("");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const paths = extractAttachedPaths(prompt);
|
|
88
|
+
if (paths.length === 0) {
|
|
89
|
+
emitShapeB("");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Aggregate citations across all attached files. Filter to existing
|
|
93
|
+
// files — non-existent `@`-mentions are typically not file refs.
|
|
94
|
+
const aggregated = {
|
|
95
|
+
invariants: [],
|
|
96
|
+
todos: [],
|
|
97
|
+
decisions: [],
|
|
98
|
+
};
|
|
99
|
+
let firstHitRel = null;
|
|
100
|
+
for (const p of paths) {
|
|
101
|
+
const abs = isAbsolute(p) ? p : resolve(repoRoot, p);
|
|
102
|
+
const content = safeRead(abs);
|
|
103
|
+
if (content === null)
|
|
104
|
+
continue;
|
|
105
|
+
const matches = scanCitations(content);
|
|
106
|
+
const hit = matches.invariants.length > 0 ||
|
|
107
|
+
matches.todos.length > 0 ||
|
|
108
|
+
matches.decisions.length > 0;
|
|
109
|
+
if (!hit)
|
|
110
|
+
continue;
|
|
111
|
+
if (firstHitRel === null) {
|
|
112
|
+
const rel = relative(repoRoot, abs);
|
|
113
|
+
if (!rel.startsWith("..") && rel.length > 0) {
|
|
114
|
+
firstHitRel = rel.replace(/\\/g, "/");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
aggregated.invariants.push(...matches.invariants);
|
|
118
|
+
aggregated.todos.push(...matches.todos);
|
|
119
|
+
aggregated.decisions.push(...matches.decisions);
|
|
120
|
+
}
|
|
121
|
+
if (aggregated.invariants.length === 0 &&
|
|
122
|
+
aggregated.todos.length === 0 &&
|
|
123
|
+
aggregated.decisions.length === 0) {
|
|
124
|
+
emitShapeB("");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const cachedEntry = firstHitRel !== null ? getScopeIndexEntry(repoRoot, firstHitRel) : null;
|
|
128
|
+
const scopeHint = cachedEntry !== null
|
|
129
|
+
? {
|
|
130
|
+
decisions: cachedEntry.decisions,
|
|
131
|
+
invariants: cachedEntry.invariants,
|
|
132
|
+
}
|
|
133
|
+
: null;
|
|
134
|
+
const invariantsLedger = getInvariantsLedger(repoRoot);
|
|
135
|
+
const decisionsLedger = getDecisionsLedger(repoRoot);
|
|
136
|
+
const resolveTaskFn = (taskId) => lookupTask(repoRoot, taskId);
|
|
137
|
+
const legend = buildLegend(aggregated, invariantsLedger, decisionsLedger, scopeHint, resolveTaskFn);
|
|
138
|
+
emitShapeB(legend ?? "");
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
try {
|
|
142
|
+
emitShapeB("");
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// last resort
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=user-prompt-submit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../../src/hooks/runners/user-prompt-submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed `.cairn/.attested-commits` with every commit reachable from
|
|
3
|
+
* HEAD at the time of seeding. Bypass detection then grandfathers all
|
|
4
|
+
* those SHAs as "already attested" — only future bypassed commits
|
|
5
|
+
* surface as attention.
|
|
6
|
+
*
|
|
7
|
+
* Two callers:
|
|
8
|
+
* 1. Phase 3b-seed (during `cairn init`) — seeds for the adopting
|
|
9
|
+
* clone the moment `.cairn/` lands, so the very next Stop hook
|
|
10
|
+
* tick doesn't false-positive every pre-adoption commit.
|
|
11
|
+
* 2. `cairn join` (per-clone bootstrap) — `.attested-commits` is
|
|
12
|
+
* gitignored + per-clone, so each new clone needs its own seed
|
|
13
|
+
* on first bootstrap.
|
|
14
|
+
*
|
|
15
|
+
* Idempotent: if the file already exists with content, we leave it
|
|
16
|
+
* alone (the post-commit hook owns ongoing appends).
|
|
17
|
+
*/
|
|
18
|
+
export type SeedAttestedStatus = "ok" | "skipped" | "error";
|
|
19
|
+
export interface SeedAttestedResult {
|
|
20
|
+
status: SeedAttestedStatus;
|
|
21
|
+
detail: string;
|
|
22
|
+
/** SHA count seeded — present only when status="ok". */
|
|
23
|
+
count?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function seedAttestedCommits(repoRoot: string, dryRun?: boolean): SeedAttestedResult;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed `.cairn/.attested-commits` with every commit reachable from
|
|
3
|
+
* HEAD at the time of seeding. Bypass detection then grandfathers all
|
|
4
|
+
* those SHAs as "already attested" — only future bypassed commits
|
|
5
|
+
* surface as attention.
|
|
6
|
+
*
|
|
7
|
+
* Two callers:
|
|
8
|
+
* 1. Phase 3b-seed (during `cairn init`) — seeds for the adopting
|
|
9
|
+
* clone the moment `.cairn/` lands, so the very next Stop hook
|
|
10
|
+
* tick doesn't false-positive every pre-adoption commit.
|
|
11
|
+
* 2. `cairn join` (per-clone bootstrap) — `.attested-commits` is
|
|
12
|
+
* gitignored + per-clone, so each new clone needs its own seed
|
|
13
|
+
* on first bootstrap.
|
|
14
|
+
*
|
|
15
|
+
* Idempotent: if the file already exists with content, we leave it
|
|
16
|
+
* alone (the post-commit hook owns ongoing appends).
|
|
17
|
+
*/
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
export function seedAttestedCommits(repoRoot, dryRun = false) {
|
|
22
|
+
const path = join(repoRoot, ".cairn", ".attested-commits");
|
|
23
|
+
if (existsSync(path)) {
|
|
24
|
+
return {
|
|
25
|
+
status: "skipped",
|
|
26
|
+
detail: ".cairn/.attested-commits already exists — leaving as-is",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (!existsSync(join(repoRoot, ".git"))) {
|
|
30
|
+
return {
|
|
31
|
+
status: "skipped",
|
|
32
|
+
detail: "no .git/ — bypass detection is git-only, nothing to seed",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
let shas = [];
|
|
36
|
+
try {
|
|
37
|
+
const out = execFileSync("git", ["log", "--format=%H"], {
|
|
38
|
+
cwd: repoRoot,
|
|
39
|
+
encoding: "utf8",
|
|
40
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
41
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
42
|
+
});
|
|
43
|
+
shas = out.split("\n").filter((s) => s.length > 0);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// git log fails on empty repos (no commits yet) and on edge cases
|
|
47
|
+
// like detached/corrupted state. In all those cases the right
|
|
48
|
+
// semantic is "nothing to seed" — never block adoption / bootstrap
|
|
49
|
+
// on the absence of pre-existing history.
|
|
50
|
+
return {
|
|
51
|
+
status: "skipped",
|
|
52
|
+
detail: "git log returned no commits — nothing to seed",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (shas.length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
status: "skipped",
|
|
58
|
+
detail: "git log returned no commits — nothing to seed",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (dryRun) {
|
|
62
|
+
return {
|
|
63
|
+
status: "ok",
|
|
64
|
+
detail: `(dry-run) would seed ${shas.length} pre-existing SHA${shas.length === 1 ? "" : "s"}`,
|
|
65
|
+
count: shas.length,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
mkdirSync(join(repoRoot, ".cairn"), { recursive: true });
|
|
70
|
+
writeFileSync(path, `${shas.join("\n")}\n`, "utf8");
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
return {
|
|
74
|
+
status: "error",
|
|
75
|
+
detail: `write ${path} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
status: "ok",
|
|
80
|
+
detail: `seeded ${shas.length} pre-existing SHA${shas.length === 1 ? "" : "s"} — bypass detection grandfathers them`,
|
|
81
|
+
count: shas.length,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=seed-attested.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-attested.js","sourceRoot":"","sources":["../../src/hooks/seed-attested.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bin entrypoint — `node cairn-core/dist/hooks/user-prompt-submit.js`.
|
|
4
|
+
* UserPromptSubmit hook; resolves §INV/§DEC/TODO(TSK-) citations in
|
|
5
|
+
* `@`-attached files (Read-bypass path).
|
|
6
|
+
*/
|
|
7
|
+
import { runUserPromptSubmitHook } from "./runners/index.js";
|
|
8
|
+
runUserPromptSubmitHook().catch((err) => {
|
|
9
|
+
process.stderr.write(`[cairn user-prompt-submit] ${err instanceof Error ? err.message : String(err)}\n`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=user-prompt-submit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src/hooks/user-prompt-submit.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,uBAAuB,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA8B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACnF,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export * from "./decision-capture/index.js";
|
|
|
13
13
|
export * from "./doctor/index.js";
|
|
14
14
|
export * from "./gc/index.js";
|
|
15
15
|
export * from "./ground/index.js";
|
|
16
|
+
export { clearDeferState, deferStatePath, isDeferActive, readDeferState, writeDeferState, } from "./hooks/defer.js";
|
|
17
|
+
export type { DeferKind, DeferState } from "./hooks/defer.js";
|
|
16
18
|
export * from "./hooks/post-tool-use/index.js";
|
|
17
19
|
export * from "./init/index.js";
|
|
18
20
|
export * from "./join/index.js";
|
|
@@ -25,9 +27,4 @@ export * from "./hooks/runners/index.js";
|
|
|
25
27
|
export * from "./session/index.js";
|
|
26
28
|
export * from "./session-start/index.js";
|
|
27
29
|
export * from "./status-line/index.js";
|
|
28
|
-
export
|
|
29
|
-
export * from "./tightener/index.js";
|
|
30
|
-
export * from "./frontend-types.js";
|
|
31
|
-
export { writeInboxRow } from "./inbox.js";
|
|
32
|
-
export { loadWorkflowTemplate, renderTemplate } from "./prompt.js";
|
|
33
|
-
export type { TemplateContext } from "./prompt.js";
|
|
30
|
+
export { appendTrace, nowEvent, traceDir, traceFilePath, type TraceEvent, type TraceSource, } from "./trace/index.js";
|