@isaacriehm/cairn-core 0.2.0 → 0.3.1
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 +2 -3
- 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 +1 -1
- package/dist/doctor/index.js +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.js +1 -1
- 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 -2
- package/dist/ground/manifest.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 +59 -12
- package/dist/ground/scope-index.js +212 -43
- package/dist/ground/scope-index.js.map +1 -1
- package/dist/hooks/bypass-detection.js +4 -2
- package/dist/hooks/bypass-detection.js.map +1 -1
- 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 +8 -0
- package/dist/hooks/post-tool-use/ledger-cache.js +53 -0
- 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 +1 -0
- package/dist/hooks/runners/index.js +1 -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 +212 -66
- 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 +67 -36
- 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/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 +1 -6
- package/dist/index.js +1 -5
- 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 +2 -0
- package/dist/init/index.js +1 -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 +12 -7
- 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 +6 -127
- 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 +5 -11
- package/dist/init/multi-dev/install.js +4 -6
- package/dist/init/multi-dev/install.js.map +1 -1
- package/dist/init/overlay.d.ts +2 -2
- package/dist/init/overlay.js +1 -1
- package/dist/init/overlay.js.map +1 -1
- package/dist/init/phases/10-strip.js +1 -2
- package/dist/init/phases/10-strip.js.map +1 -1
- package/dist/init/phases/12-multidev.d.ts +1 -6
- package/dist/init/phases/12-multidev.js +1 -6
- package/dist/init/phases/12-multidev.js.map +1 -1
- package/dist/init/phases/3b-seed.d.ts +1 -10
- package/dist/init/phases/3b-seed.js +1 -10
- package/dist/init/phases/3b-seed.js.map +1 -1
- package/dist/init/phases/4-pilot.js +53 -15
- package/dist/init/phases/4-pilot.js.map +1 -1
- package/dist/init/phases/5-brand.js +10 -10
- package/dist/init/phases/5-brand.js.map +1 -1
- 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/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/logger.d.ts +0 -1
- package/dist/logger.js +0 -1
- 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 +15 -72
- package/dist/mcp/schemas.js +21 -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/index.js +2 -2
- package/dist/mcp/tools/index.js.map +1 -1
- 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 +3 -3
- package/dist/mcp/tools/resolve-attention.js +40 -28
- 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 +1 -1
- package/dist/profiles/types.js +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 +12 -14
- package/dist/session-start/build.js +68 -38
- 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 +19 -11
- package/dist/status-line/format.js +44 -36
- package/dist/status-line/format.js.map +1 -1
- package/dist/status-line/index.d.ts +3 -1
- 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 -15
- package/dist/status-line/reader.js.map +1 -1
- package/dist/status-line/writer.d.ts +5 -0
- package/dist/status-line/writer.js +9 -0
- 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/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 +1 -1
- 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/templates/.github/workflows/cairn-check.yml +3 -5
- package/dist/context/checkpoint.d.ts +0 -11
- package/dist/context/checkpoint.js +0 -30
- 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 -14
- package/dist/hooks/index.js +0 -14
- 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 -54
- 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
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import type { StatusJson } from "./index.js";
|
|
2
2
|
/**
|
|
3
|
-
* Render a
|
|
4
|
-
* hook displays. Pure function; no I/O.
|
|
3
|
+
* Render a single-line status string for Claude Code's status_line hook.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
5
|
+
* Layout: `⬡ cairn [signal] [ctx-meter]`
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Signal priority (first match wins, blank when nothing applies):
|
|
8
|
+
* bypass_count > 0 → `⚠ N unattested`
|
|
9
|
+
* attention_count > 0 → `⚑ N draft[s]`
|
|
10
|
+
* gc_running → `◐ gc`
|
|
11
|
+
* task_state != idle → `${task_id} ${task_module}` (or fallbacks)
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Ctx meter is omitted when no payload is supplied. Color thresholds are
|
|
14
|
+
* keyed on absolute used tokens (not percentage) so a 1M-window Opus
|
|
15
|
+
* session and a 200k-window Sonnet session signal danger at comparable
|
|
16
|
+
* absolute exhaustion points.
|
|
16
17
|
*/
|
|
17
|
-
export
|
|
18
|
+
export interface CtxMeterInput {
|
|
19
|
+
/** raw used % matches Claude Code's /context display (no buffer normalization). */
|
|
20
|
+
usedPct: number;
|
|
21
|
+
/** absolute used tokens — keys the color threshold. */
|
|
22
|
+
usedTokens: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function renderCtxMeter(ctx: CtxMeterInput): string;
|
|
25
|
+
export declare function formatStatus(s: StatusJson, ctx?: CtxMeterInput): string;
|
|
@@ -1,42 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
const ANSI_RESET = "\x1b[0m";
|
|
2
|
+
const ANSI_GREEN = "\x1b[32m";
|
|
3
|
+
const ANSI_YELLOW = "\x1b[33m";
|
|
4
|
+
const ANSI_ORANGE = "\x1b[38;5;208m";
|
|
5
|
+
const ANSI_RED = "\x1b[31m";
|
|
6
|
+
function ctxColor(usedTokens) {
|
|
7
|
+
if (usedTokens < 100_000)
|
|
8
|
+
return ANSI_GREEN;
|
|
9
|
+
if (usedTokens < 300_000)
|
|
10
|
+
return ANSI_YELLOW;
|
|
11
|
+
if (usedTokens < 600_000)
|
|
12
|
+
return ANSI_ORANGE;
|
|
13
|
+
return ANSI_RED;
|
|
14
|
+
}
|
|
15
|
+
export function renderCtxMeter(ctx) {
|
|
16
|
+
const pct = Math.max(0, Math.min(100, Math.round(ctx.usedPct)));
|
|
17
|
+
const filled = Math.floor(pct / 10);
|
|
18
|
+
const bar = "█".repeat(filled) + "░".repeat(10 - filled);
|
|
19
|
+
return `${ctxColor(ctx.usedTokens)}${bar} ${pct}%${ANSI_RESET}`;
|
|
20
|
+
}
|
|
21
|
+
function renderSignal(s) {
|
|
22
|
+
if (s.bypass_count > 0)
|
|
23
|
+
return `⚠ ${s.bypass_count} unattested`;
|
|
22
24
|
if (s.attention_count > 0) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
else if (s.gc_running) {
|
|
27
|
-
stateField = "gc:active";
|
|
28
|
-
icon = "◐";
|
|
25
|
+
const noun = s.attention_count === 1 ? "draft" : "drafts";
|
|
26
|
+
return `⚑ ${s.attention_count} ${noun}`;
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
if (s.gc_running)
|
|
29
|
+
return "◐ gc";
|
|
30
|
+
if (s.task_state !== "idle") {
|
|
31
|
+
if (s.task_id && s.task_module)
|
|
32
|
+
return `${s.task_id} ${s.task_module}`;
|
|
33
|
+
if (s.task_id)
|
|
34
|
+
return s.task_id;
|
|
35
|
+
if (s.task_module)
|
|
36
|
+
return s.task_module;
|
|
37
|
+
return `task: ${s.task_state}`;
|
|
33
38
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
export function formatStatus(s, ctx) {
|
|
42
|
+
const parts = ["⬡ cairn"];
|
|
43
|
+
const signal = renderSignal(s);
|
|
44
|
+
if (signal)
|
|
45
|
+
parts.push(signal);
|
|
46
|
+
if (ctx)
|
|
47
|
+
parts.push(renderCtxMeter(ctx));
|
|
40
48
|
return parts.join(" ");
|
|
41
49
|
}
|
|
42
50
|
//# sourceMappingURL=format.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/status-line/format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/status-line/format.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,MAAM,WAAW,GAAG,UAAU,CAAC;AAC/B,MAAM,WAAW,GAAG,gBAAgB,CAAC;AACrC,MAAM,QAAQ,GAAG,UAAU,CAAC;AAE5B,SAAS,QAAQ,CAAC,UAAkB;IAClC,IAAI,UAAU,GAAG,OAAO;QAAE,OAAO,UAAU,CAAC;IAC5C,IAAI,UAAU,GAAG,OAAO;QAAE,OAAO,WAAW,CAAC;IAC7C,IAAI,UAAU,GAAG,OAAO;QAAE,OAAO,WAAW,CAAC;IAC7C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAkB;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;IACzD,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,CAAa;IACjC,IAAI,CAAC,CAAC,YAAY,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,YAAY,aAAa,CAAC;IAChE,IAAI,CAAC,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,CAAC,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,OAAO,KAAK,CAAC,CAAC,eAAe,IAAI,IAAI,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,CAAC,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,CAAC,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW;YAAE,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACvE,IAAI,CAAC,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC,WAAW,CAAC;QACxC,OAAO,SAAS,CAAC,CAAC,UAAU,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAa,EAAE,GAAmB;IAC7D,MAAM,KAAK,GAAa,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -15,12 +15,14 @@ export interface StatusJson {
|
|
|
15
15
|
decisions_in_scope: number;
|
|
16
16
|
invariants_in_scope: number;
|
|
17
17
|
task_state: TaskState;
|
|
18
|
+
task_id: string | null;
|
|
18
19
|
task_module: string | null;
|
|
19
20
|
gc_running: boolean;
|
|
20
21
|
attention_count: number;
|
|
22
|
+
bypass_count: number;
|
|
21
23
|
last_run_result: "succeeded" | "failed" | null;
|
|
22
24
|
last_run_at: string | null;
|
|
23
25
|
}
|
|
24
26
|
export { defaultStatusJson, statusJsonPath, writeStatusJson, } from "./writer.js";
|
|
25
27
|
export { readStatusForCLI } from "./reader.js";
|
|
26
|
-
export { formatStatus } from "./format.js";
|
|
28
|
+
export { formatStatus, renderCtxMeter, type CtxMeterInput } from "./format.js";
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { defaultStatusJson, statusJsonPath, writeStatusJson, } from "./writer.js";
|
|
12
12
|
export { readStatusForCLI } from "./reader.js";
|
|
13
|
-
export { formatStatus } from "./format.js";
|
|
13
|
+
export { formatStatus, renderCtxMeter } from "./format.js";
|
|
14
14
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/status-line/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/status-line/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAsB,MAAM,aAAa,CAAC"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { type CtxMeterInput } from "./format.js";
|
|
1
2
|
/**
|
|
2
3
|
* Render the current status-line string for a session inside the
|
|
3
4
|
* adopted repo at `repoRoot`. `sessionId` is the Claude Code session id
|
|
4
|
-
* (passed via the status-line hook's stdin payload).
|
|
5
|
+
* (passed via the status-line hook's stdin payload). `ctx` is the
|
|
6
|
+
* decoded `context_window` block from the same payload.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
* - `sessionId` is null/empty
|
|
8
|
-
* - the per-session status.json is missing
|
|
9
|
-
*
|
|
8
|
+
* Falls back to ground-state summary when:
|
|
9
|
+
* - `sessionId` is null/empty
|
|
10
|
+
* - the per-session status.json is missing, unreadable, or malformed
|
|
11
|
+
*
|
|
12
|
+
* Returns empty string when `.cairn/` doesn't exist (cairn not adopted).
|
|
10
13
|
*
|
|
11
14
|
* Hot path — invoked on every Claude Code prompt. Keep this cheap.
|
|
12
15
|
*/
|
|
13
|
-
export declare function readStatusForCLI(repoRoot: string, sessionId: string | null): string;
|
|
16
|
+
export declare function readStatusForCLI(repoRoot: string, sessionId: string | null, ctx?: CtxMeterInput): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { formatStatus, renderCtxMeter } from "./format.js";
|
|
3
4
|
import { statusJsonPath } from "./writer.js";
|
|
4
|
-
const PLACEHOLDER = "⬡ cairn no session ○";
|
|
5
5
|
const TASK_STATES = [
|
|
6
6
|
"idle",
|
|
7
7
|
"running",
|
|
@@ -28,46 +28,81 @@ function isStatusJson(x) {
|
|
|
28
28
|
typeof o["decisions_in_scope"] === "number" &&
|
|
29
29
|
typeof o["invariants_in_scope"] === "number" &&
|
|
30
30
|
isTaskState(o["task_state"]) &&
|
|
31
|
+
isStringOrNull(o["task_id"]) &&
|
|
31
32
|
isStringOrNull(o["task_module"]) &&
|
|
32
33
|
typeof o["gc_running"] === "boolean" &&
|
|
33
34
|
typeof o["attention_count"] === "number" &&
|
|
35
|
+
typeof o["bypass_count"] === "number" &&
|
|
34
36
|
isLastRunResult(o["last_run_result"]) &&
|
|
35
37
|
isStringOrNull(o["last_run_at"]));
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Ground-state fallback when no per-session status.json is available.
|
|
41
|
+
* Counts pending drafts from `_inbox/`; renders `⬡ cairn ⚑ N drafts` or
|
|
42
|
+
* just `⬡ cairn`. Returns empty string when `.cairn/` is absent.
|
|
43
|
+
*
|
|
44
|
+
* Ctx meter is appended when supplied — operator-side dropdown stays
|
|
45
|
+
* informative even when the session hook hasn't written status yet.
|
|
46
|
+
*/
|
|
47
|
+
function groundStateFallback(repoRoot, ctx) {
|
|
48
|
+
const cairnDir = join(repoRoot, ".cairn");
|
|
49
|
+
if (!existsSync(cairnDir))
|
|
50
|
+
return "";
|
|
51
|
+
let drafts = 0;
|
|
52
|
+
const inboxDir = join(cairnDir, "ground", "decisions", "_inbox");
|
|
53
|
+
if (existsSync(inboxDir)) {
|
|
54
|
+
try {
|
|
55
|
+
drafts = readdirSync(inboxDir, { encoding: "utf8" }).filter((f) => f.endsWith(".draft.md")).length;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
drafts = 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const parts = ["⬡ cairn"];
|
|
62
|
+
if (drafts > 0) {
|
|
63
|
+
const noun = drafts === 1 ? "draft" : "drafts";
|
|
64
|
+
parts.push(`⚑ ${drafts} ${noun}`);
|
|
65
|
+
}
|
|
66
|
+
if (ctx)
|
|
67
|
+
parts.push(renderCtxMeter(ctx));
|
|
68
|
+
return parts.join(" ");
|
|
69
|
+
}
|
|
37
70
|
/**
|
|
38
71
|
* Render the current status-line string for a session inside the
|
|
39
72
|
* adopted repo at `repoRoot`. `sessionId` is the Claude Code session id
|
|
40
|
-
* (passed via the status-line hook's stdin payload).
|
|
73
|
+
* (passed via the status-line hook's stdin payload). `ctx` is the
|
|
74
|
+
* decoded `context_window` block from the same payload.
|
|
75
|
+
*
|
|
76
|
+
* Falls back to ground-state summary when:
|
|
77
|
+
* - `sessionId` is null/empty
|
|
78
|
+
* - the per-session status.json is missing, unreadable, or malformed
|
|
41
79
|
*
|
|
42
|
-
* Returns
|
|
43
|
-
* - `sessionId` is null/empty (status-line invoked outside a session)
|
|
44
|
-
* - the per-session status.json is missing
|
|
45
|
-
* - the file is unreadable, malformed JSON, or fails shape validation
|
|
80
|
+
* Returns empty string when `.cairn/` doesn't exist (cairn not adopted).
|
|
46
81
|
*
|
|
47
82
|
* Hot path — invoked on every Claude Code prompt. Keep this cheap.
|
|
48
83
|
*/
|
|
49
|
-
export function readStatusForCLI(repoRoot, sessionId) {
|
|
84
|
+
export function readStatusForCLI(repoRoot, sessionId, ctx) {
|
|
50
85
|
if (sessionId === null || sessionId.length === 0)
|
|
51
|
-
return
|
|
86
|
+
return groundStateFallback(repoRoot, ctx);
|
|
52
87
|
const filePath = statusJsonPath(repoRoot, sessionId);
|
|
53
88
|
if (!existsSync(filePath))
|
|
54
|
-
return
|
|
89
|
+
return groundStateFallback(repoRoot, ctx);
|
|
55
90
|
let raw;
|
|
56
91
|
try {
|
|
57
92
|
raw = readFileSync(filePath, "utf8");
|
|
58
93
|
}
|
|
59
94
|
catch {
|
|
60
|
-
return
|
|
95
|
+
return groundStateFallback(repoRoot, ctx);
|
|
61
96
|
}
|
|
62
97
|
let parsed;
|
|
63
98
|
try {
|
|
64
99
|
parsed = JSON.parse(raw);
|
|
65
100
|
}
|
|
66
101
|
catch {
|
|
67
|
-
return
|
|
102
|
+
return groundStateFallback(repoRoot, ctx);
|
|
68
103
|
}
|
|
69
104
|
if (!isStatusJson(parsed))
|
|
70
|
-
return
|
|
71
|
-
return formatStatus(parsed);
|
|
105
|
+
return groundStateFallback(repoRoot, ctx);
|
|
106
|
+
return formatStatus(parsed, ctx);
|
|
72
107
|
}
|
|
73
108
|
//# sourceMappingURL=reader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/status-line/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/status-line/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAsB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,GAAyB;IACxC,MAAM;IACN,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,SAAS;IACT,WAAW;IACX,UAAU;CACX,CAAC;AAEF,SAAS,WAAW,CAAC,CAAU;IAC7B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,WAAiC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,cAAc,CAAC,CAAU;IAChC,OAAO,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC;AAC7C,CAAC;AAED,SAAS,eAAe,CAAC,CAAU;IACjC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,QAAQ,CAAC;AAC3D,CAAC;AAED,SAAS,YAAY,CAAC,CAAU;IAC9B,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ;QACnC,OAAO,CAAC,CAAC,oBAAoB,CAAC,KAAK,QAAQ;QAC3C,OAAO,CAAC,CAAC,qBAAqB,CAAC,KAAK,QAAQ;QAC5C,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC5B,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC5B,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,SAAS;QACpC,OAAO,CAAC,CAAC,iBAAiB,CAAC,KAAK,QAAQ;QACxC,OAAO,CAAC,CAAC,cAAc,CAAC,KAAK,QAAQ;QACrC,eAAe,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACrC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CACjC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,QAAgB,EAAE,GAAmB;IAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACjE,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAChE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CACxB,CAAC,MAAM,CAAC;QACX,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAa,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAgB,EAChB,SAAwB,EACxB,GAAmB;IAEnB,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5F,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAErE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAErE,OAAO,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -12,6 +12,11 @@ export declare function statusJsonPath(repoRoot: string, sessionId: string): str
|
|
|
12
12
|
* the pretty-printed result back. Creates the per-session directory if
|
|
13
13
|
* missing.
|
|
14
14
|
*
|
|
15
|
+
* Refuses to write when the repo's `.cairn/` directory is missing — a
|
|
16
|
+
* caller that forgot to gate on `resolveRepoRoot` would otherwise
|
|
17
|
+
* `mkdir -p` a phantom `.cairn/sessions/` tree in a non-adopted
|
|
18
|
+
* project.
|
|
19
|
+
*
|
|
15
20
|
* Best-effort write; no atomic-rename ceremony. The status file is
|
|
16
21
|
* cosmetic — torn writes self-heal on the next hook tick.
|
|
17
22
|
*/
|
|
@@ -16,10 +16,17 @@ export function statusJsonPath(repoRoot, sessionId) {
|
|
|
16
16
|
* the pretty-printed result back. Creates the per-session directory if
|
|
17
17
|
* missing.
|
|
18
18
|
*
|
|
19
|
+
* Refuses to write when the repo's `.cairn/` directory is missing — a
|
|
20
|
+
* caller that forgot to gate on `resolveRepoRoot` would otherwise
|
|
21
|
+
* `mkdir -p` a phantom `.cairn/sessions/` tree in a non-adopted
|
|
22
|
+
* project.
|
|
23
|
+
*
|
|
19
24
|
* Best-effort write; no atomic-rename ceremony. The status file is
|
|
20
25
|
* cosmetic — torn writes self-heal on the next hook tick.
|
|
21
26
|
*/
|
|
22
27
|
export function writeStatusJson(repoRoot, sessionId, patch) {
|
|
28
|
+
if (!existsSync(join(repoRoot, ".cairn")))
|
|
29
|
+
return;
|
|
23
30
|
const stateDir = sessionStateDir(repoRoot, sessionId);
|
|
24
31
|
const filePath = join(stateDir, "status.json");
|
|
25
32
|
let existing = {};
|
|
@@ -51,9 +58,11 @@ export function defaultStatusJson() {
|
|
|
51
58
|
decisions_in_scope: 0,
|
|
52
59
|
invariants_in_scope: 0,
|
|
53
60
|
task_state: "idle",
|
|
61
|
+
task_id: null,
|
|
54
62
|
task_module: null,
|
|
55
63
|
gc_running: false,
|
|
56
64
|
attention_count: 0,
|
|
65
|
+
bypass_count: 0,
|
|
57
66
|
last_run_result: null,
|
|
58
67
|
last_run_at: null,
|
|
59
68
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/status-line/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,SAAiB;IAChE,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;AACnE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/status-line/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,SAAiB;IAChE,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,SAAiB,EACjB,KAA0B;IAE1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAAE,OAAO;IAElD,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAE/C,IAAI,QAAQ,GAAwB,EAAE,CAAC;IACvC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;YAC1C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAClD,QAAQ,GAAG,MAA6B,CAAC;YAC3C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAwB,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;IAE9D,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,kBAAkB,EAAE,CAAC;QACrB,mBAAmB,EAAE,CAAC;QACtB,UAAU,EAAE,MAAM;QAClB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI;QACjB,UAAU,EAAE,KAAK;QACjB,eAAe,EAAE,CAAC;QAClB,YAAY,EAAE,CAAC;QACf,eAAe,EAAE,IAAI;QACrB,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified trace log for live-session debugging.
|
|
3
|
+
*
|
|
4
|
+
* Every cairn surface (Claude Code hooks, MCP tools, `claude --print`
|
|
5
|
+
* subprocess calls, init phases) appends a single jsonl row per event
|
|
6
|
+
* to `~/.local/cairn/trace/trace-<YYYY-MM-DD>.jsonl`. The `cairn trace`
|
|
7
|
+
* CLI subcommand reads them back time-sorted across the most recent
|
|
8
|
+
* day(s) so the operator can post-mortem an entire live session in one
|
|
9
|
+
* pane.
|
|
10
|
+
*
|
|
11
|
+
* Best-effort: trace failures NEVER throw — debug logging that breaks
|
|
12
|
+
* the surface it's monitoring is worse than no logging.
|
|
13
|
+
*/
|
|
14
|
+
export type TraceSource = "hook" | "mcp" | "claude" | "init-phase" | "subagent";
|
|
15
|
+
export interface TraceEvent {
|
|
16
|
+
/** ISO 8601 with millisecond precision. */
|
|
17
|
+
ts: string;
|
|
18
|
+
/** Top-level surface emitting the event. */
|
|
19
|
+
source: TraceSource;
|
|
20
|
+
/** Event-specific kind ("session-start", "tool-call", "claude-request", …). */
|
|
21
|
+
kind: string;
|
|
22
|
+
/** Adopted-repo root, when known. */
|
|
23
|
+
repo_root: string | null;
|
|
24
|
+
/** Claude Code session id, when known. */
|
|
25
|
+
session_id: string | null;
|
|
26
|
+
/** Wall time spent, when applicable. */
|
|
27
|
+
duration_ms?: number;
|
|
28
|
+
/** Success / failure outcome, when applicable. */
|
|
29
|
+
ok?: boolean;
|
|
30
|
+
/** Free-form payload — keep small; large bodies should be stored separately. */
|
|
31
|
+
payload: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
export declare function traceDir(): string;
|
|
34
|
+
export declare function traceFilePath(now?: Date): string;
|
|
35
|
+
/**
|
|
36
|
+
* Append one trace row. Best-effort — swallows any IO error so callers
|
|
37
|
+
* never break their primary surface on trace-write failure.
|
|
38
|
+
*/
|
|
39
|
+
export declare function appendTrace(event: TraceEvent): void;
|
|
40
|
+
/**
|
|
41
|
+
* Build a TraceEvent with `ts` set to now. Convenience for callsites
|
|
42
|
+
* that build the rest of the row inline.
|
|
43
|
+
*/
|
|
44
|
+
export declare function nowEvent(args: Omit<TraceEvent, "ts">): TraceEvent;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified trace log for live-session debugging.
|
|
3
|
+
*
|
|
4
|
+
* Every cairn surface (Claude Code hooks, MCP tools, `claude --print`
|
|
5
|
+
* subprocess calls, init phases) appends a single jsonl row per event
|
|
6
|
+
* to `~/.local/cairn/trace/trace-<YYYY-MM-DD>.jsonl`. The `cairn trace`
|
|
7
|
+
* CLI subcommand reads them back time-sorted across the most recent
|
|
8
|
+
* day(s) so the operator can post-mortem an entire live session in one
|
|
9
|
+
* pane.
|
|
10
|
+
*
|
|
11
|
+
* Best-effort: trace failures NEVER throw — debug logging that breaks
|
|
12
|
+
* the surface it's monitoring is worse than no logging.
|
|
13
|
+
*/
|
|
14
|
+
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
export function traceDir() {
|
|
18
|
+
return join(homedir(), ".local", "cairn", "trace");
|
|
19
|
+
}
|
|
20
|
+
function todayFilename(now) {
|
|
21
|
+
const y = now.getUTCFullYear();
|
|
22
|
+
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
23
|
+
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
24
|
+
return `trace-${y}-${m}-${d}.jsonl`;
|
|
25
|
+
}
|
|
26
|
+
export function traceFilePath(now = new Date()) {
|
|
27
|
+
return join(traceDir(), todayFilename(now));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Append one trace row. Best-effort — swallows any IO error so callers
|
|
31
|
+
* never break their primary surface on trace-write failure.
|
|
32
|
+
*/
|
|
33
|
+
export function appendTrace(event) {
|
|
34
|
+
try {
|
|
35
|
+
const dir = traceDir();
|
|
36
|
+
if (!existsSync(dir))
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
appendFileSync(traceFilePath(), `${JSON.stringify(event)}\n`, "utf8");
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// best-effort
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build a TraceEvent with `ts` set to now. Convenience for callsites
|
|
46
|
+
* that build the rest of the row inline.
|
|
47
|
+
*/
|
|
48
|
+
export function nowEvent(args) {
|
|
49
|
+
return { ts: new Date().toISOString(), ...args };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trace/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAuBjC,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,GAAS;IAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;IAC/B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAY,IAAI,IAAI,EAAE;IAClD,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,cAAc,CAAC,aAAa,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAA4B;IACnD,OAAO,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC;AACnD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isaacriehm/cairn-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Cairn core — state + context layer. Curated `.cairn/ground/` (decisions, §
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Cairn core — state + context layer. Curated `.cairn/ground/` (decisions, §INV invariants, canonical-map, brand, quality-grades), MCP server, init wizard, hook runners, sensors, GC drift sweep.",
|
|
5
5
|
"author": "Isaac Riehm",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/isaacriehm/cairn",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/cli-progress": "^3.11.6",
|
|
45
45
|
"@types/node": "^25.6.0",
|
|
46
|
-
"tsx": "^4.21.0",
|
|
47
46
|
"typescript": "^6.0.3"
|
|
48
47
|
},
|
|
49
48
|
"scripts": {
|
|
@@ -21,7 +21,7 @@ This directory holds files that were once canonical but are no longer current. *
|
|
|
21
21
|
|
|
22
22
|
## What does NOT live here
|
|
23
23
|
|
|
24
|
-
- Files marked `[STALE]` in canonical paths — Cairn rejects this pattern. Stale files are MOVED here, never banner-flagged in place.
|
|
24
|
+
- Files marked `[STALE]` in canonical paths — Cairn rejects this pattern. Stale files are MOVED here, never banner-flagged in place.
|
|
25
25
|
- Branches or tagged refs — `.archive/` is filesystem-only.
|
|
26
26
|
- Secrets — `.env*` patterns stay in `.gitignore` regardless.
|
|
27
27
|
|
|
@@ -54,7 +54,7 @@ PreToolUse-style interception is **not** used (operator decision 2026-05-04). Th
|
|
|
54
54
|
|
|
55
55
|
## Writing to `.archive/`
|
|
56
56
|
|
|
57
|
-
Only via `cairn_archive(path, reason)`. Direct moves are accepted but discouraged because they bypass the audit log. The MCP tool records the reason
|
|
57
|
+
Only via `cairn_archive(path, reason)`. Direct moves are accepted but discouraged because they bypass the audit log. The MCP tool records the reason and timestamp.
|
|
58
58
|
|
|
59
59
|
## Why we don't delete
|
|
60
60
|
|
|
@@ -64,4 +64,4 @@ Only via `cairn_archive(path, reason)`. Direct moves are accepted but discourage
|
|
|
64
64
|
|
|
65
65
|
## Restoring an archived file
|
|
66
66
|
|
|
67
|
-
`cairn restore <path>` (CLI subcommand, future) — moves a file out of `.archive/<bucket>/` back to its original location and
|
|
67
|
+
`cairn restore <path>` (CLI subcommand, future) — moves a file out of `.archive/<bucket>/` back to its original location and logs the restoration. Use sparingly.
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# Cairn sensor registry.
|
|
2
2
|
#
|
|
3
|
-
# Sensors are mechanical checks
|
|
3
|
+
# Sensors are mechanical checks run against an agent's diff.
|
|
4
4
|
# Listed here are the GENERIC kinds that ship with the Cairn package. Stack
|
|
5
|
-
# profiles
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# project's extension block (see workflow.md).
|
|
5
|
+
# profiles propose profile-specific sensors at adoption; the operator confirms
|
|
6
|
+
# each one. Project-specific sensors live in the adopting project's extension
|
|
7
|
+
# block (see workflow.md).
|
|
9
8
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# stack-profile-agnostic primitives.
|
|
9
|
+
# Cairn package code does NOT hardcode any specific project's sensor names.
|
|
10
|
+
# All concrete sensor identifiers below are stack-profile-agnostic primitives.
|
|
13
11
|
|
|
14
12
|
version: 1
|
|
15
13
|
|
|
@@ -80,11 +78,11 @@ sensors:
|
|
|
80
78
|
- every_run
|
|
81
79
|
fail_severity: hard
|
|
82
80
|
|
|
83
|
-
# ── §
|
|
81
|
+
# ── §INV invariant sensors (auto-registered by backprop) ────────────────
|
|
84
82
|
- id: invariant-suite
|
|
85
83
|
layer: invariants
|
|
86
84
|
kind: invariant_runner
|
|
87
|
-
description: "Runs every active §
|
|
85
|
+
description: "Runs every active §INV invariant's linked sensor script. Active invariants accumulate over time; superseded ones are skipped."
|
|
88
86
|
triggers:
|
|
89
87
|
- every_run
|
|
90
88
|
sources:
|
|
@@ -95,25 +93,25 @@ sensors:
|
|
|
95
93
|
- id: reviewer-subagent
|
|
96
94
|
layer: C
|
|
97
95
|
kind: inferential
|
|
98
|
-
description: "Same model as implementer; fresh context. Reads only spec.tightened.md + diff + decisions ledger + in-scope assertions. Anti-completionist framing. For high-stakes runs, also enforces query-scope completeness
|
|
96
|
+
description: "Same model as implementer; fresh context. Reads only spec.tightened.md + diff + decisions ledger + in-scope assertions. Anti-completionist framing. For high-stakes runs, also enforces query-scope completeness."
|
|
99
97
|
model_strategy: same_as_implementer
|
|
100
98
|
triggers:
|
|
101
99
|
- all_layer_d_sensors_passed
|
|
102
|
-
fail_severity: soft
|
|
100
|
+
fail_severity: soft
|
|
103
101
|
|
|
104
102
|
# ── Layer E — high-stakes E2E / demo ─────────────────────────────────────
|
|
105
103
|
- id: e2e-real-db
|
|
106
104
|
layer: E
|
|
107
105
|
kind: e2e
|
|
108
|
-
description: "Runs the project's E2E suite against a real database (no mocks). High-stakes UAT MUST include a cross-tenant negative fixture
|
|
106
|
+
description: "Runs the project's E2E suite against a real database (no mocks). High-stakes UAT MUST include a cross-tenant negative fixture — a request from user/org B against a resource owned by user/org A returning the expected denial."
|
|
109
107
|
triggers:
|
|
110
108
|
- high_stakes_glob_match
|
|
111
109
|
profile_hook: profile.e2e_command
|
|
112
110
|
fail_severity: hard
|
|
113
111
|
requires:
|
|
114
|
-
cross_tenant_fixture: true
|
|
112
|
+
cross_tenant_fixture: true
|
|
115
113
|
|
|
116
|
-
# ── Layer U — UAT
|
|
114
|
+
# ── Layer U — UAT headless ───────────────────────────────────────────────
|
|
117
115
|
- id: uat-headless-chrome
|
|
118
116
|
layer: U
|
|
119
117
|
kind: uat
|
|
@@ -131,7 +129,7 @@ sensors:
|
|
|
131
129
|
- nightly_cron
|
|
132
130
|
fail_severity: soft
|
|
133
131
|
|
|
134
|
-
# ── Local-dirty-overlap gate
|
|
132
|
+
# ── Local-dirty-overlap gate ──────────────────────────────────────────────
|
|
135
133
|
- id: local-dirty-overlap
|
|
136
134
|
layer: pre_dispatch
|
|
137
135
|
kind: working_tree_check
|
|
@@ -139,7 +137,7 @@ sensors:
|
|
|
139
137
|
triggers:
|
|
140
138
|
- pre_dispatch
|
|
141
139
|
- pre_push
|
|
142
|
-
fail_severity: soft
|
|
140
|
+
fail_severity: soft
|
|
143
141
|
|
|
144
142
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
145
143
|
# Scoped-glob keys.
|
|
@@ -178,8 +176,7 @@ copy_safety:
|
|
|
178
176
|
|
|
179
177
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
180
178
|
# Disable list — sensors known to false-positive at >50% on a given project.
|
|
181
|
-
# Default empty; populated per-project via
|
|
182
|
-
# we DO NOT mock or stub sensor results to make them pass).
|
|
179
|
+
# Default empty; populated per-project via the attention flow.
|
|
183
180
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
184
181
|
|
|
185
182
|
disabled_per_project: []
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Layer A — mechanical stub-pattern catalog.
|
|
2
2
|
#
|
|
3
|
-
# Initial seed (~30 patterns). Grows additively via the
|
|
4
|
-
#
|
|
3
|
+
# Initial seed (~30 patterns). Grows additively via the attention flow.
|
|
4
|
+
# Patterns NEVER added by typed CLI command.
|
|
5
5
|
#
|
|
6
6
|
# Each pattern is language-tagged. The orchestrator filters patterns by the
|
|
7
7
|
# stack profile's language list before running. False-positive cases are not
|
|
8
|
-
# silenced by editing this file — they're handled per-failure via the
|
|
9
|
-
#
|
|
8
|
+
# silenced by editing this file — they're handled per-failure via the attention
|
|
9
|
+
# flow (do NOT disable sensors to make diffs pass).
|
|
10
10
|
#
|
|
11
11
|
# Format:
|
|
12
12
|
# - id: <stable identifier; cited in failure messages>
|