intentdna 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/hooks/hooks.json +12 -0
- package/.claude-plugin/plugin.json +5 -0
- 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/setup.d.ts +9 -0
- package/dist/cli/commands/setup.js +42 -0
- package/dist/cli/commands/show.d.ts +0 -1
- package/dist/cli/commands/show.js +0 -1
- package/dist/cli/commands/sync.d.ts +13 -1
- package/dist/cli/commands/sync.js +158 -33
- 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 +9 -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 +33 -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 +156 -0
- package/dist/hooks/enforce.d.ts +106 -0
- package/dist/hooks/enforce.js +431 -0
- package/dist/hooks/index.d.ts +9 -0
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/protocol.d.ts +73 -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 +33 -20
- package/dist/schema/types.js +0 -1
- package/dist/schema/validate.d.ts +0 -1
- package/dist/schema/validate.js +32 -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 +4 -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
|
@@ -10,12 +10,11 @@
|
|
|
10
10
|
* - attract/repel: same target accumulates, different targets independent
|
|
11
11
|
* - amplify/suppress: factors multiply (1.5x × 2.0x = 3.0x)
|
|
12
12
|
*/
|
|
13
|
-
import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef
|
|
13
|
+
import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef } from "../schema/types.js";
|
|
14
14
|
export interface CascadedDNA {
|
|
15
15
|
genes: Record<string, Gene>;
|
|
16
16
|
contexts: Record<string, ContextRegion>;
|
|
17
17
|
roles: Record<string, RoleDef>;
|
|
18
|
-
modes: Record<string, ModeDef>;
|
|
19
18
|
workflows: Record<string, WorkflowDef>;
|
|
20
19
|
epigenetic_markers: EpigeneticMarker[];
|
|
21
20
|
source_ids: string[];
|
|
@@ -31,4 +30,3 @@ export interface CascadedDNA {
|
|
|
31
30
|
* - Same namespace: higher priority replaces (expected cascade behavior)
|
|
32
31
|
*/
|
|
33
32
|
export declare function cascadeDNA(layers: IntentDNA[]): CascadedDNA;
|
|
34
|
-
//# sourceMappingURL=cascade.d.ts.map
|
package/dist/compiler/cascade.js
CHANGED
|
@@ -81,7 +81,6 @@ function prefixWorkflowRoles(wf, ns) {
|
|
|
81
81
|
steps: wf.steps.map((step) => ({
|
|
82
82
|
...step,
|
|
83
83
|
role: `${ns}_${step.role}`,
|
|
84
|
-
mode: step.mode ? `${ns}_${step.mode}` : undefined,
|
|
85
84
|
})),
|
|
86
85
|
};
|
|
87
86
|
}
|
|
@@ -101,7 +100,6 @@ export function cascadeDNA(layers) {
|
|
|
101
100
|
const mergedGenes = {};
|
|
102
101
|
const mergedContexts = {};
|
|
103
102
|
const mergedRoles = {};
|
|
104
|
-
const mergedModes = {};
|
|
105
103
|
const mergedWorkflows = {};
|
|
106
104
|
const allMarkers = [];
|
|
107
105
|
const sourceIds = [];
|
|
@@ -126,12 +124,7 @@ export function cascadeDNA(layers) {
|
|
|
126
124
|
const key = ns ? `${ns}_${name}` : name;
|
|
127
125
|
mergedRoles[key] = role;
|
|
128
126
|
}
|
|
129
|
-
// Merge
|
|
130
|
-
for (const [name, mode] of Object.entries(dna.modes ?? {})) {
|
|
131
|
-
const key = ns ? `${ns}_${name}` : name;
|
|
132
|
-
mergedModes[key] = mode;
|
|
133
|
-
}
|
|
134
|
-
// Merge workflows: namespace-prefixed keys + step role/mode references
|
|
127
|
+
// Merge workflows: namespace-prefixed keys + step role references
|
|
135
128
|
const normalized = normalizeWorkflowsFromDNA(dna);
|
|
136
129
|
for (const [name, wf] of Object.entries(normalized)) {
|
|
137
130
|
if (ns) {
|
|
@@ -151,10 +144,8 @@ export function cascadeDNA(layers) {
|
|
|
151
144
|
genes: mergedGenes,
|
|
152
145
|
contexts: mergedContexts,
|
|
153
146
|
roles: mergedRoles,
|
|
154
|
-
modes: mergedModes,
|
|
155
147
|
workflows: mergedWorkflows,
|
|
156
148
|
epigenetic_markers: allMarkers,
|
|
157
149
|
source_ids: sourceIds,
|
|
158
150
|
};
|
|
159
151
|
}
|
|
160
|
-
//# sourceMappingURL=cascade.js.map
|
package/dist/compiler/compile.js
CHANGED
|
@@ -198,32 +198,49 @@ export function compileDNA(activated, cascaded) {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
// Build
|
|
202
|
-
const modeConstraints = [];
|
|
203
|
-
if (cascaded?.modes) {
|
|
204
|
-
for (const [name, mode] of Object.entries(cascaded.modes)) {
|
|
205
|
-
modeConstraints.push({
|
|
206
|
-
mode_name: name,
|
|
207
|
-
description: mode.description,
|
|
208
|
-
tool_deny: mode.tool_permissions?.deny,
|
|
209
|
-
scope_write: mode.scope?.write,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// Build step_checkpoints from all workflow steps that have checkpoints + a mode
|
|
201
|
+
// Build step_checkpoints from all workflow steps that have checkpoints
|
|
214
202
|
const stepCheckpoints = [];
|
|
215
203
|
const allWorkflows = Object.values(cascaded?.workflows ?? {});
|
|
216
204
|
for (const wf of allWorkflows) {
|
|
217
205
|
for (const step of wf.steps ?? []) {
|
|
218
|
-
if (step.checkpoints && step.checkpoints.length > 0
|
|
206
|
+
if (step.checkpoints && step.checkpoints.length > 0) {
|
|
219
207
|
stepCheckpoints.push({
|
|
220
|
-
|
|
208
|
+
step_role: step.role,
|
|
221
209
|
step_id: step.id,
|
|
222
210
|
checkpoints: [...step.checkpoints],
|
|
223
211
|
});
|
|
224
212
|
}
|
|
225
213
|
}
|
|
226
214
|
}
|
|
215
|
+
// Build workflows_ir: per-workflow partitioned view with namespace context
|
|
216
|
+
const workflowsIR = [];
|
|
217
|
+
if (cascaded?.workflows) {
|
|
218
|
+
for (const [wfKey, wf] of Object.entries(cascaded.workflows)) {
|
|
219
|
+
const wfStepCheckpoints = [];
|
|
220
|
+
const activeRoles = [];
|
|
221
|
+
for (const step of wf.steps ?? []) {
|
|
222
|
+
if (!activeRoles.includes(step.role)) {
|
|
223
|
+
activeRoles.push(step.role);
|
|
224
|
+
}
|
|
225
|
+
if (step.checkpoints && step.checkpoints.length > 0) {
|
|
226
|
+
wfStepCheckpoints.push({
|
|
227
|
+
step_role: step.role,
|
|
228
|
+
step_id: step.id,
|
|
229
|
+
checkpoints: [...step.checkpoints],
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Derive namespace from workflow key: "ns_wfname" → "ns", "wfname" → ""
|
|
234
|
+
const underscoreIdx = wfKey.indexOf("_");
|
|
235
|
+
const namespace = underscoreIdx > 0 ? wfKey.slice(0, underscoreIdx) : "";
|
|
236
|
+
workflowsIR.push({
|
|
237
|
+
workflow_name: wfKey,
|
|
238
|
+
namespace,
|
|
239
|
+
step_checkpoints: wfStepCheckpoints,
|
|
240
|
+
active_roles: activeRoles,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
227
244
|
// Sort directives by priority
|
|
228
245
|
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
229
246
|
directives.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
@@ -240,8 +257,7 @@ export function compileDNA(activated, cascaded) {
|
|
|
240
257
|
role_tool_permissions: roleToolPermissions,
|
|
241
258
|
role_scope: roleScope,
|
|
242
259
|
roles_scope_map: rolesScopeMap.length > 0 ? rolesScopeMap : undefined,
|
|
243
|
-
mode_constraints: modeConstraints.length > 0 ? modeConstraints : undefined,
|
|
244
260
|
step_checkpoints: stepCheckpoints.length > 0 ? stepCheckpoints : undefined,
|
|
261
|
+
workflows_ir: workflowsIR.length > 0 ? workflowsIR : undefined,
|
|
245
262
|
};
|
|
246
263
|
}
|
|
247
|
-
//# sourceMappingURL=compile.js.map
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -25,4 +25,3 @@ export interface CompileOptions {
|
|
|
25
25
|
* Optionally injects stored epigenetic markers from the evolution engine.
|
|
26
26
|
*/
|
|
27
27
|
export declare function compileFromFiles(filePaths: string[], contextOrOptions?: string | CompileOptions): Promise<ConstraintIR>;
|
|
28
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/compiler/index.js
CHANGED
|
@@ -34,4 +34,3 @@ export interface CompileWorkflowResult {
|
|
|
34
34
|
* Returns a result object with either a plan or errors.
|
|
35
35
|
*/
|
|
36
36
|
export declare function compileWorkflow(workflow: WorkflowDef, options?: CompileWorkflowOptions): CompileWorkflowResult;
|
|
37
|
-
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -166,7 +166,6 @@ function toWorkflowStep(def) {
|
|
|
166
166
|
return {
|
|
167
167
|
id: def.id,
|
|
168
168
|
role: def.role,
|
|
169
|
-
mode: def.mode ?? null,
|
|
170
169
|
description: def.description ?? def.id,
|
|
171
170
|
optional: def.optional ?? false,
|
|
172
171
|
run_if: resolveRunIf(def.run_if),
|
|
@@ -273,4 +272,3 @@ export function compileWorkflow(workflow, options) {
|
|
|
273
272
|
}
|
|
274
273
|
return { ok: true, plan, errors: [] };
|
|
275
274
|
}
|
|
276
|
-
//# sourceMappingURL=workflow.js.map
|
package/dist/evolution/index.js
CHANGED
package/dist/evolution/store.js
CHANGED
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,156 @@
|
|
|
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, enforceSessionStart, enforceStop, } from "./enforce.js";
|
|
21
|
+
import { appendAudit, readWorkflowState } 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
|
+
// Special handling for Stop — needs async workflow state read
|
|
56
|
+
if (event === "Stop") {
|
|
57
|
+
const wfState = await readWorkflowState(projectDir, sessionId);
|
|
58
|
+
const stopContext = wfState ? {
|
|
59
|
+
active: wfState.active,
|
|
60
|
+
workflow: wfState.workflow,
|
|
61
|
+
current_step: wfState.current_step,
|
|
62
|
+
current_role: wfState.current_role,
|
|
63
|
+
started_at: wfState.started_at,
|
|
64
|
+
} : null;
|
|
65
|
+
const stopOutput = enforceStop(ir, {
|
|
66
|
+
cwd: typeof rawInput.cwd === "string" ? rawInput.cwd : undefined,
|
|
67
|
+
session_id: sessionId,
|
|
68
|
+
stop_reason: typeof rawInput.stop_reason === "string" ? rawInput.stop_reason : undefined,
|
|
69
|
+
}, stopContext);
|
|
70
|
+
writeOutput(stopOutput);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Dispatch to enforcement engine
|
|
74
|
+
const output = dispatch(event, ir, rawInput, state);
|
|
75
|
+
writeOutput(output);
|
|
76
|
+
// Side effect: audit log for notifications
|
|
77
|
+
if (event === "Notification" && output.hookSpecificOutput?.additionalContext?.includes("violation")) {
|
|
78
|
+
const message = typeof rawInput.message === "string" ? rawInput.message : "";
|
|
79
|
+
await appendAudit(projectDir, {
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
event: "violation",
|
|
82
|
+
message,
|
|
83
|
+
session_id: sessionId,
|
|
84
|
+
}).catch(() => { }); // Fail-open
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ── Dispatch ───────────────────────────────────────────────
|
|
88
|
+
function dispatch(event, ir, input, state) {
|
|
89
|
+
switch (event) {
|
|
90
|
+
case "PreToolUse":
|
|
91
|
+
return enforcePreToolUse(ir, {
|
|
92
|
+
tool_name: String(input.tool_name ?? ""),
|
|
93
|
+
tool_input: (input.tool_input ?? {}),
|
|
94
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
95
|
+
cwd: typeof input.cwd === "string" ? input.cwd : undefined,
|
|
96
|
+
sessionId: typeof input.sessionId === "string" ? input.sessionId : undefined,
|
|
97
|
+
}, state);
|
|
98
|
+
case "PostToolUse":
|
|
99
|
+
return enforcePostToolUse(ir, {
|
|
100
|
+
tool_name: String(input.tool_name ?? ""),
|
|
101
|
+
tool_input: (input.tool_input ?? {}),
|
|
102
|
+
tool_output: typeof input.tool_output === "string" ? input.tool_output : undefined,
|
|
103
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
104
|
+
});
|
|
105
|
+
case "UserPromptSubmit":
|
|
106
|
+
return enforceUserPromptSubmit(ir, {
|
|
107
|
+
prompt: typeof input.prompt === "string" ? input.prompt : undefined,
|
|
108
|
+
});
|
|
109
|
+
case "SubagentStop":
|
|
110
|
+
return enforceSubagentStop(ir, {
|
|
111
|
+
agent_name: typeof input.agent_name === "string" ? input.agent_name : undefined,
|
|
112
|
+
agent_type: typeof input.agent_type === "string" ? input.agent_type : undefined,
|
|
113
|
+
});
|
|
114
|
+
case "PreCompact":
|
|
115
|
+
return enforcePreCompact(ir);
|
|
116
|
+
case "Notification":
|
|
117
|
+
return enforceNotification(ir, {
|
|
118
|
+
title: typeof input.title === "string" ? input.title : undefined,
|
|
119
|
+
message: typeof input.message === "string" ? input.message : undefined,
|
|
120
|
+
});
|
|
121
|
+
case "Stop":
|
|
122
|
+
return silentOutput(); // DNA doesn't block stops
|
|
123
|
+
case "SessionStart":
|
|
124
|
+
return enforceSessionStart(ir, {
|
|
125
|
+
cwd: typeof input.cwd === "string" ? input.cwd : undefined,
|
|
126
|
+
session_id: typeof input.sessionId === "string" ? input.sessionId : undefined,
|
|
127
|
+
trigger: typeof input.trigger === "string" ? input.trigger : undefined,
|
|
128
|
+
});
|
|
129
|
+
default:
|
|
130
|
+
return silentOutput();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ── IR Loading ─────────────────────────────────────────────
|
|
134
|
+
async function loadIR(irPath) {
|
|
135
|
+
try {
|
|
136
|
+
const raw = await readFile(irPath, "utf-8");
|
|
137
|
+
const data = JSON.parse(raw);
|
|
138
|
+
// Support both raw IR and wrapped CompiledIRFile format
|
|
139
|
+
if (data.ir_version && data.ir) {
|
|
140
|
+
return data.ir;
|
|
141
|
+
}
|
|
142
|
+
// Direct ConstraintIR
|
|
143
|
+
if (data.compiled_at && data.source_dna_ids) {
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ── Entry Point ────────────────────────────────────────────
|
|
153
|
+
main().catch(() => {
|
|
154
|
+
// Fail-open: never block Claude Code on unexpected errors
|
|
155
|
+
writeOutput(silentOutput());
|
|
156
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
/** SessionStart input fields */
|
|
19
|
+
export interface SessionStartInput {
|
|
20
|
+
cwd?: string;
|
|
21
|
+
session_id?: string;
|
|
22
|
+
trigger?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface EnforceState {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Enforce PreToolUse constraints.
|
|
28
|
+
* Returns the first blocking/escalating result, or allow if all pass.
|
|
29
|
+
*
|
|
30
|
+
* Optional `roles` parameter provides output_schema enforcement.
|
|
31
|
+
*/
|
|
32
|
+
export declare function enforcePreToolUse(ir: ConstraintIR, input: PreToolUseInput, state?: EnforceState, roles?: Record<string, RoleDef>): HookOutput;
|
|
33
|
+
/**
|
|
34
|
+
* Enforce PostToolUse validators.
|
|
35
|
+
* Currently logs validators for audit. Returns allow with context.
|
|
36
|
+
*/
|
|
37
|
+
export declare function enforcePostToolUse(ir: ConstraintIR, input: PostToolUseInput): HookOutput;
|
|
38
|
+
/**
|
|
39
|
+
* Inject DNA policy reminders on each user prompt.
|
|
40
|
+
* Only injects when there are high-priority directives.
|
|
41
|
+
*/
|
|
42
|
+
export declare function enforceUserPromptSubmit(ir: ConstraintIR, _input: UserPromptSubmitInput): HookOutput;
|
|
43
|
+
/**
|
|
44
|
+
* Audit role scope compliance when a subagent finishes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function enforceSubagentStop(ir: ConstraintIR, input: SubagentStopInput): HookOutput;
|
|
47
|
+
/**
|
|
48
|
+
* Preserve DNA directive summary before context compaction.
|
|
49
|
+
*/
|
|
50
|
+
export declare function enforcePreCompact(ir: ConstraintIR): HookOutput;
|
|
51
|
+
/**
|
|
52
|
+
* Check if a notification is a DNA violation and return audit info.
|
|
53
|
+
* The CLI handles actual audit file writes.
|
|
54
|
+
*/
|
|
55
|
+
export declare function enforceNotification(ir: ConstraintIR, input: NotificationInput): HookOutput;
|
|
56
|
+
/**
|
|
57
|
+
* Initialize DNA state and inject policy summary on session start.
|
|
58
|
+
* Returns silent if no DNA policies are active.
|
|
59
|
+
*/
|
|
60
|
+
export declare function enforceSessionStart(ir: ConstraintIR, input: SessionStartInput): HookOutput;
|
|
61
|
+
/** Stop enforcement input — includes workflow awareness */
|
|
62
|
+
export interface StopEnforceInput {
|
|
63
|
+
cwd?: string;
|
|
64
|
+
session_id?: string;
|
|
65
|
+
stop_reason?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Workflow state context for stop enforcement */
|
|
68
|
+
export interface StopWorkflowContext {
|
|
69
|
+
active: boolean;
|
|
70
|
+
workflow: string;
|
|
71
|
+
current_step: string;
|
|
72
|
+
current_role: string;
|
|
73
|
+
started_at: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Enforce Stop hook — verify workflow checkpoint completion.
|
|
77
|
+
*
|
|
78
|
+
* Safety valves (NEVER block):
|
|
79
|
+
* - context_limit / context_window stops
|
|
80
|
+
* - Stale workflow state (>2h)
|
|
81
|
+
* - No active workflow
|
|
82
|
+
* - No checkpoints defined
|
|
83
|
+
*
|
|
84
|
+
* Block when:
|
|
85
|
+
* - Workflow active + unmet checkpoints for current step
|
|
86
|
+
*/
|
|
87
|
+
export declare function enforceStop(ir: ConstraintIR, input: StopEnforceInput, workflowState: StopWorkflowContext | null): HookOutput;
|
|
88
|
+
/** Check if a file path is allowed by a list of write globs (prefix matching). */
|
|
89
|
+
export declare function checkWriteAllowed(filePath: string, allowedGlobs: string[]): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Extract directory prefix from a glob pattern.
|
|
92
|
+
* "src/**\/*.ts" → "src/"
|
|
93
|
+
* "test/**" → "test/"
|
|
94
|
+
* "*.md" → "" (root)
|
|
95
|
+
* "package.json" → "package.json" (exact path)
|
|
96
|
+
*/
|
|
97
|
+
export declare function globToPrefix(glob: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* Evaluate a gate condition against a PreToolUse input.
|
|
100
|
+
* Returns true if the condition triggers, false if not, null if too complex.
|
|
101
|
+
*/
|
|
102
|
+
export declare function evaluateGateCondition(condition: string, input: PreToolUseInput): boolean | null;
|
|
103
|
+
/**
|
|
104
|
+
* Resolve a tool filter target to concrete Claude Code tool names.
|
|
105
|
+
*/
|
|
106
|
+
export declare function resolveToolTarget(target?: string): string[] | null;
|