intentdna 1.9.3 → 1.9.4
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/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/compiler/input-resolver.d.ts +2 -1
- package/dist/compiler/input-resolver.js +3 -30
- package/dist/hooks/cli.js +12 -12
- package/dist/project-config.d.ts +2 -0
- package/dist/project-config.js +71 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "intentdna",
|
|
11
11
|
"description": "Declarative policy layer for AI agent behavior with plugin-managed hook runtime for Claude Code.",
|
|
12
|
-
"version": "1.9.
|
|
12
|
+
"version": "1.9.4",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Samuel"
|
|
15
15
|
},
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"version": "1.9.
|
|
28
|
+
"version": "1.9.4"
|
|
29
29
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { detectDNAConfigs } from "../project-config.js";
|
|
1
2
|
import type { DNASourceProvenance, IntentDNA } from "../schema/types.js";
|
|
2
3
|
import type { Diagnostic } from "./diagnostics.js";
|
|
4
|
+
export { detectDNAConfigs };
|
|
3
5
|
export interface ResolveDNAInputOptions {
|
|
4
6
|
cwd?: string;
|
|
5
7
|
includeDiagnostics?: boolean;
|
|
@@ -11,7 +13,6 @@ export interface ResolvedDNAInputs {
|
|
|
11
13
|
}
|
|
12
14
|
export declare function parseDNAFile(filePath: string): Promise<IntentDNA>;
|
|
13
15
|
export declare function resolveSpeciesReference(ref: string): string | null;
|
|
14
|
-
export declare function detectDNAConfigs(projectDir?: string): Promise<string[]>;
|
|
15
16
|
export interface ExpandDNAInputOptions {
|
|
16
17
|
cwd?: string;
|
|
17
18
|
enterprisePolicyDirs?: string[];
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import { readFile, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { TextDecoder } from "node:util";
|
|
7
|
+
import { detectDNAConfigs } from "../project-config.js";
|
|
7
8
|
import { validateDNA } from "../schema/validate.js";
|
|
8
9
|
import { parseYAML } from "../schema/yaml-parser.js";
|
|
9
10
|
import { DNADiagnosticsError, hasDiagnosticErrors } from "./diagnostics.js";
|
|
10
|
-
|
|
11
|
-
".dna/config.yaml",
|
|
12
|
-
".dna/config.yml",
|
|
13
|
-
".dna/config.json",
|
|
14
|
-
".dna.yaml",
|
|
15
|
-
".dna.yml",
|
|
16
|
-
".dna.json",
|
|
17
|
-
];
|
|
11
|
+
export { detectDNAConfigs };
|
|
18
12
|
async function fileExists(path) {
|
|
19
13
|
try {
|
|
20
14
|
await stat(path);
|
|
@@ -39,27 +33,6 @@ export function resolveSpeciesReference(ref) {
|
|
|
39
33
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
40
34
|
return resolve(thisDir, "..", "species", `${name}.dna.json`);
|
|
41
35
|
}
|
|
42
|
-
export async function detectDNAConfigs(projectDir = process.cwd()) {
|
|
43
|
-
const configsDir = resolve(projectDir, ".dna", "configs");
|
|
44
|
-
try {
|
|
45
|
-
const files = await readdir(configsDir);
|
|
46
|
-
const configs = files
|
|
47
|
-
.filter((file) => file.endsWith(".yaml") || file.endsWith(".yml"))
|
|
48
|
-
.sort()
|
|
49
|
-
.map((file) => resolve(configsDir, file));
|
|
50
|
-
if (configs.length > 0)
|
|
51
|
-
return configs;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Directory does not exist.
|
|
55
|
-
}
|
|
56
|
-
for (const candidate of DNA_CONFIG_CANDIDATES) {
|
|
57
|
-
const path = resolve(projectDir, candidate);
|
|
58
|
-
if (await fileExists(path))
|
|
59
|
-
return [path];
|
|
60
|
-
}
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
36
|
function fingerprint(snapshotBytes) {
|
|
64
37
|
return `sha256:${createHash("sha256").update(snapshotBytes).digest("hex")}`;
|
|
65
38
|
}
|
package/dist/hooks/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import { spawn } from "node:child_process";
|
|
|
20
20
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
21
21
|
import { createHash, randomUUID } from "node:crypto";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { hasDNAProjectConfigBoundary } from "../project-config.js";
|
|
23
24
|
import { readStdinResult, writeOutput, silentOutput, allowOutput, blockOutput, stopOutput } from "./protocol.js";
|
|
24
25
|
import { isCurrentClaudeSubagentStopInput, validateHookInput } from "./schema.js";
|
|
25
26
|
import { hookFailureOutput } from "./enforcement-boundary.js";
|
|
@@ -91,20 +92,19 @@ function isTerminalWorkflowDeactivation(event, rawInput, state) {
|
|
|
91
92
|
return args.active === false && args.workflow === state.workflow;
|
|
92
93
|
}
|
|
93
94
|
async function hookProjectBoundaryExists(projectDir) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
try {
|
|
96
|
+
await stat(resolve(projectDir, ".git"));
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const code = error instanceof Error && "code" in error
|
|
101
|
+
? error.code
|
|
102
|
+
: undefined;
|
|
103
|
+
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
97
104
|
return true;
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
const code = error instanceof Error && "code" in error
|
|
101
|
-
? error.code
|
|
102
|
-
: undefined;
|
|
103
|
-
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
105
|
}
|
|
107
|
-
|
|
106
|
+
// Artifact-only directories such as .dna/reviews do not establish policy ownership.
|
|
107
|
+
return hasDNAProjectConfigBoundary(projectDir);
|
|
108
108
|
}
|
|
109
109
|
async function resolveHookRuntimePaths(cwd, irPath = DEFAULT_IR_PATH, explicitIRPath = false) {
|
|
110
110
|
const initialDir = resolve(cwd);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const LEGACY_DNA_CONFIG_CANDIDATES = [
|
|
4
|
+
".dna/config.yaml",
|
|
5
|
+
".dna/config.yml",
|
|
6
|
+
".dna/config.json",
|
|
7
|
+
".dna.yaml",
|
|
8
|
+
".dna.yml",
|
|
9
|
+
".dna.json",
|
|
10
|
+
];
|
|
11
|
+
const DNA_PROJECT_CONFIG_BOUNDARY_MARKERS = [
|
|
12
|
+
".dna/config.yaml",
|
|
13
|
+
".dna/config.yml",
|
|
14
|
+
".dna/config.json",
|
|
15
|
+
".dna/configs",
|
|
16
|
+
".dna.yaml",
|
|
17
|
+
".dna.yml",
|
|
18
|
+
".dna.json",
|
|
19
|
+
];
|
|
20
|
+
function filesystemErrorCode(error) {
|
|
21
|
+
return error instanceof Error && "code" in error
|
|
22
|
+
? error.code
|
|
23
|
+
: undefined;
|
|
24
|
+
}
|
|
25
|
+
function pathIsAbsent(error) {
|
|
26
|
+
const code = filesystemErrorCode(error);
|
|
27
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
28
|
+
}
|
|
29
|
+
async function fileExists(path) {
|
|
30
|
+
try {
|
|
31
|
+
await stat(path);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function detectDNAConfigs(projectDir = process.cwd()) {
|
|
39
|
+
const configsDir = resolve(projectDir, ".dna", "configs");
|
|
40
|
+
try {
|
|
41
|
+
const files = await readdir(configsDir);
|
|
42
|
+
const configs = files
|
|
43
|
+
.filter((file) => file.endsWith(".yaml") || file.endsWith(".yml"))
|
|
44
|
+
.sort()
|
|
45
|
+
.map((file) => resolve(configsDir, file));
|
|
46
|
+
if (configs.length > 0)
|
|
47
|
+
return configs;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Directory does not exist or cannot be read.
|
|
51
|
+
}
|
|
52
|
+
for (const candidate of LEGACY_DNA_CONFIG_CANDIDATES) {
|
|
53
|
+
const path = resolve(projectDir, candidate);
|
|
54
|
+
if (await fileExists(path))
|
|
55
|
+
return [path];
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
export async function hasDNAProjectConfigBoundary(projectDir) {
|
|
60
|
+
for (const marker of DNA_PROJECT_CONFIG_BOUNDARY_MARKERS) {
|
|
61
|
+
try {
|
|
62
|
+
await stat(resolve(projectDir, marker));
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (!pathIsAbsent(error))
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|