intentdna 1.2.2 → 1.2.3
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/audit/index.d.ts +0 -1
- package/dist/audit/index.js +0 -1
- package/dist/cli/commands/compile.d.ts +0 -1
- package/dist/cli/commands/compile.js +0 -1
- package/dist/cli/commands/epigenetic.d.ts +0 -1
- package/dist/cli/commands/epigenetic.js +0 -1
- package/dist/cli/commands/evolve.d.ts +0 -1
- package/dist/cli/commands/evolve.js +0 -1
- package/dist/cli/commands/guard.d.ts +0 -1
- package/dist/cli/commands/guard.js +0 -3
- package/dist/cli/commands/import.d.ts +0 -1
- package/dist/cli/commands/import.js +0 -1
- package/dist/cli/commands/init.d.ts +0 -1
- package/dist/cli/commands/init.js +0 -1
- package/dist/cli/commands/run.d.ts +0 -1
- package/dist/cli/commands/run.js +4 -27
- package/dist/cli/commands/show.d.ts +0 -1
- package/dist/cli/commands/show.js +0 -1
- package/dist/cli/commands/sync.d.ts +1 -1
- package/dist/cli/commands/sync.js +57 -32
- package/dist/cli/commands/validate.d.ts +0 -1
- package/dist/cli/commands/validate.js +0 -1
- package/dist/cli/commands/verify.d.ts +0 -1
- package/dist/cli/commands/verify.js +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/util/format.d.ts +0 -1
- package/dist/cli/util/format.js +0 -1
- package/dist/cli/util/prompt.d.ts +0 -1
- package/dist/cli/util/prompt.js +0 -1
- package/dist/compiler/activate.d.ts +0 -1
- package/dist/compiler/activate.js +0 -1
- package/dist/compiler/cascade.d.ts +1 -3
- package/dist/compiler/cascade.js +1 -10
- package/dist/compiler/compile.d.ts +0 -1
- package/dist/compiler/compile.js +3 -17
- package/dist/compiler/index.d.ts +0 -1
- package/dist/compiler/index.js +0 -1
- package/dist/compiler/workflow.d.ts +0 -1
- package/dist/compiler/workflow.js +0 -2
- package/dist/evolution/collector.d.ts +0 -1
- package/dist/evolution/collector.js +0 -1
- package/dist/evolution/fitness.d.ts +0 -1
- package/dist/evolution/fitness.js +0 -1
- package/dist/evolution/index.d.ts +0 -1
- package/dist/evolution/index.js +0 -1
- package/dist/evolution/marker-gen.d.ts +0 -1
- package/dist/evolution/marker-gen.js +0 -1
- package/dist/evolution/store.d.ts +0 -1
- package/dist/evolution/store.js +0 -1
- package/dist/evolution/types.d.ts +0 -1
- package/dist/evolution/types.js +0 -1
- package/dist/hooks/cli.d.ts +17 -0
- package/dist/hooks/cli.js +134 -0
- package/dist/hooks/enforce.d.ts +68 -0
- package/dist/hooks/enforce.js +332 -0
- package/dist/hooks/index.d.ts +9 -0
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/protocol.d.ts +70 -0
- package/dist/hooks/protocol.js +91 -0
- package/dist/hooks/state.d.ts +53 -0
- package/dist/hooks/state.js +93 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/log/logger.d.ts +0 -1
- package/dist/log/logger.js +0 -1
- package/dist/runtime/agent-md.d.ts +0 -1
- package/dist/runtime/agent-md.js +95 -105
- package/dist/runtime/claude-sdk.d.ts +0 -1
- package/dist/runtime/claude-sdk.js +0 -1
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/markdown.d.ts +0 -1
- package/dist/runtime/markdown.js +0 -1
- package/dist/runtime/plugin-adapter.d.ts +42 -0
- package/dist/runtime/plugin-adapter.js +77 -0
- package/dist/runtime/settings-adapter.d.ts +8 -9
- package/dist/runtime/settings-adapter.js +57 -46
- package/dist/runtime/skill-adapter.d.ts +0 -1
- package/dist/runtime/skill-adapter.js +94 -71
- package/dist/runtime/workflow-runner.d.ts +0 -1
- package/dist/runtime/workflow-runner.js +0 -14
- package/dist/schema/index.d.ts +0 -1
- package/dist/schema/index.js +0 -1
- package/dist/schema/types.d.ts +14 -20
- package/dist/schema/types.js +0 -1
- package/dist/schema/validate.d.ts +0 -1
- package/dist/schema/validate.js +3 -19
- package/dist/schema/yaml-parser.d.ts +0 -1
- package/dist/schema/yaml-parser.js +0 -1
- package/dist/templates/code-review-pipeline.dna.yaml +0 -11
- package/dist/templates/flutter-behavior-lock.dna.yaml +0 -16
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +0 -12
- package/dist/templates/flutter-rewrite.dna.yaml +0 -27
- package/dist/templates/full-pipeline.dna.yaml +0 -25
- package/dist/templates/multi-perspective-review.dna.yaml +0 -12
- package/dist/templates/subagent-parallel.dna.yaml +0 -16
- package/package.json +3 -2
- package/dist/audit/index.d.ts.map +0 -1
- package/dist/audit/index.js.map +0 -1
- package/dist/cli/commands/compile.d.ts.map +0 -1
- package/dist/cli/commands/compile.js.map +0 -1
- package/dist/cli/commands/epigenetic.d.ts.map +0 -1
- package/dist/cli/commands/epigenetic.js.map +0 -1
- package/dist/cli/commands/evolve.d.ts.map +0 -1
- package/dist/cli/commands/evolve.js.map +0 -1
- package/dist/cli/commands/guard.d.ts.map +0 -1
- package/dist/cli/commands/guard.js.map +0 -1
- package/dist/cli/commands/import.d.ts.map +0 -1
- package/dist/cli/commands/import.js.map +0 -1
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/run.d.ts.map +0 -1
- package/dist/cli/commands/run.js.map +0 -1
- package/dist/cli/commands/show.d.ts.map +0 -1
- package/dist/cli/commands/show.js.map +0 -1
- package/dist/cli/commands/sync.d.ts.map +0 -1
- package/dist/cli/commands/sync.js.map +0 -1
- package/dist/cli/commands/validate.d.ts.map +0 -1
- package/dist/cli/commands/validate.js.map +0 -1
- package/dist/cli/commands/verify.d.ts.map +0 -1
- package/dist/cli/commands/verify.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/util/format.d.ts.map +0 -1
- package/dist/cli/util/format.js.map +0 -1
- package/dist/cli/util/prompt.d.ts.map +0 -1
- package/dist/cli/util/prompt.js.map +0 -1
- package/dist/compiler/activate.d.ts.map +0 -1
- package/dist/compiler/activate.js.map +0 -1
- package/dist/compiler/cascade.d.ts.map +0 -1
- package/dist/compiler/cascade.js.map +0 -1
- package/dist/compiler/compile.d.ts.map +0 -1
- package/dist/compiler/compile.js.map +0 -1
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/index.js.map +0 -1
- package/dist/compiler/workflow.d.ts.map +0 -1
- package/dist/compiler/workflow.js.map +0 -1
- package/dist/evolution/collector.d.ts.map +0 -1
- package/dist/evolution/collector.js.map +0 -1
- package/dist/evolution/fitness.d.ts.map +0 -1
- package/dist/evolution/fitness.js.map +0 -1
- package/dist/evolution/index.d.ts.map +0 -1
- package/dist/evolution/index.js.map +0 -1
- package/dist/evolution/marker-gen.d.ts.map +0 -1
- package/dist/evolution/marker-gen.js.map +0 -1
- package/dist/evolution/store.d.ts.map +0 -1
- package/dist/evolution/store.js.map +0 -1
- package/dist/evolution/types.d.ts.map +0 -1
- package/dist/evolution/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/log/logger.d.ts.map +0 -1
- package/dist/log/logger.js.map +0 -1
- package/dist/runtime/agent-md.d.ts.map +0 -1
- package/dist/runtime/agent-md.js.map +0 -1
- package/dist/runtime/claude-sdk.d.ts.map +0 -1
- package/dist/runtime/claude-sdk.js.map +0 -1
- package/dist/runtime/hooks.d.ts +0 -67
- package/dist/runtime/hooks.d.ts.map +0 -1
- package/dist/runtime/hooks.js +0 -785
- package/dist/runtime/hooks.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/markdown.d.ts.map +0 -1
- package/dist/runtime/markdown.js.map +0 -1
- package/dist/runtime/settings-adapter.d.ts.map +0 -1
- package/dist/runtime/settings-adapter.js.map +0 -1
- package/dist/runtime/skill-adapter.d.ts.map +0 -1
- package/dist/runtime/skill-adapter.js.map +0 -1
- package/dist/runtime/workflow-runner.d.ts.map +0 -1
- package/dist/runtime/workflow-runner.js.map +0 -1
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js.map +0 -1
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js.map +0 -1
- package/dist/schema/validate.d.ts.map +0 -1
- package/dist/schema/validate.js.map +0 -1
- package/dist/schema/yaml-parser.d.ts.map +0 -1
- package/dist/schema/yaml-parser.js.map +0 -1
package/dist/evolution/types.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Intent DNA — Hook CLI (`dna-hook`)
|
|
4
|
+
*
|
|
5
|
+
* Claude Code invokes this binary for each hook event:
|
|
6
|
+
* dna-hook <event> [--ir <path>] [--state-dir <path>]
|
|
7
|
+
*
|
|
8
|
+
* Runtime flow:
|
|
9
|
+
* 1. Read JSON from stdin (Claude Code hook protocol)
|
|
10
|
+
* 2. Load compiled IR from .dna/compiled/ir.json
|
|
11
|
+
* 3. Read state from .dna/state/
|
|
12
|
+
* 4. Call enforcement engine
|
|
13
|
+
* 5. Write JSON result to stdout
|
|
14
|
+
*
|
|
15
|
+
* Fail-open: all errors → { continue: true, suppressOutput: true }
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Intent DNA — Hook CLI (`dna-hook`)
|
|
4
|
+
*
|
|
5
|
+
* Claude Code invokes this binary for each hook event:
|
|
6
|
+
* dna-hook <event> [--ir <path>] [--state-dir <path>]
|
|
7
|
+
*
|
|
8
|
+
* Runtime flow:
|
|
9
|
+
* 1. Read JSON from stdin (Claude Code hook protocol)
|
|
10
|
+
* 2. Load compiled IR from .dna/compiled/ir.json
|
|
11
|
+
* 3. Read state from .dna/state/
|
|
12
|
+
* 4. Call enforcement engine
|
|
13
|
+
* 5. Write JSON result to stdout
|
|
14
|
+
*
|
|
15
|
+
* Fail-open: all errors → { continue: true, suppressOutput: true }
|
|
16
|
+
*/
|
|
17
|
+
import { readFile } from "node:fs/promises";
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
import { readStdin, writeOutput, silentOutput } from "./protocol.js";
|
|
20
|
+
import { enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, } from "./enforce.js";
|
|
21
|
+
import { appendAudit } from "./state.js";
|
|
22
|
+
// ── Constants ──────────────────────────────────────────────
|
|
23
|
+
const DEFAULT_IR_PATH = ".dna/compiled/ir.json";
|
|
24
|
+
const VALID_EVENTS = new Set([
|
|
25
|
+
"PreToolUse", "PostToolUse", "UserPromptSubmit",
|
|
26
|
+
"SubagentStop", "PreCompact", "Notification", "Stop", "SessionStart",
|
|
27
|
+
]);
|
|
28
|
+
// ── Main ───────────────────────────────────────────────────
|
|
29
|
+
async function main() {
|
|
30
|
+
// Parse args
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const event = args[0];
|
|
33
|
+
if (!event || !VALID_EVENTS.has(event)) {
|
|
34
|
+
writeOutput(silentOutput());
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let irPath = DEFAULT_IR_PATH;
|
|
38
|
+
const irFlagIdx = args.indexOf("--ir");
|
|
39
|
+
if (irFlagIdx !== -1 && args[irFlagIdx + 1]) {
|
|
40
|
+
irPath = args[irFlagIdx + 1];
|
|
41
|
+
}
|
|
42
|
+
// Read stdin
|
|
43
|
+
const rawInput = await readStdin(5000);
|
|
44
|
+
// Resolve project directory from input or cwd
|
|
45
|
+
const projectDir = typeof rawInput.cwd === "string" ? rawInput.cwd : process.cwd();
|
|
46
|
+
const sessionId = typeof rawInput.sessionId === "string" ? rawInput.sessionId : undefined;
|
|
47
|
+
// Load compiled IR
|
|
48
|
+
const resolvedIRPath = resolve(projectDir, irPath);
|
|
49
|
+
const ir = await loadIR(resolvedIRPath);
|
|
50
|
+
if (!ir) {
|
|
51
|
+
writeOutput(silentOutput());
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const state = {};
|
|
55
|
+
// Dispatch to enforcement engine
|
|
56
|
+
const output = dispatch(event, ir, rawInput, state);
|
|
57
|
+
writeOutput(output);
|
|
58
|
+
// Side effect: audit log for notifications
|
|
59
|
+
if (event === "Notification" && output.hookSpecificOutput?.additionalContext?.includes("violation")) {
|
|
60
|
+
const message = typeof rawInput.message === "string" ? rawInput.message : "";
|
|
61
|
+
await appendAudit(projectDir, {
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
event: "violation",
|
|
64
|
+
message,
|
|
65
|
+
session_id: sessionId,
|
|
66
|
+
}).catch(() => { }); // Fail-open
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ── Dispatch ───────────────────────────────────────────────
|
|
70
|
+
function dispatch(event, ir, input, state) {
|
|
71
|
+
switch (event) {
|
|
72
|
+
case "PreToolUse":
|
|
73
|
+
return enforcePreToolUse(ir, {
|
|
74
|
+
tool_name: String(input.tool_name ?? ""),
|
|
75
|
+
tool_input: (input.tool_input ?? {}),
|
|
76
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
77
|
+
cwd: typeof input.cwd === "string" ? input.cwd : undefined,
|
|
78
|
+
sessionId: typeof input.sessionId === "string" ? input.sessionId : undefined,
|
|
79
|
+
}, state);
|
|
80
|
+
case "PostToolUse":
|
|
81
|
+
return enforcePostToolUse(ir, {
|
|
82
|
+
tool_name: String(input.tool_name ?? ""),
|
|
83
|
+
tool_input: (input.tool_input ?? {}),
|
|
84
|
+
tool_output: typeof input.tool_output === "string" ? input.tool_output : undefined,
|
|
85
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
86
|
+
});
|
|
87
|
+
case "UserPromptSubmit":
|
|
88
|
+
return enforceUserPromptSubmit(ir, {
|
|
89
|
+
prompt: typeof input.prompt === "string" ? input.prompt : undefined,
|
|
90
|
+
});
|
|
91
|
+
case "SubagentStop":
|
|
92
|
+
return enforceSubagentStop(ir, {
|
|
93
|
+
agent_name: typeof input.agent_name === "string" ? input.agent_name : undefined,
|
|
94
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
95
|
+
});
|
|
96
|
+
case "PreCompact":
|
|
97
|
+
return enforcePreCompact(ir);
|
|
98
|
+
case "Notification":
|
|
99
|
+
return enforceNotification(ir, {
|
|
100
|
+
title: typeof input.title === "string" ? input.title : undefined,
|
|
101
|
+
message: typeof input.message === "string" ? input.message : undefined,
|
|
102
|
+
});
|
|
103
|
+
case "Stop":
|
|
104
|
+
return silentOutput(); // DNA doesn't block stops
|
|
105
|
+
case "SessionStart":
|
|
106
|
+
return silentOutput(); // Placeholder — will initialize DNA state in future
|
|
107
|
+
default:
|
|
108
|
+
return silentOutput();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ── IR Loading ─────────────────────────────────────────────
|
|
112
|
+
async function loadIR(irPath) {
|
|
113
|
+
try {
|
|
114
|
+
const raw = await readFile(irPath, "utf-8");
|
|
115
|
+
const data = JSON.parse(raw);
|
|
116
|
+
// Support both raw IR and wrapped CompiledIRFile format
|
|
117
|
+
if (data.ir_version && data.ir) {
|
|
118
|
+
return data.ir;
|
|
119
|
+
}
|
|
120
|
+
// Direct ConstraintIR
|
|
121
|
+
if (data.compiled_at && data.source_dna_ids) {
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Entry Point ────────────────────────────────────────────
|
|
131
|
+
main().catch(() => {
|
|
132
|
+
// Fail-open: never block Claude Code on unexpected errors
|
|
133
|
+
writeOutput(silentOutput());
|
|
134
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Enforcement Engine
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that evaluate ConstraintIR against hook inputs.
|
|
5
|
+
* No I/O — reads no files, runs no commands, writes no state.
|
|
6
|
+
* The hook CLI handles stdin/stdout, IR loading, state, and checkpoint execution.
|
|
7
|
+
*
|
|
8
|
+
* Enforcement order for PreToolUse:
|
|
9
|
+
* 1. Role scope (agent_type → write glob patterns)
|
|
10
|
+
* 2. Tool filters (block/warn/deprioritize by tool name)
|
|
11
|
+
* 3. Pre-execution gates (condition-based block/escalate)
|
|
12
|
+
* 4. Output schema (required sections for Write tool)
|
|
13
|
+
*
|
|
14
|
+
* Step checkpoints are handled by the CLI, not here.
|
|
15
|
+
*/
|
|
16
|
+
import type { ConstraintIR, RoleDef } from "../schema/types.js";
|
|
17
|
+
import type { PreToolUseInput, PostToolUseInput, UserPromptSubmitInput, SubagentStopInput, NotificationInput, HookOutput } from "./protocol.js";
|
|
18
|
+
export interface EnforceState {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Enforce PreToolUse constraints.
|
|
22
|
+
* Returns the first blocking/escalating result, or allow if all pass.
|
|
23
|
+
*
|
|
24
|
+
* Optional `roles` parameter provides output_schema enforcement.
|
|
25
|
+
*/
|
|
26
|
+
export declare function enforcePreToolUse(ir: ConstraintIR, input: PreToolUseInput, state?: EnforceState, roles?: Record<string, RoleDef>): HookOutput;
|
|
27
|
+
/**
|
|
28
|
+
* Enforce PostToolUse validators.
|
|
29
|
+
* Currently logs validators for audit. Returns allow with context.
|
|
30
|
+
*/
|
|
31
|
+
export declare function enforcePostToolUse(ir: ConstraintIR, input: PostToolUseInput): HookOutput;
|
|
32
|
+
/**
|
|
33
|
+
* Inject DNA policy reminders on each user prompt.
|
|
34
|
+
* Only injects when there are high-priority directives.
|
|
35
|
+
*/
|
|
36
|
+
export declare function enforceUserPromptSubmit(ir: ConstraintIR, _input: UserPromptSubmitInput): HookOutput;
|
|
37
|
+
/**
|
|
38
|
+
* Audit role scope compliance when a subagent finishes.
|
|
39
|
+
*/
|
|
40
|
+
export declare function enforceSubagentStop(ir: ConstraintIR, input: SubagentStopInput): HookOutput;
|
|
41
|
+
/**
|
|
42
|
+
* Preserve DNA directive summary before context compaction.
|
|
43
|
+
*/
|
|
44
|
+
export declare function enforcePreCompact(ir: ConstraintIR): HookOutput;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a notification is a DNA violation and return audit info.
|
|
47
|
+
* The CLI handles actual audit file writes.
|
|
48
|
+
*/
|
|
49
|
+
export declare function enforceNotification(ir: ConstraintIR, input: NotificationInput): HookOutput;
|
|
50
|
+
/** Check if a file path is allowed by a list of write globs (prefix matching). */
|
|
51
|
+
export declare function checkWriteAllowed(filePath: string, allowedGlobs: string[]): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Extract directory prefix from a glob pattern.
|
|
54
|
+
* "src/**\/*.ts" → "src/"
|
|
55
|
+
* "test/**" → "test/"
|
|
56
|
+
* "*.md" → "" (root)
|
|
57
|
+
* "package.json" → "package.json" (exact path)
|
|
58
|
+
*/
|
|
59
|
+
export declare function globToPrefix(glob: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Evaluate a gate condition against a PreToolUse input.
|
|
62
|
+
* Returns true if the condition triggers, false if not, null if too complex.
|
|
63
|
+
*/
|
|
64
|
+
export declare function evaluateGateCondition(condition: string, input: PreToolUseInput): boolean | null;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a tool filter target to concrete Claude Code tool names.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveToolTarget(target?: string): string[] | null;
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Enforcement Engine
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that evaluate ConstraintIR against hook inputs.
|
|
5
|
+
* No I/O — reads no files, runs no commands, writes no state.
|
|
6
|
+
* The hook CLI handles stdin/stdout, IR loading, state, and checkpoint execution.
|
|
7
|
+
*
|
|
8
|
+
* Enforcement order for PreToolUse:
|
|
9
|
+
* 1. Role scope (agent_type → write glob patterns)
|
|
10
|
+
* 2. Tool filters (block/warn/deprioritize by tool name)
|
|
11
|
+
* 3. Pre-execution gates (condition-based block/escalate)
|
|
12
|
+
* 4. Output schema (required sections for Write tool)
|
|
13
|
+
*
|
|
14
|
+
* Step checkpoints are handled by the CLI, not here.
|
|
15
|
+
*/
|
|
16
|
+
import { allowOutput, blockOutput, escalateOutput, silentOutput } from "./protocol.js";
|
|
17
|
+
// ── PreToolUse Enforcement ─────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Enforce PreToolUse constraints.
|
|
20
|
+
* Returns the first blocking/escalating result, or allow if all pass.
|
|
21
|
+
*
|
|
22
|
+
* Optional `roles` parameter provides output_schema enforcement.
|
|
23
|
+
*/
|
|
24
|
+
export function enforcePreToolUse(ir, input, state, roles) {
|
|
25
|
+
// Layer 2: Role scope
|
|
26
|
+
if (input.agent_type && ir.roles_scope_map && ir.roles_scope_map.length > 0) {
|
|
27
|
+
const result = enforceRoleScope(ir.roles_scope_map, input);
|
|
28
|
+
if (result)
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
// Layer 3: Tool filters
|
|
32
|
+
if (ir.tool_filters.length > 0) {
|
|
33
|
+
const result = enforceToolFilters(ir.tool_filters, input);
|
|
34
|
+
if (result)
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
// Layer 4: Pre-execution gates
|
|
38
|
+
if (ir.pre_execution_gates.length > 0) {
|
|
39
|
+
const result = enforceGates(ir.pre_execution_gates, input);
|
|
40
|
+
if (result)
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
// Layer 5: Output schema
|
|
44
|
+
if (roles && input.tool_name === "Write") {
|
|
45
|
+
const result = enforceOutputSchema(roles, input);
|
|
46
|
+
if (result)
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
return silentOutput();
|
|
50
|
+
}
|
|
51
|
+
// ── PostToolUse Enforcement ────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Enforce PostToolUse validators.
|
|
54
|
+
* Currently logs validators for audit. Returns allow with context.
|
|
55
|
+
*/
|
|
56
|
+
export function enforcePostToolUse(ir, input) {
|
|
57
|
+
const validators = ir.post_execution_validators;
|
|
58
|
+
if (validators.length === 0)
|
|
59
|
+
return silentOutput();
|
|
60
|
+
const lines = [];
|
|
61
|
+
for (const v of validators) {
|
|
62
|
+
lines.push(`[Intent DNA] PostToolUse validator: ${v.check} (gene: ${v.source_gene})`);
|
|
63
|
+
}
|
|
64
|
+
return allowOutput(lines.join("\n"));
|
|
65
|
+
}
|
|
66
|
+
// ── UserPromptSubmit Enforcement ───────────────────────────
|
|
67
|
+
/**
|
|
68
|
+
* Inject DNA policy reminders on each user prompt.
|
|
69
|
+
* Only injects when there are high-priority directives.
|
|
70
|
+
*/
|
|
71
|
+
export function enforceUserPromptSubmit(ir, _input) {
|
|
72
|
+
const highPriority = ir.prompt_directives.filter(d => d.priority === "high");
|
|
73
|
+
if (highPriority.length === 0)
|
|
74
|
+
return silentOutput();
|
|
75
|
+
const lines = [
|
|
76
|
+
"<system-reminder>[Intent DNA] Active high-priority policies:",
|
|
77
|
+
];
|
|
78
|
+
for (const d of highPriority) {
|
|
79
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
80
|
+
}
|
|
81
|
+
lines.push("</system-reminder>");
|
|
82
|
+
return allowOutput(lines.join("\n"));
|
|
83
|
+
}
|
|
84
|
+
// ── SubagentStop Enforcement ───────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Audit role scope compliance when a subagent finishes.
|
|
87
|
+
*/
|
|
88
|
+
export function enforceSubagentStop(ir, input) {
|
|
89
|
+
const rolesScopeMap = ir.roles_scope_map;
|
|
90
|
+
if (!rolesScopeMap || rolesScopeMap.length === 0)
|
|
91
|
+
return silentOutput();
|
|
92
|
+
if (!input.agent_type)
|
|
93
|
+
return silentOutput();
|
|
94
|
+
for (const entry of rolesScopeMap) {
|
|
95
|
+
const agentTypeName = `dna-${toKebabCase(entry.role_name)}`;
|
|
96
|
+
if (input.agent_type === agentTypeName) {
|
|
97
|
+
const writeGlobs = entry.scope.write ?? [];
|
|
98
|
+
const scopeDesc = writeGlobs.length > 0
|
|
99
|
+
? `write:[${writeGlobs.join(",")}]`
|
|
100
|
+
: "write:[]";
|
|
101
|
+
return allowOutput(`[Intent DNA] SubagentStop audit: role=${entry.role_name} agent=${input.agent_name ?? "unknown"} scope=${scopeDesc}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return allowOutput(`[Intent DNA] SubagentStop: unknown role for agent_type=${input.agent_type}`);
|
|
105
|
+
}
|
|
106
|
+
// ── PreCompact Enforcement ─────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Preserve DNA directive summary before context compaction.
|
|
109
|
+
*/
|
|
110
|
+
export function enforcePreCompact(ir) {
|
|
111
|
+
if (ir.prompt_directives.length === 0)
|
|
112
|
+
return silentOutput();
|
|
113
|
+
const critical = ir.prompt_directives.filter(d => d.priority === "high");
|
|
114
|
+
const standard = ir.prompt_directives.filter(d => d.priority === "medium");
|
|
115
|
+
const lines = [
|
|
116
|
+
"[Intent DNA] PreCompact: Preserving directive summary for compact",
|
|
117
|
+
];
|
|
118
|
+
if (critical.length > 0) {
|
|
119
|
+
lines.push(" Critical directives:");
|
|
120
|
+
for (const d of critical) {
|
|
121
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (standard.length > 0) {
|
|
125
|
+
lines.push(` Standard directives (${standard.length}):`);
|
|
126
|
+
for (const d of standard) {
|
|
127
|
+
lines.push(` - [${d.source_gene}] ${d.text}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return allowOutput(lines.join("\n"));
|
|
131
|
+
}
|
|
132
|
+
// ── Notification Enforcement ───────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Check if a notification is a DNA violation and return audit info.
|
|
135
|
+
* The CLI handles actual audit file writes.
|
|
136
|
+
*/
|
|
137
|
+
export function enforceNotification(ir, input) {
|
|
138
|
+
const blockGates = ir.pre_execution_gates.filter(g => g.action === "block" || g.action === "escalate");
|
|
139
|
+
const blockFilters = ir.tool_filters.filter(f => f.action === "remove");
|
|
140
|
+
if (blockGates.length === 0 && blockFilters.length === 0)
|
|
141
|
+
return silentOutput();
|
|
142
|
+
const message = input.message ?? "";
|
|
143
|
+
if (message.includes("Intent DNA")) {
|
|
144
|
+
return allowOutput(`[Intent DNA] Notification: violation detected — ${input.title ?? "untitled"}: ${message}`);
|
|
145
|
+
}
|
|
146
|
+
return silentOutput();
|
|
147
|
+
}
|
|
148
|
+
// ── Internal: Layer Enforcement ────────────────────────────
|
|
149
|
+
function enforceRoleScope(rolesScopeMap, input) {
|
|
150
|
+
if (!input.agent_type)
|
|
151
|
+
return null;
|
|
152
|
+
if (!isWriteTool(input.tool_name))
|
|
153
|
+
return null;
|
|
154
|
+
const filePath = extractFilePath(input);
|
|
155
|
+
if (!filePath)
|
|
156
|
+
return null;
|
|
157
|
+
for (const entry of rolesScopeMap) {
|
|
158
|
+
const agentTypeName = `dna-${toKebabCase(entry.role_name)}`;
|
|
159
|
+
if (input.agent_type !== agentTypeName)
|
|
160
|
+
continue;
|
|
161
|
+
const writeGlobs = entry.scope.write ?? [];
|
|
162
|
+
if (writeGlobs.length === 0) {
|
|
163
|
+
return blockOutput(`[Intent DNA]: Role '${entry.role_name}' has no write permission (path: ${filePath})`);
|
|
164
|
+
}
|
|
165
|
+
if (!checkWriteAllowed(filePath, writeGlobs)) {
|
|
166
|
+
return blockOutput(`[Intent DNA]: Role '${entry.role_name}' cannot write to '${filePath}' (allowed: ${writeGlobs.join(", ")})`);
|
|
167
|
+
}
|
|
168
|
+
return null; // Role matched, write allowed
|
|
169
|
+
}
|
|
170
|
+
return null; // No matching role — allow
|
|
171
|
+
}
|
|
172
|
+
function enforceToolFilters(filters, input) {
|
|
173
|
+
for (const filter of filters) {
|
|
174
|
+
const matchedTools = resolveToolTarget(filter.target);
|
|
175
|
+
if (!matchedTools)
|
|
176
|
+
continue;
|
|
177
|
+
if (!matchedTools.includes(input.tool_name))
|
|
178
|
+
continue;
|
|
179
|
+
if (filter.action === "remove") {
|
|
180
|
+
return blockOutput(`[Intent DNA]: ${filter.reason} (gene: ${filter.source_gene})`);
|
|
181
|
+
}
|
|
182
|
+
if (filter.action === "warn") {
|
|
183
|
+
// Warn but don't block — inject context
|
|
184
|
+
return allowOutput(`WARN [Intent DNA]: ${filter.reason} (gene: ${filter.source_gene})`);
|
|
185
|
+
}
|
|
186
|
+
// deprioritize — log only
|
|
187
|
+
// Don't return, continue checking other filters
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
function enforceGates(gates, input) {
|
|
192
|
+
for (const gate of gates) {
|
|
193
|
+
const matches = evaluateGateCondition(gate.condition, input);
|
|
194
|
+
if (matches === null)
|
|
195
|
+
continue; // Condition too complex — skip
|
|
196
|
+
if (!matches)
|
|
197
|
+
continue; // Condition not triggered
|
|
198
|
+
if (gate.action === "block") {
|
|
199
|
+
return blockOutput(`[Intent DNA]: ${gate.message}`);
|
|
200
|
+
}
|
|
201
|
+
if (gate.action === "escalate") {
|
|
202
|
+
return escalateOutput(`[Intent DNA] ${gate.message}`);
|
|
203
|
+
}
|
|
204
|
+
// warn
|
|
205
|
+
return allowOutput(`WARN [Intent DNA]: ${gate.message}`);
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
function enforceOutputSchema(roles, input) {
|
|
210
|
+
if (!input.agent_type)
|
|
211
|
+
return null;
|
|
212
|
+
const content = input.tool_input.content;
|
|
213
|
+
if (typeof content !== "string" || !content)
|
|
214
|
+
return null;
|
|
215
|
+
for (const [roleName, role] of Object.entries(roles)) {
|
|
216
|
+
if (!role.output_schema || typeof role.output_schema === "string")
|
|
217
|
+
continue;
|
|
218
|
+
const schema = role.output_schema;
|
|
219
|
+
if (!schema.required_sections || schema.required_sections.length === 0)
|
|
220
|
+
continue;
|
|
221
|
+
const agentName = `dna-${toKebabCase(roleName)}`;
|
|
222
|
+
if (input.agent_type !== agentName)
|
|
223
|
+
continue;
|
|
224
|
+
for (const section of schema.required_sections) {
|
|
225
|
+
if (!content.includes(`## ${section}`)) {
|
|
226
|
+
return blockOutput(`[Intent DNA]: Output missing required section '${section}' (role: ${roleName})`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
// ── Public Helpers ──────────────────────────────────────────
|
|
233
|
+
/** Check if a file path is allowed by a list of write globs (prefix matching). */
|
|
234
|
+
export function checkWriteAllowed(filePath, allowedGlobs) {
|
|
235
|
+
for (const glob of allowedGlobs) {
|
|
236
|
+
const prefix = globToPrefix(glob);
|
|
237
|
+
if (prefix.length === 0)
|
|
238
|
+
return true; // Root glob like "*.md" — allow all
|
|
239
|
+
if (filePath.startsWith(prefix))
|
|
240
|
+
return true;
|
|
241
|
+
// Also check exact match for non-glob patterns
|
|
242
|
+
if (!glob.includes("*") && filePath === glob)
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Extract directory prefix from a glob pattern.
|
|
249
|
+
* "src/**\/*.ts" → "src/"
|
|
250
|
+
* "test/**" → "test/"
|
|
251
|
+
* "*.md" → "" (root)
|
|
252
|
+
* "package.json" → "package.json" (exact path)
|
|
253
|
+
*/
|
|
254
|
+
export function globToPrefix(glob) {
|
|
255
|
+
const starIdx = glob.indexOf("*");
|
|
256
|
+
if (starIdx === -1)
|
|
257
|
+
return glob; // exact path
|
|
258
|
+
return glob.substring(0, starIdx);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Evaluate a gate condition against a PreToolUse input.
|
|
262
|
+
* Returns true if the condition triggers, false if not, null if too complex.
|
|
263
|
+
*/
|
|
264
|
+
export function evaluateGateCondition(condition, input) {
|
|
265
|
+
// Pattern: "personal_data_exposure == false"
|
|
266
|
+
// → check if tool input contains sensitive patterns
|
|
267
|
+
const boolFalseMatch = condition.match(/^(\w+)\s*==\s*false$/);
|
|
268
|
+
if (boolFalseMatch) {
|
|
269
|
+
const metric = boolFalseMatch[1];
|
|
270
|
+
if (metric === "personal_data_exposure") {
|
|
271
|
+
const inputStr = JSON.stringify(input.tool_input).toLowerCase();
|
|
272
|
+
const sensitivePatterns = ["password", "secret", "token", "api_key", "ssn", "credit_card"];
|
|
273
|
+
return sensitivePatterns.some(p => inputStr.includes(p));
|
|
274
|
+
}
|
|
275
|
+
return null; // Unknown boolean metric
|
|
276
|
+
}
|
|
277
|
+
// Pattern: "tool_name in ['X', 'Y']"
|
|
278
|
+
const toolInMatch = condition.match(/^tool_name\s+in\s+\[([^\]]+)\]/);
|
|
279
|
+
if (toolInMatch) {
|
|
280
|
+
const tools = toolInMatch[1]
|
|
281
|
+
.split(",")
|
|
282
|
+
.map(t => t.trim().replace(/['"]/g, ""));
|
|
283
|
+
return tools.includes(input.tool_name);
|
|
284
|
+
}
|
|
285
|
+
return null; // Too complex
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Resolve a tool filter target to concrete Claude Code tool names.
|
|
289
|
+
*/
|
|
290
|
+
export function resolveToolTarget(target) {
|
|
291
|
+
if (!target)
|
|
292
|
+
return null;
|
|
293
|
+
const toolMappings = {
|
|
294
|
+
file_modification: ["Edit", "Write", "NotebookEdit"],
|
|
295
|
+
file_write: ["Write"],
|
|
296
|
+
file_edit: ["Edit"],
|
|
297
|
+
code_execution: ["Bash"],
|
|
298
|
+
shell: ["Bash"],
|
|
299
|
+
harmful_actions: ["Bash"],
|
|
300
|
+
unsafe_operations: ["Bash"],
|
|
301
|
+
data_sharing_external: ["WebFetch", "Bash"],
|
|
302
|
+
};
|
|
303
|
+
const tools = toolMappings[target];
|
|
304
|
+
if (tools)
|
|
305
|
+
return tools;
|
|
306
|
+
// PascalCase target → direct tool name
|
|
307
|
+
if (/^[A-Z][a-zA-Z]+$/.test(target))
|
|
308
|
+
return [target];
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
// ── Internal Helpers ───────────────────────────────────────
|
|
312
|
+
const WRITE_TOOLS = new Set(["Edit", "Write", "NotebookEdit"]);
|
|
313
|
+
function isWriteTool(toolName) {
|
|
314
|
+
return WRITE_TOOLS.has(toolName);
|
|
315
|
+
}
|
|
316
|
+
function extractFilePath(input) {
|
|
317
|
+
const fp = input.tool_input.file_path;
|
|
318
|
+
if (typeof fp === "string" && fp)
|
|
319
|
+
return fp;
|
|
320
|
+
// Bash tool — try to extract from command
|
|
321
|
+
const cmd = input.tool_input.command;
|
|
322
|
+
if (typeof cmd === "string")
|
|
323
|
+
return null; // Can't reliably extract
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
/** Convert string to kebab-case (matches agent-md.ts toKebabCase). */
|
|
327
|
+
function toKebabCase(s) {
|
|
328
|
+
return s
|
|
329
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
330
|
+
.replace(/[_\s]+/g, "-")
|
|
331
|
+
.toLowerCase();
|
|
332
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Node.js Hook Runtime
|
|
3
|
+
*
|
|
4
|
+
* Public exports for the hook enforcement engine.
|
|
5
|
+
* Used by the `dna-hook` CLI and importable for programmatic use.
|
|
6
|
+
*/
|
|
7
|
+
export { type HookEvent, type HookInput, type HookOutput, type HookInputBase, type PreToolUseInput, type PostToolUseInput, type UserPromptSubmitInput, type SubagentStopInput, type PreCompactInput, type NotificationInput, type StopInput, readStdin, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
8
|
+
export { type EnforceState, enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
9
|
+
export { type DNAWorkflowState, type AuditEntry, resolveStateDir, readWorkflowState, writeWorkflowState, clearWorkflowState, appendAudit, } from "./state.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Node.js Hook Runtime
|
|
3
|
+
*
|
|
4
|
+
* Public exports for the hook enforcement engine.
|
|
5
|
+
* Used by the `dna-hook` CLI and importable for programmatic use.
|
|
6
|
+
*/
|
|
7
|
+
// Protocol types and I/O
|
|
8
|
+
export { readStdin, writeOutput, allowOutput, blockOutput, escalateOutput, silentOutput, } from "./protocol.js";
|
|
9
|
+
// Enforcement engine
|
|
10
|
+
export { enforcePreToolUse, enforcePostToolUse, enforceUserPromptSubmit, enforceSubagentStop, enforcePreCompact, enforceNotification, checkWriteAllowed, globToPrefix, evaluateGateCondition, resolveToolTarget, } from "./enforce.js";
|
|
11
|
+
// State management
|
|
12
|
+
export { resolveStateDir, readWorkflowState, writeWorkflowState, clearWorkflowState, appendAudit, } from "./state.js";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent DNA — Hook Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript types for Claude Code hook I/O and stdin/stdout utilities.
|
|
5
|
+
* All hooks read JSON from stdin and write JSON to stdout.
|
|
6
|
+
*
|
|
7
|
+
* Protocol reference: docs/references/cc-source-analysis.md §2
|
|
8
|
+
*/
|
|
9
|
+
export type HookEvent = "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "SubagentStop" | "PreCompact" | "Notification" | "Stop" | "SessionStart";
|
|
10
|
+
/** Base fields present in all hook inputs */
|
|
11
|
+
export interface HookInputBase {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PreToolUseInput extends HookInputBase {
|
|
16
|
+
tool_name: string;
|
|
17
|
+
tool_input: Record<string, unknown>;
|
|
18
|
+
agent_type?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PostToolUseInput extends HookInputBase {
|
|
21
|
+
tool_name: string;
|
|
22
|
+
tool_input: Record<string, unknown>;
|
|
23
|
+
tool_output?: string;
|
|
24
|
+
agent_type?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface UserPromptSubmitInput extends HookInputBase {
|
|
27
|
+
prompt?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SubagentStopInput extends HookInputBase {
|
|
30
|
+
agent_name?: string;
|
|
31
|
+
agent_type?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface PreCompactInput extends HookInputBase {
|
|
34
|
+
}
|
|
35
|
+
export interface NotificationInput extends HookInputBase {
|
|
36
|
+
title?: string;
|
|
37
|
+
message?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface StopInput extends HookInputBase {
|
|
40
|
+
stop_reason?: string;
|
|
41
|
+
}
|
|
42
|
+
export type HookInput = PreToolUseInput | PostToolUseInput | UserPromptSubmitInput | SubagentStopInput | PreCompactInput | NotificationInput | StopInput;
|
|
43
|
+
export interface HookOutput {
|
|
44
|
+
continue: boolean;
|
|
45
|
+
decision?: "block" | "allow";
|
|
46
|
+
reason?: string;
|
|
47
|
+
suppressOutput?: boolean;
|
|
48
|
+
hookSpecificOutput?: {
|
|
49
|
+
hookEventName?: string;
|
|
50
|
+
additionalContext?: string;
|
|
51
|
+
permissionDecision?: "ask" | "allow" | "deny";
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Create an allow output, optionally injecting text into the conversation. */
|
|
55
|
+
export declare function allowOutput(additionalContext?: string): HookOutput;
|
|
56
|
+
/** Create a block output with reason. */
|
|
57
|
+
export declare function blockOutput(reason: string): HookOutput;
|
|
58
|
+
/** Create an escalate output (ask user for permission). */
|
|
59
|
+
export declare function escalateOutput(reason: string): HookOutput;
|
|
60
|
+
/** Create a silent allow output (no visible output to user). */
|
|
61
|
+
export declare function silentOutput(): HookOutput;
|
|
62
|
+
/**
|
|
63
|
+
* Read JSON from stdin with timeout protection.
|
|
64
|
+
* Returns parsed object on success, empty object on failure (fail-open).
|
|
65
|
+
*/
|
|
66
|
+
export declare function readStdin(timeoutMs?: number): Promise<Record<string, unknown>>;
|
|
67
|
+
/**
|
|
68
|
+
* Write hook output to stdout as JSON.
|
|
69
|
+
*/
|
|
70
|
+
export declare function writeOutput(output: HookOutput): void;
|