@alecsibilia/luca 13.0.0-alpha.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.
Files changed (128) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +47 -0
  3. package/bin/luca.js +3 -0
  4. package/dist/chunks/branch.mjs +47 -0
  5. package/dist/chunks/bun-runtime.mjs +46 -0
  6. package/dist/chunks/checks.mjs +53 -0
  7. package/dist/chunks/claim-verify.mjs +465 -0
  8. package/dist/chunks/classify.mjs +105 -0
  9. package/dist/chunks/confidence.mjs +199 -0
  10. package/dist/chunks/doctor.mjs +158 -0
  11. package/dist/chunks/hook.mjs +696 -0
  12. package/dist/chunks/init.mjs +715 -0
  13. package/dist/chunks/muninndb-health.mjs +66 -0
  14. package/dist/chunks/phase.mjs +38 -0
  15. package/dist/chunks/pr-review.mjs +122 -0
  16. package/dist/chunks/preferences.mjs +61 -0
  17. package/dist/chunks/repair.mjs +111 -0
  18. package/dist/chunks/repo.mjs +58 -0
  19. package/dist/chunks/retro.mjs +86 -0
  20. package/dist/chunks/roadmap.mjs +58 -0
  21. package/dist/chunks/rules.mjs +527 -0
  22. package/dist/chunks/stale-mcp-server.mjs +90 -0
  23. package/dist/chunks/state.mjs +57 -0
  24. package/dist/chunks/stray-local-install.mjs +200 -0
  25. package/dist/chunks/telemetry.mjs +165 -0
  26. package/dist/chunks/todo.mjs +151 -0
  27. package/dist/chunks/vault-init.mjs +300 -0
  28. package/dist/chunks/verification.mjs +95 -0
  29. package/dist/chunks/version.mjs +70 -0
  30. package/dist/chunks/workflow.mjs +47 -0
  31. package/dist/claude/.claude/agents/architect.md +410 -0
  32. package/dist/claude/.claude/agents/build.md +111 -0
  33. package/dist/claude/.claude/agents/discuss.md +93 -0
  34. package/dist/claude/.claude/agents/discussion.md +149 -0
  35. package/dist/claude/.claude/agents/execute.md +416 -0
  36. package/dist/claude/.claude/agents/executor.md +161 -0
  37. package/dist/claude/.claude/agents/fast.md +84 -0
  38. package/dist/claude/.claude/agents/finalize.md +484 -0
  39. package/dist/claude/.claude/agents/learner.md +160 -0
  40. package/dist/claude/.claude/agents/plan-reviewer.md +129 -0
  41. package/dist/claude/.claude/agents/plan.md +96 -0
  42. package/dist/claude/.claude/agents/research.md +327 -0
  43. package/dist/claude/.claude/agents/researcher.md +78 -0
  44. package/dist/claude/.claude/agents/review.md +283 -0
  45. package/dist/claude/.claude/agents/reviewer.md +163 -0
  46. package/dist/claude/.claude/agents/shadow-scanner.md +257 -0
  47. package/dist/claude/.claude/agents/triage.md +230 -0
  48. package/dist/claude/.claude/agents/verifier.md +131 -0
  49. package/dist/claude/.claude/commands/bug-diagnose.md +12 -0
  50. package/dist/claude/.claude/commands/gh-issue-triage.md +14 -0
  51. package/dist/claude/.claude/commands/gh-pr-address.md +235 -0
  52. package/dist/claude/.claude/commands/gh-prepare.md +12 -0
  53. package/dist/claude/.claude/commands/grill-me.md +12 -0
  54. package/dist/claude/.claude/commands/lu-review.md +51 -0
  55. package/dist/claude/.claude/commands/lu.md +75 -0
  56. package/dist/claude/.claude/commands/luca-init.md +14 -0
  57. package/dist/claude/.claude/commands/luca-telemetry-report.md +12 -0
  58. package/dist/claude/.claude/commands/memory-audit.md +12 -0
  59. package/dist/claude/.claude/commands/milestone-new.md +122 -0
  60. package/dist/claude/.claude/commands/phase-discuss.md +45 -0
  61. package/dist/claude/.claude/commands/phase-execute.md +39 -0
  62. package/dist/claude/.claude/commands/phase-plan.md +53 -0
  63. package/dist/claude/.claude/commands/repo-cleanup.md +80 -0
  64. package/dist/claude/.claude/commands/todo-add.md +28 -0
  65. package/dist/claude/.claude/commands/todo-check.md +36 -0
  66. package/dist/claude/.claude/hooks/context-refresher.ts +285 -0
  67. package/dist/claude/.claude/hooks/continuation-messages.ts +215 -0
  68. package/dist/claude/.claude/hooks/pipeline-guard.ts +182 -0
  69. package/dist/claude/.claude/settings.json +41 -0
  70. package/dist/claude/skills/arch-audit/SKILL.md +161 -0
  71. package/dist/claude/skills/autopilot/SKILL.md +1299 -0
  72. package/dist/claude/skills/bug-diagnose/SKILL.md +102 -0
  73. package/dist/claude/skills/choose/SKILL.md +124 -0
  74. package/dist/claude/skills/gh-issue-triage/SKILL.md +97 -0
  75. package/dist/claude/skills/gh-pr-address/SKILL.md +235 -0
  76. package/dist/claude/skills/gh-prepare/SKILL.md +209 -0
  77. package/dist/claude/skills/grill-me/SKILL.md +46 -0
  78. package/dist/claude/skills/lu/SKILL.md +112 -0
  79. package/dist/claude/skills/lu-review/SKILL.md +51 -0
  80. package/dist/claude/skills/luca-init/SKILL.md +91 -0
  81. package/dist/claude/skills/luca-telemetry-report/SKILL.md +145 -0
  82. package/dist/claude/skills/luca-write-surface/SKILL.md +213 -0
  83. package/dist/claude/skills/memory-audit/SKILL.md +217 -0
  84. package/dist/claude/skills/milestone-audit/SKILL.md +545 -0
  85. package/dist/claude/skills/milestone-complete/SKILL.md +168 -0
  86. package/dist/claude/skills/milestone-gaps/SKILL.md +60 -0
  87. package/dist/claude/skills/milestone-new/SKILL.md +125 -0
  88. package/dist/claude/skills/note/SKILL.md +162 -0
  89. package/dist/claude/skills/phase-add/SKILL.md +91 -0
  90. package/dist/claude/skills/phase-assumptions/SKILL.md +92 -0
  91. package/dist/claude/skills/phase-discuss/SKILL.md +165 -0
  92. package/dist/claude/skills/phase-execute/SKILL.md +1786 -0
  93. package/dist/claude/skills/phase-insert/SKILL.md +100 -0
  94. package/dist/claude/skills/phase-plan/SKILL.md +461 -0
  95. package/dist/claude/skills/phase-remove/SKILL.md +113 -0
  96. package/dist/claude/skills/phase-research/SKILL.md +80 -0
  97. package/dist/claude/skills/post-init-tour/SKILL.md +58 -0
  98. package/dist/claude/skills/progress/SKILL.md +271 -0
  99. package/dist/claude/skills/project-new/SKILL.md +609 -0
  100. package/dist/claude/skills/quick/SKILL.md +256 -0
  101. package/dist/claude/skills/rename-audit/SKILL.md +52 -0
  102. package/dist/claude/skills/repo-audit/SKILL.md +88 -0
  103. package/dist/claude/skills/repo-cleanup/SKILL.md +80 -0
  104. package/dist/claude/skills/seed-memory/SKILL.md +235 -0
  105. package/dist/claude/skills/session-pause/SKILL.md +126 -0
  106. package/dist/claude/skills/session-plan/SKILL.md +112 -0
  107. package/dist/claude/skills/session-resume/SKILL.md +75 -0
  108. package/dist/claude/skills/todo-add/SKILL.md +85 -0
  109. package/dist/claude/skills/todo-check/SKILL.md +77 -0
  110. package/dist/claude/skills/workflow-save/SKILL.md +277 -0
  111. package/dist/index.d.mts +33 -0
  112. package/dist/index.d.ts +33 -0
  113. package/dist/index.mjs +69 -0
  114. package/dist/shared/luca.B3Mimc0P.mjs +52 -0
  115. package/dist/shared/luca.B3saVjJm.mjs +163 -0
  116. package/dist/shared/luca.BYdjkfnz.mjs +217 -0
  117. package/dist/shared/luca.BmhNkYe2.mjs +56 -0
  118. package/dist/shared/luca.C4gMUoBd.mjs +358 -0
  119. package/dist/shared/luca.CQ3g1xrD.mjs +19 -0
  120. package/dist/shared/luca.CRmaAfXR.mjs +713 -0
  121. package/dist/shared/luca.CrXzXueR.mjs +57 -0
  122. package/dist/shared/luca.DTomPq7I.mjs +91 -0
  123. package/dist/shared/luca.DjDTeDCi.mjs +1904 -0
  124. package/dist/shared/luca.HZxBTBgD.mjs +201 -0
  125. package/dist/shared/luca.TSMg1t7I.mjs +10 -0
  126. package/dist/shared/luca.dM-MKlNE.mjs +25 -0
  127. package/dist/shared/luca.naWEcQ4B.mjs +7 -0
  128. package/package.json +76 -0
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * continuation-messages handler — `PostToolUse` hook that injects a
4
+ * mode-entry kick-off prompt after the pipeline advances.
5
+ *
6
+ * This is the Claude Code delivery vehicle for the pure
7
+ * `computeContinuationMessage()` builder in
8
+ * `@alecsibilia/luca-core/orchestration`. The algorithm decides whether
9
+ * to emit and what to say; this handler is glue:
10
+ *
11
+ * 1. Read the Claude Code PostToolUse payload from stdin.
12
+ * 2. If it isn't a `Bash` invocation of `luca state advance <step>`,
13
+ * exit 0 silently (this hook only fires after pipeline advances).
14
+ * 3. Read `.luca/state.json` for the now-current `pipelineStep`.
15
+ * 4. Confirm the requested step matches the current step (i.e. the
16
+ * advance succeeded). If not, exit 0 silently — the CLI's own
17
+ * validation rejected the call, and an injected continuation for
18
+ * a step we didn't actually enter would mislead the agent.
19
+ * 5. Call `computeContinuationMessage()`.
20
+ * 6. Emit the message via the Claude Code PostToolUse hook output
21
+ * JSON shape:
22
+ *
23
+ * {
24
+ * "hookSpecificOutput": {
25
+ * "hookEventName": "PostToolUse",
26
+ * "additionalContext": "<system-reminder>...</system-reminder>"
27
+ * }
28
+ * }
29
+ *
30
+ * Exit 0 — `additionalContext` is the hook's only effect on the
31
+ * session.
32
+ *
33
+ * The Claude Code hook contract (PostToolUse):
34
+ *
35
+ * stdin JSON shape (snake_case fields per the docs):
36
+ * {
37
+ * "session_id": "...",
38
+ * "hook_event_name": "PostToolUse",
39
+ * "tool_name": "Bash",
40
+ * "tool_input": { "command": "luca state advance plan", ... },
41
+ * "tool_response": { "stdout": "...", "stderr": "...", ... }
42
+ * }
43
+ *
44
+ * Exit codes:
45
+ * 0 → all signals come from stdout JSON
46
+ * 2 → block (not used here — this hook is informational)
47
+ * * → other non-zero → error (failure-open: we never use this
48
+ * because a hook bug should never disrupt the session)
49
+ *
50
+ * Stdout JSON for context injection:
51
+ * {
52
+ * "hookSpecificOutput": {
53
+ * "hookEventName": "PostToolUse",
54
+ * "additionalContext": "..."
55
+ * }
56
+ * }
57
+ *
58
+ * Failure-open philosophy (per E-1 / E-2 reasoning, intentionally
59
+ * reused): if ANYTHING unexpected happens (stdin parse error,
60
+ * state.json missing/malformed, command argv doesn't parse), exit 0
61
+ * silently. A hook that mis-injects a continuation will confuse the
62
+ * agent; a hook that crashes is worse. Choose silent skip.
63
+ */
64
+ import { appendLedger } from '@alecsibilia/luca-core/ledger'
65
+ import {
66
+ computeContinuationMessage,
67
+ type ContinuationInput,
68
+ } from '@alecsibilia/luca-core/orchestration'
69
+ import {
70
+ loadCurrentState,
71
+ parseAdvanceCommand,
72
+ } from '@alecsibilia/luca-core/state'
73
+
74
+ /**
75
+ * Shape of the relevant slice of the PostToolUse stdin payload. We
76
+ * only look at `tool_name` and `tool_input.command`; everything else
77
+ * is ignored. Defensive typing — the harness may add fields and the
78
+ * handler should not break on them.
79
+ */
80
+ interface PostToolUsePayload {
81
+ tool_name?: string
82
+ toolName?: string
83
+ tool_input?: { command?: string }
84
+ toolInput?: { command?: string }
85
+ }
86
+
87
+ async function main(): Promise<number> {
88
+ const raw = await Bun.stdin.text()
89
+ if (!raw.trim()) {
90
+ // Empty stdin — nothing to do. Allow silently.
91
+ return 0
92
+ }
93
+
94
+ let payload: PostToolUsePayload
95
+ try {
96
+ payload = JSON.parse(raw) as PostToolUsePayload
97
+ } catch {
98
+ // Malformed stdin is the harness's problem. Fail open silently.
99
+ return 0
100
+ }
101
+
102
+ const toolName = payload.tool_name ?? payload.toolName
103
+ if (toolName !== 'Bash') {
104
+ // The Bash matcher catches every Bash call; we only act on
105
+ // `luca state advance`. Other Bash → silent skip.
106
+ return 0
107
+ }
108
+
109
+ const command = payload.tool_input?.command ?? payload.toolInput?.command
110
+ if (typeof command !== 'string') {
111
+ return 0
112
+ }
113
+
114
+ const requestedStep = parseAdvanceCommand(command)
115
+ if (requestedStep === null) {
116
+ // Not a `luca state advance` invocation.
117
+ return 0
118
+ }
119
+
120
+ const cwd = process.cwd()
121
+ const state = await loadCurrentState({ cwd })
122
+
123
+ // Confirm the advance actually happened by comparing the now-current
124
+ // step with the requested step. If they don't match the CLI rejected
125
+ // the transition (or the user wrote an exotic command form we didn't
126
+ // recognise); either way, skip silently rather than inject a
127
+ // misleading continuation.
128
+ if (state.pipelineStep !== requestedStep) {
129
+ return 0
130
+ }
131
+
132
+ const input: ContinuationInput = {
133
+ currentStep: state.pipelineStep,
134
+ ...(state.complexity !== undefined
135
+ ? { complexity: state.complexity }
136
+ : {}),
137
+ ...(state.oversight !== undefined
138
+ ? { oversight: state.oversight }
139
+ : {}),
140
+ ...(typeof state.currentPhase === 'number'
141
+ ? { currentPhase: state.currentPhase }
142
+ : {}),
143
+ ...(typeof state.totalPhases === 'number'
144
+ ? { totalPhases: state.totalPhases }
145
+ : {}),
146
+ }
147
+
148
+ const verdict = computeContinuationMessage(input)
149
+
150
+ // Emit a ledger event for the hook firing. Records whether a
151
+ // continuation was emitted (and why not, when applicable) so the
152
+ // postmortem analyzer has signal on hook coverage. Failure-open.
153
+ try {
154
+ const runId = typeof (state as { sessionId?: unknown }).sessionId === 'string'
155
+ ? (state as { sessionId: string }).sessionId
156
+ : ''
157
+ appendLedger({
158
+ cwd,
159
+ runId,
160
+ event: 'hook.continuation-messages.fired',
161
+ data: {
162
+ pipelineStep: state.pipelineStep,
163
+ decision: verdict === null
164
+ ? 'skipped'
165
+ : verdict.reason === 'unknown-current-step'
166
+ ? 'skipped'
167
+ : 'emitted',
168
+ reason: verdict === null ? 'no-continuation' : verdict.reason,
169
+ },
170
+ })
171
+ } catch {
172
+ // Failure-open.
173
+ }
174
+
175
+ if (verdict === null) {
176
+ // No continuation appropriate (e.g. advance into `idle`).
177
+ return 0
178
+ }
179
+
180
+ if (verdict.reason === 'unknown-current-step') {
181
+ // The algorithm flagged a data-integrity warning; we don't
182
+ // surface this to the model (it would be alarming and not
183
+ // actionable). Skip silently — the CLI's own validation will
184
+ // catch the underlying state.json corruption.
185
+ return 0
186
+ }
187
+
188
+ // Emit the additionalContext payload. Claude Code consumes the
189
+ // first valid JSON object on stdout for PostToolUse hooks.
190
+ const output = {
191
+ hookSpecificOutput: {
192
+ hookEventName: 'PostToolUse',
193
+ additionalContext: verdict.message,
194
+ },
195
+ }
196
+ process.stdout.write(JSON.stringify(output) + '\n')
197
+ return 0
198
+ }
199
+
200
+ // `parseAdvanceCommand` (and its `stripQuotes` helper) was promoted to
201
+ // `@alecsibilia/luca-core/state` (see `packages/luca-core/src/state/
202
+ // cli-parse.ts`) — both this hook and `pipeline-guard/handler.ts`
203
+ // used byte-identical copies, so the helper now lives in luca-core
204
+ // and is imported above (CF11).
205
+
206
+ main().then(
207
+ (code) => process.exit(code),
208
+ (err) => {
209
+ // Defensive: any unexpected throw means we fail open silently.
210
+ // No stderr — this is a PostToolUse informational hook; users
211
+ // shouldn't see internal errors.
212
+ void err
213
+ process.exit(0)
214
+ },
215
+ )
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pipeline-guard handler — `PreToolUse` hook that vets pipeline-step
4
+ * transitions before they execute.
5
+ *
6
+ * This is the Claude Code delivery vehicle for the pure
7
+ * `checkPipelineGuard()` algorithm in `@alecsibilia/luca-core/orchestration`.
8
+ * The algorithm decides legality; this handler is glue:
9
+ *
10
+ * 1. Read the Claude Code PreToolUse payload from stdin.
11
+ * 2. If it isn't a `Bash` invocation of `luca state advance <step>`,
12
+ * exit 0 (this hook only guards that one command).
13
+ * 3. Read `.luca/state.json` for the current `pipelineStep`.
14
+ * 4. Call `checkPipelineGuard()` with current + requested step.
15
+ * 5. Exit 0 on allow, 2 on block (Claude Code blocks the tool call
16
+ * and surfaces the stderr message to the model).
17
+ *
18
+ * The Claude Code hook contract (PreToolUse):
19
+ *
20
+ * stdin JSON shape (snake_case fields per the docs):
21
+ * {
22
+ * "session_id": "...",
23
+ * "hook_event_name": "PreToolUse",
24
+ * "tool_name": "Bash",
25
+ * "tool_input": { "command": "luca state advance plan", ... }
26
+ * }
27
+ *
28
+ * Exit codes:
29
+ * 0 → allow the tool call
30
+ * 2 → block + send stderr to the model
31
+ * * → other non-zero → error (Claude Code surfaces to the user;
32
+ * we never use this path because we'd rather fail open than
33
+ * break the user's workflow on a parse error)
34
+ *
35
+ * Why a bun-script: the algorithm needs typed access to
36
+ * `checkPipelineGuard` + the pipeline-transitions table. A shell
37
+ * wrapper would have to re-implement the legality check or shell out
38
+ * to the `luca` CLI; both create drift surfaces. Direct TS import
39
+ * keeps the source of truth in one place.
40
+ *
41
+ * Failure-open philosophy: if ANYTHING unexpected happens (stdin
42
+ * parse error, state.json missing/malformed, command argv doesn't
43
+ * parse), we exit 0 and let the call through. Hooks that crash will
44
+ * break the user's session; hooks that wrongly block will frustrate
45
+ * them. Both are bad — but blocking is worse because the user can't
46
+ * recover without disabling the hook. Choose to fail open and lean
47
+ * on the CLI's own validation (the `luca state advance` command does
48
+ * its own legal-transition check independently).
49
+ */
50
+ import { join } from 'node:path'
51
+
52
+ import { appendLedger } from '@alecsibilia/luca-core/ledger'
53
+ import {
54
+ checkPipelineGuard,
55
+ type PipelineGuardInput,
56
+ } from '@alecsibilia/luca-core/orchestration'
57
+ import {
58
+ loadCurrentState,
59
+ parseAdvanceCommand,
60
+ } from '@alecsibilia/luca-core/state'
61
+
62
+ /**
63
+ * Shape of the relevant slice of the PreToolUse stdin payload. We
64
+ * only look at `tool_name` and `tool_input.command`; everything else
65
+ * is ignored. Defensive typing — the harness may add fields and the
66
+ * handler should not break on them.
67
+ */
68
+ interface PreToolUsePayload {
69
+ tool_name?: string
70
+ toolName?: string
71
+ tool_input?: { command?: string }
72
+ toolInput?: { command?: string }
73
+ }
74
+
75
+ async function main(): Promise<number> {
76
+ const raw = await Bun.stdin.text()
77
+ if (!raw.trim()) {
78
+ // Empty stdin — nothing to guard. Allow.
79
+ return 0
80
+ }
81
+
82
+ let payload: PreToolUsePayload
83
+ try {
84
+ payload = JSON.parse(raw) as PreToolUsePayload
85
+ } catch {
86
+ // Malformed stdin is the harness's problem, not ours. Fail open.
87
+ return 0
88
+ }
89
+
90
+ const toolName = payload.tool_name ?? payload.toolName
91
+ if (toolName !== 'Bash') {
92
+ // The hook also matches non-Bash tools if registered broadly,
93
+ // but pipeline-guard only cares about `luca state advance`.
94
+ return 0
95
+ }
96
+
97
+ const command = payload.tool_input?.command ?? payload.toolInput?.command
98
+ if (typeof command !== 'string') {
99
+ return 0
100
+ }
101
+
102
+ const requestedStep = parseAdvanceCommand(command)
103
+ if (requestedStep === null) {
104
+ // Not a `luca state advance` invocation, or shape doesn't
105
+ // match. Nothing to guard.
106
+ return 0
107
+ }
108
+
109
+ const cwd = process.cwd()
110
+ const state = await loadCurrentState({ cwd })
111
+
112
+ const input: PipelineGuardInput = {
113
+ currentStep: state.pipelineStep,
114
+ requestedStep,
115
+ ...(state.complexity !== undefined
116
+ ? { complexity: state.complexity }
117
+ : {}),
118
+ ...(state.oversight !== undefined
119
+ ? { oversight: state.oversight }
120
+ : {}),
121
+ }
122
+
123
+ const verdict = checkPipelineGuard(input)
124
+
125
+ // Emit a ledger event for the hook firing. The postmortem analyzer
126
+ // scans the ledger for `hook.pipeline-guard.fired` events to detect
127
+ // pipeline-guard rejections and forced transitions over time.
128
+ // Failure-open: any ledger-write error MUST NOT block the hook.
129
+ try {
130
+ const runId = typeof (state as { sessionId?: unknown }).sessionId === 'string'
131
+ ? (state as { sessionId: string }).sessionId
132
+ : ''
133
+ appendLedger({
134
+ cwd,
135
+ runId,
136
+ event: 'hook.pipeline-guard.fired',
137
+ data: {
138
+ pipelineStep: state.pipelineStep,
139
+ requestedStep,
140
+ decision: verdict.allowed ? 'allow' : 'block',
141
+ reason: verdict.allowed ? undefined : verdict.message,
142
+ },
143
+ })
144
+ } catch {
145
+ // Failure-open: never block the hook on a ledger-write error.
146
+ }
147
+
148
+ if (verdict.allowed) {
149
+ return 0
150
+ }
151
+
152
+ // Block: print to stderr (Claude Code surfaces to the model) and
153
+ // exit 2.
154
+ process.stderr.write(verdict.message + '\n')
155
+ return 2
156
+ }
157
+
158
+ // `parseAdvanceCommand` (and its `stripQuotes` helper) was promoted to
159
+ // `@alecsibilia/luca-core/state` (see `packages/luca-core/src/state/
160
+ // cli-parse.ts`) — both the pipeline-guard hook and the
161
+ // continuation-messages hook used byte-identical copies, so the
162
+ // helper now lives in luca-core and is imported above (CF11).
163
+
164
+ // Touched to suppress unused-import in some toolchains; `join` may be
165
+ // folded out by the bundler if unused, but kept here in case future
166
+ // edits need explicit cwd handling.
167
+ void join
168
+
169
+ main().then(
170
+ (code) => process.exit(code),
171
+ (err) => {
172
+ // Defensive: any unexpected throw means we fail open. We
173
+ // explicitly do NOT exit 2 on internal errors — that would
174
+ // block the user's command on a hook bug.
175
+ process.stderr.write(
176
+ `pipeline-guard handler: internal error (failing open): ${
177
+ (err as Error).message
178
+ }\n`,
179
+ )
180
+ process.exit(0)
181
+ },
182
+ )
@@ -0,0 +1,41 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pipeline-guard.ts",
9
+ "timeout": 5,
10
+ "statusMessage": "PreToolUse guard for `luca state advance` — blocks illegal pipelineStep transitions via the canonical legal-transitions table."
11
+ }
12
+ ],
13
+ "matcher": "Bash"
14
+ }
15
+ ],
16
+ "PostToolUse": [
17
+ {
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/continuation-messages.ts",
22
+ "timeout": 5,
23
+ "statusMessage": "PostToolUse continuation prompt for `luca state advance` — injects a mode-entry kick-off message via additionalContext when the pipeline advances."
24
+ }
25
+ ],
26
+ "matcher": "Bash"
27
+ },
28
+ {
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/context-refresher.ts",
33
+ "timeout": 5,
34
+ "statusMessage": "PostToolUse context-refresher — surfaces a per-step <luca-reminder> via additionalContext after every Nth tool call (default 30) or on a step change since the last fire."
35
+ }
36
+ ],
37
+ "matcher": "*"
38
+ }
39
+ ]
40
+ }
41
+ }
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: arch-audit
3
+ description: "Find deepening opportunities — shallow modules, premature abstractions, misplaced seams. Uses the deletion test and promotion model to surface architectural friction. Use when user says \"audit architecture\", \"find refactoring opportunities\", \"what's shallow\", \"improve structure\", or invokes /arch-audit."
4
+ ---
5
+
6
+ # arch-audit
7
+
8
+ Surface architectural friction and propose deepening opportunities — refactors that turn shallow modules into deep ones, improve testability, and increase locality of change.
9
+
10
+ ## Vocabulary
11
+
12
+ Use these terms exactly in every finding. Consistent language prevents drift into vague terms like "component," "service," or "boundary."
13
+
14
+ - **Module** — anything with an interface and implementation (function, class, file, package). Scale-agnostic.
15
+ - **Interface** — what a caller must know: types, invariants, error modes, ordering. Not just the type signature.
16
+ - **Depth** — leverage at the interface. **Deep** = significant behavior behind a small interface. **Shallow** = interface nearly as complex as the implementation.
17
+ - **Seam** — where behavior can be altered without editing in place. A boundary that accepts different adapters.
18
+ - **Deletion test** — imagine deleting the module. Complexity vanishes → pass-through (shallow). Complexity reappears across callers → earning its keep (deep).
19
+ - **Promotion tier** — where code lives based on caller count. Tier 1 (single caller, private). Tier 2 (shared within feature). Tier 3 (shared across features).
20
+
21
+ ## Step 1: Recall architectural context
22
+
23
+ Query MuninnDB for past decisions so you don't re-suggest rejected refactors:
24
+
25
+ ```
26
+ mcp__muninn__muninn_recall({
27
+ vault: "<repo_vault>",
28
+ context: ["architectural decisions", "rejected refactors", "module structure"],
29
+ tags: ["decision"],
30
+ limit: 15
31
+ })
32
+ ```
33
+
34
+ Resolve `<repo_vault>` from `.luca/config.json` → `muninn.vault`, falling back to `"default"`.
35
+
36
+ If a `docs/context.md` exists, read it for project constraints and domain terminology (check the repo root for a legacy `context.md` as a fallback).
37
+
38
+ **Guard**: if a past decision explicitly rejected a refactor you'd otherwise suggest, skip it. Only resurface if friction has materially worsened since the decision was recorded.
39
+
40
+ ## Step 2: Explore for friction
41
+
42
+ Spawn one or more codebase-exploration subagents (the `Explore` agent, via the `Agent` tool) to walk the codebase. Provide this brief:
43
+
44
+ > Walk the codebase and report friction. Look for:
45
+ >
46
+ > 1. **Shallow modules** — interface nearly as complex as implementation. Apply the deletion test: would removing this module concentrate complexity (earning its keep) or just redistribute it (pass-through)?
47
+ > 2. **Concept bouncing** — understanding one concept requires reading across many small files
48
+ > 3. **Premature abstractions** — interfaces/abstract types with a single implementation
49
+ > 4. **Leaked coupling** — tightly-coupled modules that share internal details across their seam
50
+ > 5. **Misplaced code** — helpers/utilities at promotion tier 3 (shared across features) that have only 1-2 callers
51
+ > 6. **Untestable surfaces** — code that's hard to test through its current public interface
52
+ >
53
+ > For each finding, report: file path, what the friction is, deletion test result (if applicable), caller count.
54
+
55
+ If the codebase is large, scope the exploration to specific areas the user mentions, or split into multiple focused subagents (e.g., one per package in a monorepo).
56
+
57
+ Do NOT follow rigid heuristics. Explore organically. Note where you experience friction — where understanding breaks down, where you bounce between files, where interfaces feel heavier than what they hide.
58
+
59
+ ## Step 3: Present deepening candidates
60
+
61
+ Synthesize subagent findings into a numbered list of **deepening opportunities**. For each candidate:
62
+
63
+ ```
64
+ ### <N>. <Short description>
65
+
66
+ - **Files**: <paths involved>
67
+ - **Problem**: <what the friction is — use vocabulary precisely>
68
+ - **Deletion test**: <result — "vanishes" or "reappears across N callers">
69
+ - **Promotion check**: <current tier vs appropriate tier based on caller count>
70
+ - **Proposed direction**: <one sentence — what "deeper" looks like here>
71
+ - **Risk**: <what could go wrong if refactored>
72
+ ```
73
+
74
+ Order by impact (most friction first). Limit to 5-7 candidates — more than that is noise.
75
+
76
+ **Do NOT propose interfaces or implementations yet.** Present candidates only.
77
+
78
+ Ask: "Which of these would you like to explore?"
79
+
80
+ ## Step 4: Design alternatives (design-it-twice)
81
+
82
+ For the candidate the user selects:
83
+
84
+ 1. **Frame the problem** — write a short explanation for the user:
85
+ - What constraints any new interface must satisfy
86
+ - What dependencies exist and their nature
87
+ - A rough illustrative sketch (not a proposal — just grounding)
88
+
89
+ 2. **Spawn 2-3 `Explore` / `Plan` subagents in parallel** (via the `Agent` tool) — each must produce a *radically different* interface design for the deepened module. Brief each subagent with:
90
+ - File paths and current coupling
91
+ - Caller list and their expectations
92
+ - The vocabulary (Module, Interface, Depth, Seam)
93
+ - Constraint: "Your design must be fundamentally different from the others"
94
+
95
+ 3. **Present designs sequentially** — let the user absorb each one:
96
+ - Interface signature (public surface)
97
+ - Usage example (how callers use it)
98
+ - What it hides (complexity kept internal)
99
+ - Trade-offs (what this design makes easy vs hard)
100
+
101
+ 4. **Compare and recommend** — contrast by:
102
+ - Depth (leverage at the interface)
103
+ - Locality (where change concentrates)
104
+ - Seam placement (where behavior can be swapped)
105
+ - Testability (how easy to test through the interface)
106
+
107
+ Give your recommendation. If elements from different designs combine well, propose a hybrid.
108
+
109
+ 5. **User decides** — they pick a design (or request iteration).
110
+
111
+ ## Step 5: Record the outcome
112
+
113
+ After the user decides, store the result in MuninnDB.
114
+
115
+ **If the refactor is accepted:**
116
+
117
+ ```
118
+ mcp__muninn__muninn_remember({
119
+ vault: "<repo_vault>",
120
+ concept: "decision:arch-audit-<descriptive-slug>",
121
+ content: "Accepted deepening: <what was refactored, which design was chosen, why>. Files: <paths>. Date: <ISO>.",
122
+ tags: ["decision", "architecture", "refactor"]
123
+ })
124
+ ```
125
+
126
+ Capture the returned id and promote this user-confirmed decision to the verified tier: `mcp__muninn__muninn_trust({ id: <returned-id>, trust: "verified", vault: "<repo_vault>" })`.
127
+
128
+ **If the refactor is rejected:**
129
+
130
+ ```
131
+ mcp__muninn__muninn_remember({
132
+ vault: "<repo_vault>",
133
+ concept: "decision:arch-audit-rejected-<descriptive-slug>",
134
+ content: "Rejected deepening of <module>. Reason: <user's reason>. Friction level at time of rejection: <description>. Re-evaluate if: <conditions that would change the decision>.",
135
+ tags: ["decision", "architecture", "rejected"]
136
+ })
137
+ ```
138
+
139
+ Capture the returned id and promote it to the verified tier: `mcp__muninn__muninn_trust({ id: <returned-id>, trust: "verified", vault: "<repo_vault>" })`.
140
+
141
+ The rejected decision prevents re-suggestion on future runs (Step 1 guard).
142
+
143
+ Note: `decision:*` memories are project-scoped — they route to the **repo** vault.
144
+
145
+ ## Step 6: Plan the refactor (optional)
146
+
147
+ If the user wants to proceed with implementation:
148
+
149
+ - Ask: "Want me to create a plan for this refactor, or do it directly?"
150
+ - If plan → suggest invoking `/lu` with the chosen design as context
151
+ - If direct → proceed with implementation in the current session
152
+
153
+ The chosen interface from Step 4 becomes the first test's target (interface-first task boundaries).
154
+
155
+ ## Behavioral Notes
156
+
157
+ - **This is ad-hoc** — invoked when the user feels friction, not on a schedule
158
+ - **No file dependencies** — everything lives in MuninnDB or is inlined in this skill
159
+ - **Scope** — if the user doesn't specify an area, ask before exploring the entire codebase. For monorepos, start with one package.
160
+ - **Vocabulary discipline** — use Module/Interface/Depth/Seam/Deletion Test consistently. Don't drift into "component," "service," "API," "boundary," "layer."
161
+ - **Don't be prescriptive about tooling** — the skill identifies friction and proposes designs. It doesn't force a specific architecture style (DDD, hexagonal, etc.) unless the project already uses one.