@rasensio/aidlc 1.66.0 → 1.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/autopilot/driver.d.ts +35 -2
- package/dist/autopilot/driver.d.ts.map +1 -1
- package/dist/autopilot/driver.js +650 -27
- package/dist/autopilot/driver.js.map +1 -1
- package/dist/autopilot/presets.d.ts.map +1 -1
- package/dist/autopilot/presets.js +5 -0
- package/dist/autopilot/presets.js.map +1 -1
- package/dist/autopilot/result.d.ts +133 -0
- package/dist/autopilot/result.d.ts.map +1 -0
- package/dist/autopilot/result.js +431 -0
- package/dist/autopilot/result.js.map +1 -0
- package/dist/autopilot/run.d.ts +2 -0
- package/dist/autopilot/run.d.ts.map +1 -1
- package/dist/autopilot/run.js +33 -6
- package/dist/autopilot/run.js.map +1 -1
- package/dist/autopilot/state.d.ts +51 -0
- package/dist/autopilot/state.d.ts.map +1 -1
- package/dist/autopilot/state.js.map +1 -1
- package/dist/autopilot/typed.d.ts +98 -0
- package/dist/autopilot/typed.d.ts.map +1 -0
- package/dist/autopilot/typed.js +122 -0
- package/dist/autopilot/typed.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/hook.d.ts +37 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +208 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +66 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/knowledge.d.ts.map +1 -1
- package/dist/commands/knowledge.js +8 -4
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/roadmap.d.ts +20 -3
- package/dist/commands/roadmap.d.ts.map +1 -1
- package/dist/commands/roadmap.js +60 -15
- package/dist/commands/roadmap.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +8 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/compile/adapters/claude-code.d.ts.map +1 -1
- package/dist/compile/adapters/claude-code.js +35 -4
- package/dist/compile/adapters/claude-code.js.map +1 -1
- package/dist/compile/adapters/codex.d.ts +12 -0
- package/dist/compile/adapters/codex.d.ts.map +1 -1
- package/dist/compile/adapters/codex.js +58 -8
- package/dist/compile/adapters/codex.js.map +1 -1
- package/dist/compile/loaders.d.ts.map +1 -1
- package/dist/compile/loaders.js +2 -0
- package/dist/compile/loaders.js.map +1 -1
- package/dist/compile/skill-metadata.d.ts +82 -0
- package/dist/compile/skill-metadata.d.ts.map +1 -0
- package/dist/compile/skill-metadata.js +156 -0
- package/dist/compile/skill-metadata.js.map +1 -0
- package/dist/concurrency/claim-store.d.ts +17 -0
- package/dist/concurrency/claim-store.d.ts.map +1 -1
- package/dist/concurrency/claim-store.js +42 -2
- package/dist/concurrency/claim-store.js.map +1 -1
- package/dist/concurrency/common-dir.d.ts +15 -0
- package/dist/concurrency/common-dir.d.ts.map +1 -1
- package/dist/concurrency/common-dir.js +39 -0
- package/dist/concurrency/common-dir.js.map +1 -1
- package/dist/core/types.d.ts +17 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/cost/hooks/arm.d.ts +9 -1
- package/dist/cost/hooks/arm.d.ts.map +1 -1
- package/dist/cost/hooks/arm.js +16 -2
- package/dist/cost/hooks/arm.js.map +1 -1
- package/dist/cost/providers/codex.d.ts +13 -0
- package/dist/cost/providers/codex.d.ts.map +1 -1
- package/dist/cost/providers/codex.js +74 -2
- package/dist/cost/providers/codex.js.map +1 -1
- package/dist/doctor/migrations/index.d.ts.map +1 -1
- package/dist/doctor/migrations/index.js +5 -0
- package/dist/doctor/migrations/index.js.map +1 -1
- package/dist/doctor/migrations/roadmap-mermaid-graphs.d.ts +22 -0
- package/dist/doctor/migrations/roadmap-mermaid-graphs.d.ts.map +1 -0
- package/dist/doctor/migrations/roadmap-mermaid-graphs.js +74 -0
- package/dist/doctor/migrations/roadmap-mermaid-graphs.js.map +1 -0
- package/dist/hooks/guard.d.ts +41 -0
- package/dist/hooks/guard.d.ts.map +1 -0
- package/dist/hooks/guard.js +108 -0
- package/dist/hooks/guard.js.map +1 -0
- package/dist/hooks/install.d.ts +78 -0
- package/dist/hooks/install.d.ts.map +1 -0
- package/dist/hooks/install.js +207 -0
- package/dist/hooks/install.js.map +1 -0
- package/dist/hooks/lifecycle-context.d.ts +65 -0
- package/dist/hooks/lifecycle-context.d.ts.map +1 -0
- package/dist/hooks/lifecycle-context.js +212 -0
- package/dist/hooks/lifecycle-context.js.map +1 -0
- package/dist/knowledge/engine.d.ts +19 -2
- package/dist/knowledge/engine.d.ts.map +1 -1
- package/dist/knowledge/engine.js +35 -11
- package/dist/knowledge/engine.js.map +1 -1
- package/dist/knowledge/index/knowledge-index.d.ts +28 -2
- package/dist/knowledge/index/knowledge-index.d.ts.map +1 -1
- package/dist/knowledge/index/sqlite-index.d.ts +19 -1
- package/dist/knowledge/index/sqlite-index.d.ts.map +1 -1
- package/dist/knowledge/index/sqlite-index.js +66 -5
- package/dist/knowledge/index/sqlite-index.js.map +1 -1
- package/dist/knowledge/source-fingerprint.d.ts +71 -0
- package/dist/knowledge/source-fingerprint.d.ts.map +1 -0
- package/dist/knowledge/source-fingerprint.js +105 -0
- package/dist/knowledge/source-fingerprint.js.map +1 -0
- package/dist/knowledge/sync.d.ts +12 -1
- package/dist/knowledge/sync.d.ts.map +1 -1
- package/dist/knowledge/sync.js +23 -3
- package/dist/knowledge/sync.js.map +1 -1
- package/dist/knowledge/types.d.ts +10 -1
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +10 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +2 -0
- package/dist/menu/roster.js.map +1 -1
- package/dist/roadmap/graph.d.ts +73 -28
- package/dist/roadmap/graph.d.ts.map +1 -1
- package/dist/roadmap/graph.js +184 -125
- package/dist/roadmap/graph.js.map +1 -1
- package/dist/roadmap/next.d.ts +5 -0
- package/dist/roadmap/next.d.ts.map +1 -1
- package/dist/roadmap/next.js +7 -1
- package/dist/roadmap/next.js.map +1 -1
- package/dist/roadmap/open-browser.d.ts +34 -0
- package/dist/roadmap/open-browser.d.ts.map +1 -0
- package/dist/roadmap/open-browser.js +40 -0
- package/dist/roadmap/open-browser.js.map +1 -0
- package/dist/roadmap/paths.d.ts +5 -1
- package/dist/roadmap/paths.d.ts.map +1 -1
- package/dist/roadmap/paths.js +5 -9
- package/dist/roadmap/paths.js.map +1 -1
- package/dist/roadmap/view-assets.d.ts +18 -0
- package/dist/roadmap/view-assets.d.ts.map +1 -0
- package/dist/roadmap/view-assets.js +301 -0
- package/dist/roadmap/view-assets.js.map +1 -0
- package/dist/roadmap/view-html.d.ts +37 -0
- package/dist/roadmap/view-html.d.ts.map +1 -0
- package/dist/roadmap/view-html.js +317 -0
- package/dist/roadmap/view-html.js.map +1 -0
- package/dist/roadmap/view-model.d.ts +93 -0
- package/dist/roadmap/view-model.d.ts.map +1 -0
- package/dist/roadmap/view-model.js +194 -0
- package/dist/roadmap/view-model.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The phase guard: a Claude Code `PreToolUse` hook that asks before a code file
|
|
3
|
+
* is edited while the instance claimed here is still being planned
|
|
4
|
+
* (hooks-carry-lifecycle-state/AC-11..AC-13).
|
|
5
|
+
*
|
|
6
|
+
* The opposite contract to the reviewer guard (`compile/reviewer-guard.ts`), on
|
|
7
|
+
* purpose. That one fails closed because it is a security boundary for an agent
|
|
8
|
+
* that must not write. This one enforces a process rule — "no code before the
|
|
9
|
+
* design is agreed" — and the risk named for it is a hook that blocks legitimate
|
|
10
|
+
* work. So it only ever *asks*, and every way it can fail is silence: the
|
|
11
|
+
* decision falls back to the normal permission flow, never to a block (design D5).
|
|
12
|
+
* Writes made through `Bash` are not seen; it is a guardrail, not a boundary.
|
|
13
|
+
*
|
|
14
|
+
* Design: design.md C3
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import { realpathSync } from 'node:fs';
|
|
19
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
20
|
+
import { isWithin, toPosixRelative } from '../core/paths.js';
|
|
21
|
+
import { PLANNING_PHASES, claimsHere, currentPhase, plainLine } from './lifecycle-context.js';
|
|
22
|
+
/** The tools that write a file named in their input. */
|
|
23
|
+
export const GUARDED_TOOLS = ['Edit', 'Write', 'MultiEdit', 'NotebookEdit'];
|
|
24
|
+
function targetOf(toolInput) {
|
|
25
|
+
for (const key of ['file_path', 'notebook_path']) {
|
|
26
|
+
const value = toolInput[key];
|
|
27
|
+
if (typeof value === 'string' && value !== '')
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The project root as written and as resolved through symlinks. Harnesses may
|
|
34
|
+
* name a file through either (macOS `/var` is `/private/var`), and a purely
|
|
35
|
+
* lexical test against only one form would call a project file "outside".
|
|
36
|
+
*/
|
|
37
|
+
function rootForms(projectRoot) {
|
|
38
|
+
const forms = [resolve(projectRoot)];
|
|
39
|
+
try {
|
|
40
|
+
const real = realpathSync(projectRoot);
|
|
41
|
+
if (real !== forms[0])
|
|
42
|
+
forms.push(real);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// keep the lexical form only
|
|
46
|
+
}
|
|
47
|
+
return forms;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether to ask before this tool call, from the raw `PreToolUse` payload.
|
|
51
|
+
*
|
|
52
|
+
* Null in every case but one: a guarded tool, writing a file inside the project
|
|
53
|
+
* and outside `.aidlc/`, while an instance claimed here is in ideation,
|
|
54
|
+
* requirements or design (AC-11, AC-12). Unreadable input is null, never a throw.
|
|
55
|
+
*/
|
|
56
|
+
export function guardDecision(projectRoot, raw) {
|
|
57
|
+
let input;
|
|
58
|
+
try {
|
|
59
|
+
input = JSON.parse(raw);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (typeof input !== 'object' || input === null)
|
|
65
|
+
return null;
|
|
66
|
+
const { tool_name: toolName, tool_input: toolInput, cwd } = input;
|
|
67
|
+
if (typeof toolName !== 'string' || !GUARDED_TOOLS.includes(toolName))
|
|
68
|
+
return null;
|
|
69
|
+
if (typeof toolInput !== 'object' || toolInput === null)
|
|
70
|
+
return null;
|
|
71
|
+
const target = targetOf(toolInput);
|
|
72
|
+
if (target === null)
|
|
73
|
+
return null;
|
|
74
|
+
const base = typeof cwd === 'string' && isAbsolute(cwd) ? cwd : projectRoot;
|
|
75
|
+
const file = resolve(base, target);
|
|
76
|
+
const root = rootForms(projectRoot).find((r) => isWithin(r, file));
|
|
77
|
+
// The root itself is a directory, not a file anyone edits.
|
|
78
|
+
if (root === undefined || file === root || isWithin(join(root, '.aidlc'), file))
|
|
79
|
+
return null;
|
|
80
|
+
for (const instance of claimsHere(projectRoot)) {
|
|
81
|
+
const phase = currentPhase(projectRoot, instance);
|
|
82
|
+
if (phase === null || !PLANNING_PHASES.includes(phase))
|
|
83
|
+
continue;
|
|
84
|
+
const rel = toPosixRelative(root, file);
|
|
85
|
+
return {
|
|
86
|
+
instance,
|
|
87
|
+
phase,
|
|
88
|
+
file: rel,
|
|
89
|
+
reason: plainLine(`AIDLC: ${instance} is in the ${phase} phase; code is written in implementation. Edit ${rel} anyway?`),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Claude Code's `PreToolUse` answer for a decision. Always `ask` — never `deny`
|
|
96
|
+
* and never `allow` (AC-13): an `allow` would skip the permission prompt the
|
|
97
|
+
* user configured, which is not this hook's to waive.
|
|
98
|
+
*/
|
|
99
|
+
export function guardOutput(decision) {
|
|
100
|
+
return JSON.stringify({
|
|
101
|
+
hookSpecificOutput: {
|
|
102
|
+
hookEventName: 'PreToolUse',
|
|
103
|
+
permissionDecision: 'ask',
|
|
104
|
+
permissionDecisionReason: decision.reason,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/hooks/guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs the lifecycle hooks: the session-start hook for Claude Code and
|
|
3
|
+
* Codex, and the opt-in phase guard for Claude Code
|
|
4
|
+
* (hooks-carry-lifecycle-state/AC-14..AC-16, AC-20, AC-21).
|
|
5
|
+
*
|
|
6
|
+
* Identity is a trailing marker, as for the cost hooks, but a different one
|
|
7
|
+
* (design D9). With a shared marker `aidlc cost hooks status` would count a
|
|
8
|
+
* lifecycle hook as a cost hook and `cost hooks remove` would delete it. The cost
|
|
9
|
+
* installer matches `endsWith('#aidlc-cost-hook')`, which no command here
|
|
10
|
+
* satisfies, so neither installer sees the other's entries and the cost code is
|
|
11
|
+
* unchanged. The merge below is written again rather than generalised out of
|
|
12
|
+
* `cost/hooks/claude-code.ts` because it also writes Codex's file, and the cost
|
|
13
|
+
* installer's tests pin its exact behaviour.
|
|
14
|
+
*
|
|
15
|
+
* Codex trusts a hook by its content hash, and only the user's own config may
|
|
16
|
+
* record that hash (knowledge: `codex-hook-trust-model`). So this module writes
|
|
17
|
+
* the project's `.codex/hooks.json` and nothing else for Codex — never
|
|
18
|
+
* `~/.codex/`, never a trust hash — and writes only when the text would change,
|
|
19
|
+
* so installing twice cannot cost the user the trust they already gave.
|
|
20
|
+
*
|
|
21
|
+
* Design: design.md C4
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
export type LifecyclePlatform = 'claude-code' | 'codex';
|
|
26
|
+
export type LifecycleHook = 'session-start' | 'guard';
|
|
27
|
+
export declare const LIFECYCLE_PLATFORMS: readonly LifecyclePlatform[];
|
|
28
|
+
export declare const LIFECYCLE_HOOK_MARKER = "#aidlc-lifecycle-hook";
|
|
29
|
+
export declare const GUARD_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
|
|
30
|
+
/**
|
|
31
|
+
* The two hook commands for a platform. Both run through the cost-capture shim,
|
|
32
|
+
* which finds an absolute `node` and this project's CLI at run time and never
|
|
33
|
+
* consults `PATH` (cost-capture-goes-dark/AC-15) — a bare `aidlc` here would be a
|
|
34
|
+
* `PATH` lookup on every session start (design D1).
|
|
35
|
+
*/
|
|
36
|
+
export declare function lifecycleCommands(platform?: LifecyclePlatform): Record<LifecycleHook, string>;
|
|
37
|
+
export declare function hookFilePath(projectRoot: string, platform: LifecyclePlatform): string;
|
|
38
|
+
/** Whether the phase guard can be installed for a platform. */
|
|
39
|
+
export declare function supportsGuard(platform: LifecyclePlatform): boolean;
|
|
40
|
+
/** The step the user still owes before the platform runs the hook, or null. */
|
|
41
|
+
export declare function trustStep(platform: LifecyclePlatform): string | null;
|
|
42
|
+
/** Whether writing a platform's hook needs the user's explicit yes (AC-18..AC-20). */
|
|
43
|
+
export declare function needsConsent(platform: LifecyclePlatform): boolean;
|
|
44
|
+
/** The command that writes a consent-gated platform's hook, for every path that does not. */
|
|
45
|
+
export declare function installCommand(platform: LifecyclePlatform): string;
|
|
46
|
+
export interface WriteResult {
|
|
47
|
+
/** False when the file already said exactly this. */
|
|
48
|
+
written: boolean;
|
|
49
|
+
path: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Install the session-start hook, and the guard when asked, into one platform's
|
|
53
|
+
* hook file (AC-15, AC-16, AC-20). Writes the shim first when it is missing or
|
|
54
|
+
* unusable, since every command here runs through it.
|
|
55
|
+
*
|
|
56
|
+
* @throws when the guard is asked for Codex — its `PreToolUse` can deny or
|
|
57
|
+
* rewrite a call but not ask, and the guard only ever asks.
|
|
58
|
+
*/
|
|
59
|
+
export declare function installLifecycleHooks(projectRoot: string, opts: {
|
|
60
|
+
platform: LifecyclePlatform;
|
|
61
|
+
guard?: boolean;
|
|
62
|
+
}): WriteResult;
|
|
63
|
+
/**
|
|
64
|
+
* Remove the lifecycle hooks from one platform's file, or only the guard
|
|
65
|
+
* (AC-14, AC-16). Unmarked entries and every other key stay; a file that does
|
|
66
|
+
* not exist is not created.
|
|
67
|
+
*/
|
|
68
|
+
export declare function removeLifecycleHooks(projectRoot: string, opts: {
|
|
69
|
+
platform: LifecyclePlatform;
|
|
70
|
+
guardOnly?: boolean;
|
|
71
|
+
}): WriteResult;
|
|
72
|
+
/** Whether one lifecycle hook is declared in a platform's file. Never throws. */
|
|
73
|
+
export declare function lifecycleHookInstalled(projectRoot: string, platform: LifecyclePlatform, which: LifecycleHook): boolean;
|
|
74
|
+
/** The line every Codex install prints: the hook is inert until the user trusts it. */
|
|
75
|
+
export declare const CODEX_TRUST_STEP: string;
|
|
76
|
+
/** The command that adds the Codex hook, for every path that does not write it. */
|
|
77
|
+
export declare const CODEX_INSTALL_COMMAND: string;
|
|
78
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,OAAO,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,OAAO,CAAC;AAEtD,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAA6B,CAAC;AAC1F,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAC7D,eAAO,MAAM,aAAa,sCAAsC,CAAC;AAuBjE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,iBAAiC,GAAG,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAM5G;AAkCD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAErF;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAElE;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAElE;AAgED,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,WAAW,CAUb;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GACzD,WAAW,CAoBb;AAED,iFAAiF;AACjF,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,aAAa,GACnB,OAAO,CAOT;AAED,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,EAAqC,MAAM,CAAC;AAEzE,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,QAA0B,CAAC"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs the lifecycle hooks: the session-start hook for Claude Code and
|
|
3
|
+
* Codex, and the opt-in phase guard for Claude Code
|
|
4
|
+
* (hooks-carry-lifecycle-state/AC-14..AC-16, AC-20, AC-21).
|
|
5
|
+
*
|
|
6
|
+
* Identity is a trailing marker, as for the cost hooks, but a different one
|
|
7
|
+
* (design D9). With a shared marker `aidlc cost hooks status` would count a
|
|
8
|
+
* lifecycle hook as a cost hook and `cost hooks remove` would delete it. The cost
|
|
9
|
+
* installer matches `endsWith('#aidlc-cost-hook')`, which no command here
|
|
10
|
+
* satisfies, so neither installer sees the other's entries and the cost code is
|
|
11
|
+
* unchanged. The merge below is written again rather than generalised out of
|
|
12
|
+
* `cost/hooks/claude-code.ts` because it also writes Codex's file, and the cost
|
|
13
|
+
* installer's tests pin its exact behaviour.
|
|
14
|
+
*
|
|
15
|
+
* Codex trusts a hook by its content hash, and only the user's own config may
|
|
16
|
+
* record that hash (knowledge: `codex-hook-trust-model`). So this module writes
|
|
17
|
+
* the project's `.codex/hooks.json` and nothing else for Codex — never
|
|
18
|
+
* `~/.codex/`, never a trust hash — and writes only when the text would change,
|
|
19
|
+
* so installing twice cannot cost the user the trust they already gave.
|
|
20
|
+
*
|
|
21
|
+
* Design: design.md C4
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { dirname, join } from 'node:path';
|
|
27
|
+
import { cliEntryPath } from '../cost/hooks/entry.js';
|
|
28
|
+
import { shimInvocation, shimProblem, writeShim } from '../cost/hooks/shim.js';
|
|
29
|
+
export const LIFECYCLE_PLATFORMS = ['claude-code', 'codex'];
|
|
30
|
+
export const LIFECYCLE_HOOK_MARKER = '#aidlc-lifecycle-hook';
|
|
31
|
+
export const GUARD_MATCHER = 'Edit|Write|MultiEdit|NotebookEdit';
|
|
32
|
+
/** A hook needs only to start the CLI and read a few files; ten seconds is generous. */
|
|
33
|
+
const TIMEOUT_SECONDS = 10;
|
|
34
|
+
const EVENT = { 'session-start': 'SessionStart', guard: 'PreToolUse' };
|
|
35
|
+
/**
|
|
36
|
+
* The two hook commands for a platform. Both run through the cost-capture shim,
|
|
37
|
+
* which finds an absolute `node` and this project's CLI at run time and never
|
|
38
|
+
* consults `PATH` (cost-capture-goes-dark/AC-15) — a bare `aidlc` here would be a
|
|
39
|
+
* `PATH` lookup on every session start (design D1).
|
|
40
|
+
*/
|
|
41
|
+
export function lifecycleCommands(platform = 'claude-code') {
|
|
42
|
+
const base = shimInvocation(PLATFORM_HOOKS[platform].rootVar);
|
|
43
|
+
return {
|
|
44
|
+
'session-start': `${base} hook session-start ${LIFECYCLE_HOOK_MARKER}`,
|
|
45
|
+
guard: `${base} hook guard ${LIFECYCLE_HOOK_MARKER}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The one table mapping a platform to its lifecycle hooks (agent-portability/AC-41). */
|
|
49
|
+
const PLATFORM_HOOKS = {
|
|
50
|
+
'claude-code': { file: ['.claude', 'settings.json'], guard: true, trustStep: null, rootVar: 'CLAUDE_PROJECT_DIR' },
|
|
51
|
+
// Codex's PreToolUse can deny or rewrite a call but not ask (design D7), and a
|
|
52
|
+
// project hook runs only once the user trusts it by hash.
|
|
53
|
+
codex: {
|
|
54
|
+
file: ['.codex', 'hooks.json'],
|
|
55
|
+
guard: false,
|
|
56
|
+
trustStep: 'Codex runs it only after you trust it: open Codex in this project and run /hooks.',
|
|
57
|
+
rootVar: 'AIDLC_PROJECT_ROOT',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
export function hookFilePath(projectRoot, platform) {
|
|
61
|
+
return join(projectRoot, ...PLATFORM_HOOKS[platform].file);
|
|
62
|
+
}
|
|
63
|
+
/** Whether the phase guard can be installed for a platform. */
|
|
64
|
+
export function supportsGuard(platform) {
|
|
65
|
+
return PLATFORM_HOOKS[platform].guard;
|
|
66
|
+
}
|
|
67
|
+
/** The step the user still owes before the platform runs the hook, or null. */
|
|
68
|
+
export function trustStep(platform) {
|
|
69
|
+
return PLATFORM_HOOKS[platform].trustStep;
|
|
70
|
+
}
|
|
71
|
+
/** Whether writing a platform's hook needs the user's explicit yes (AC-18..AC-20). */
|
|
72
|
+
export function needsConsent(platform) {
|
|
73
|
+
return trustStep(platform) !== null;
|
|
74
|
+
}
|
|
75
|
+
/** The command that writes a consent-gated platform's hook, for every path that does not. */
|
|
76
|
+
export function installCommand(platform) {
|
|
77
|
+
return `aidlc hook install --platform ${platform}`;
|
|
78
|
+
}
|
|
79
|
+
function isMarked(h) {
|
|
80
|
+
return typeof h.command === 'string' && h.command.endsWith(LIFECYCLE_HOOK_MARKER);
|
|
81
|
+
}
|
|
82
|
+
function groupFor(which, platform) {
|
|
83
|
+
const hook = { type: 'command', command: lifecycleCommands(platform)[which], timeout: TIMEOUT_SECONDS };
|
|
84
|
+
// No matcher on SessionStart, so it fires on startup, resume, clear and compact.
|
|
85
|
+
return which === 'guard' ? { matcher: GUARD_MATCHER, hooks: [hook] } : { hooks: [hook] };
|
|
86
|
+
}
|
|
87
|
+
function load(path) {
|
|
88
|
+
if (!existsSync(path))
|
|
89
|
+
return { raw: null, file: {} };
|
|
90
|
+
const raw = readFileSync(path, 'utf8');
|
|
91
|
+
// A file that does not parse is left alone: overwriting it would discard
|
|
92
|
+
// whatever the user had there. The error reaches the caller.
|
|
93
|
+
return { raw, file: JSON.parse(raw) };
|
|
94
|
+
}
|
|
95
|
+
function save(path, raw, file) {
|
|
96
|
+
const text = JSON.stringify(file, null, 2) + '\n';
|
|
97
|
+
if (text === raw)
|
|
98
|
+
return false;
|
|
99
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
100
|
+
writeFileSync(path, text);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The event's groups with every marked command taken out, and how many were.
|
|
105
|
+
* Only a group this takes a command from is dropped for being left empty; a
|
|
106
|
+
* user's group is passed through as it was, empty or not.
|
|
107
|
+
*/
|
|
108
|
+
function withoutMarked(groups) {
|
|
109
|
+
let removed = 0;
|
|
110
|
+
const kept = [];
|
|
111
|
+
for (const g of groups ?? []) {
|
|
112
|
+
const hooks = Array.isArray(g.hooks) ? g.hooks : [];
|
|
113
|
+
const unmarked = hooks.filter((h) => !isMarked(h));
|
|
114
|
+
removed += hooks.length - unmarked.length;
|
|
115
|
+
if (unmarked.length === hooks.length)
|
|
116
|
+
kept.push(g);
|
|
117
|
+
else if (unmarked.length > 0)
|
|
118
|
+
kept.push({ ...g, hooks: unmarked });
|
|
119
|
+
}
|
|
120
|
+
return { groups: kept, removed };
|
|
121
|
+
}
|
|
122
|
+
/** Exactly one marked command under the event, in the group shape the installer writes. */
|
|
123
|
+
function isCurrent(groups, which, platform) {
|
|
124
|
+
const expected = groupFor(which, platform);
|
|
125
|
+
const marked = (groups ?? []).filter((g) => (g.hooks ?? []).some(isMarked));
|
|
126
|
+
return marked.length === 1 && JSON.stringify(marked[0]) === JSON.stringify(expected);
|
|
127
|
+
}
|
|
128
|
+
function ensure(file, which, platform) {
|
|
129
|
+
const hooks = file.hooks ?? {};
|
|
130
|
+
const event = EVENT[which];
|
|
131
|
+
// Left where it is when already current, so a re-install does not even reorder
|
|
132
|
+
// the file (AC-16).
|
|
133
|
+
if (!isCurrent(hooks[event], which, platform)) {
|
|
134
|
+
hooks[event] = [...withoutMarked(hooks[event]).groups, groupFor(which, platform)];
|
|
135
|
+
}
|
|
136
|
+
file.hooks = hooks;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Install the session-start hook, and the guard when asked, into one platform's
|
|
140
|
+
* hook file (AC-15, AC-16, AC-20). Writes the shim first when it is missing or
|
|
141
|
+
* unusable, since every command here runs through it.
|
|
142
|
+
*
|
|
143
|
+
* @throws when the guard is asked for Codex — its `PreToolUse` can deny or
|
|
144
|
+
* rewrite a call but not ask, and the guard only ever asks.
|
|
145
|
+
*/
|
|
146
|
+
export function installLifecycleHooks(projectRoot, opts) {
|
|
147
|
+
if (opts.guard && !supportsGuard(opts.platform)) {
|
|
148
|
+
throw new Error(`the phase guard is Claude Code only — ${opts.platform} hooks can deny or rewrite a call, not ask`);
|
|
149
|
+
}
|
|
150
|
+
const path = hookFilePath(projectRoot, opts.platform);
|
|
151
|
+
const { raw, file } = load(path);
|
|
152
|
+
if (shimProblem(projectRoot) !== null)
|
|
153
|
+
writeShim(projectRoot, process.execPath, cliEntryPath());
|
|
154
|
+
ensure(file, 'session-start', opts.platform);
|
|
155
|
+
if (opts.guard)
|
|
156
|
+
ensure(file, 'guard', opts.platform);
|
|
157
|
+
return { written: save(path, raw, file), path };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Remove the lifecycle hooks from one platform's file, or only the guard
|
|
161
|
+
* (AC-14, AC-16). Unmarked entries and every other key stay; a file that does
|
|
162
|
+
* not exist is not created.
|
|
163
|
+
*/
|
|
164
|
+
export function removeLifecycleHooks(projectRoot, opts) {
|
|
165
|
+
const path = hookFilePath(projectRoot, opts.platform);
|
|
166
|
+
if (!existsSync(path))
|
|
167
|
+
return { written: false, path };
|
|
168
|
+
const { raw, file } = load(path);
|
|
169
|
+
if (!file.hooks)
|
|
170
|
+
return { written: false, path };
|
|
171
|
+
const events = opts.guardOnly ? [EVENT.guard] : Object.keys(file.hooks);
|
|
172
|
+
let removed = 0;
|
|
173
|
+
for (const event of events) {
|
|
174
|
+
if (!(event in file.hooks))
|
|
175
|
+
continue;
|
|
176
|
+
const result = withoutMarked(file.hooks[event]);
|
|
177
|
+
if (result.removed === 0)
|
|
178
|
+
continue;
|
|
179
|
+
removed += result.removed;
|
|
180
|
+
if (result.groups.length > 0)
|
|
181
|
+
file.hooks[event] = result.groups;
|
|
182
|
+
else
|
|
183
|
+
delete file.hooks[event];
|
|
184
|
+
}
|
|
185
|
+
// Nothing marked, nothing written: re-serialising would reformat a file this
|
|
186
|
+
// command had no business touching, and report a removal that did not happen.
|
|
187
|
+
if (removed === 0)
|
|
188
|
+
return { written: false, path };
|
|
189
|
+
if (Object.keys(file.hooks).length === 0)
|
|
190
|
+
delete file.hooks;
|
|
191
|
+
return { written: save(path, raw, file), path };
|
|
192
|
+
}
|
|
193
|
+
/** Whether one lifecycle hook is declared in a platform's file. Never throws. */
|
|
194
|
+
export function lifecycleHookInstalled(projectRoot, platform, which) {
|
|
195
|
+
try {
|
|
196
|
+
const { file } = load(hookFilePath(projectRoot, platform));
|
|
197
|
+
return (file.hooks?.[EVENT[which]] ?? []).some((g) => (g.hooks ?? []).some(isMarked));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** The line every Codex install prints: the hook is inert until the user trusts it. */
|
|
204
|
+
export const CODEX_TRUST_STEP = PLATFORM_HOOKS.codex.trustStep;
|
|
205
|
+
/** The command that adds the Codex hook, for every path that does not write it. */
|
|
206
|
+
export const CODEX_INSTALL_COMMAND = installCommand('codex');
|
|
207
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a harness hook needs to know about the lifecycle: which instance is
|
|
3
|
+
* claimed in this checkout, what phase it is in, and what comes next.
|
|
4
|
+
*
|
|
5
|
+
* Read-only by contract (hooks-carry-lifecycle-state/AC-8). Everything here runs
|
|
6
|
+
* inside a `SessionStart` or `PreToolUse` hook, on every session start or every
|
|
7
|
+
* edit, so it reads only the claims and the claimed instances' own files — never
|
|
8
|
+
* every instance, ledger or cache. `aidlc status --json` reads all of those and
|
|
9
|
+
* took 3.5 s on this repository; the CLI itself starts in 0.16 s (AC-10).
|
|
10
|
+
*
|
|
11
|
+
* Requirements: hooks-carry-lifecycle-state/AC-1..AC-9
|
|
12
|
+
* Design: design.md C2
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import type { NextStep } from '../core/types.js';
|
|
17
|
+
/** Phases in which code is not yet written — what the phase guard asks about. */
|
|
18
|
+
export declare const PLANNING_PHASES: readonly string[];
|
|
19
|
+
/** Longest line either hook prints (AC-9). */
|
|
20
|
+
export declare const MAX_LINE = 200;
|
|
21
|
+
export interface InstanceContext {
|
|
22
|
+
name: string;
|
|
23
|
+
template: string;
|
|
24
|
+
scope: string;
|
|
25
|
+
/** `current_phase` from instance.yaml — what `aidlc status --json` reports (AC-2). */
|
|
26
|
+
phase: string;
|
|
27
|
+
/** What `aidlc continue --json` reports as `nextStep` (AC-2). */
|
|
28
|
+
step: NextStep;
|
|
29
|
+
/** The lowest-numbered open task, in implementation only (AC-3). */
|
|
30
|
+
openTask: {
|
|
31
|
+
id: number;
|
|
32
|
+
title: string;
|
|
33
|
+
} | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Instances claimed in this checkout, sorted by name (AC-6).
|
|
37
|
+
*
|
|
38
|
+
* Held here means recorded from this working directory: the claim's
|
|
39
|
+
* `worktree_path` is this checkout, or it has none and this is the primary
|
|
40
|
+
* checkout. Staleness is deliberately not consulted — claims renew only at phase
|
|
41
|
+
* transitions, and one phase routinely outlasts the 60-minute window, so a
|
|
42
|
+
* live-only rule would go quiet on a compaction deep into implementation, the
|
|
43
|
+
* case this hook exists for (design D3). Session ids are not compared either:
|
|
44
|
+
* they change across `startup`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function claimsHere(projectRoot: string): string[];
|
|
47
|
+
/** `current_phase` of one instance, or null when its state cannot be read. */
|
|
48
|
+
export declare function currentPhase(projectRoot: string, name: string): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* One instance's phase and next step, or null when any of its state cannot be
|
|
51
|
+
* read (AC-7) — a hook must never stop a session starting.
|
|
52
|
+
*/
|
|
53
|
+
export declare function instanceContext(projectRoot: string, name: string): InstanceContext | null;
|
|
54
|
+
/**
|
|
55
|
+
* One printable line: control characters (escape codes included) become spaces,
|
|
56
|
+
* and anything past `max` is cut with an ellipsis (AC-9).
|
|
57
|
+
*/
|
|
58
|
+
export declare function plainLine(text: string, max?: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* What `aidlc hook session-start` prints: three lines for one claim, one line
|
|
61
|
+
* for several, nothing for none (AC-1, AC-4, AC-5). An instance whose state
|
|
62
|
+
* cannot be read is left out (AC-7).
|
|
63
|
+
*/
|
|
64
|
+
export declare function sessionStartLines(projectRoot: string): string[];
|
|
65
|
+
//# sourceMappingURL=lifecycle-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-context.d.ts","sourceRoot":"","sources":["../../src/hooks/lifecycle-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAiB,QAAQ,EAAgC,MAAM,kBAAkB,CAAC;AAQ9F,iFAAiF;AACjF,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAA2C,CAAC;AAEzF,8CAA8C;AAC9C,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,QAAQ,CAAC;IACf,oEAAoE;IACpE,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAChD;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAcxD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7E;AAiCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAmBzF;AAcD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAW,GAAG,MAAM,CAG9D;AAmDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAO/D"}
|