intentdna 1.7.4 → 1.8.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/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/LICENSE +201 -0
- package/README.md +143 -194
- package/dist/assets/candidates.d.ts +124 -0
- package/dist/assets/candidates.js +1249 -0
- package/dist/assets/catalog.d.ts +101 -0
- package/dist/assets/catalog.js +311 -0
- package/dist/assets/evidence.d.ts +93 -0
- package/dist/assets/evidence.js +391 -0
- package/dist/assets/index.d.ts +4 -0
- package/dist/assets/index.js +4 -0
- package/dist/assets/metadata.d.ts +19 -0
- package/dist/assets/metadata.js +227 -0
- package/dist/cli/commands/assets.d.ts +65 -0
- package/dist/cli/commands/assets.js +2871 -0
- package/dist/cli/commands/auth.d.ts +322 -0
- package/dist/cli/commands/auth.js +1592 -0
- package/dist/cli/commands/cloud-auth-guidance.d.ts +2 -0
- package/dist/cli/commands/cloud-auth-guidance.js +4 -0
- package/dist/cli/commands/cloud.d.ts +1359 -0
- package/dist/cli/commands/cloud.js +8364 -0
- package/dist/cli/commands/evolve.js +18 -2
- package/dist/cli/commands/generate.js +25 -2
- package/dist/cli/commands/guard.d.ts +1 -1
- package/dist/cli/commands/guard.js +1 -1
- package/dist/cli/commands/guide.d.ts +15 -0
- package/dist/cli/commands/guide.js +1747 -0
- package/dist/cli/commands/init.d.ts +12 -0
- package/dist/cli/commands/init.js +95 -60
- package/dist/cli/commands/org.d.ts +16 -0
- package/dist/cli/commands/org.js +2393 -0
- package/dist/cli/commands/release.d.ts +59 -0
- package/dist/cli/commands/release.js +716 -0
- package/dist/cli/commands/report.d.ts +82 -0
- package/dist/cli/commands/report.js +1516 -0
- package/dist/cli/commands/run.d.ts +6 -1
- package/dist/cli/commands/run.js +95 -26
- package/dist/cli/commands/sync.d.ts +8 -1
- package/dist/cli/commands/sync.js +436 -141
- package/dist/cli/commands/templates.d.ts +64 -0
- package/dist/cli/commands/templates.js +2151 -1
- package/dist/cli/commands/workflow.d.ts +26 -0
- package/dist/cli/commands/workflow.js +3053 -0
- package/dist/cli/index.js +719 -42
- package/dist/compiler/cascade.js +4 -9
- package/dist/compiler/compile.js +1 -0
- package/dist/governance/evidence-capture-attribution.d.ts +12 -0
- package/dist/governance/evidence-capture-attribution.js +46 -0
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.js +1 -0
- package/dist/hooks/cli.d.ts +6 -0
- package/dist/hooks/cli.js +168 -12
- package/dist/hooks/enforce.d.ts +1 -0
- package/dist/hooks/protocol.d.ts +1 -1
- package/dist/hooks/protocol.js +1 -1
- package/dist/hooks/state.d.ts +41 -0
- package/dist/hooks/state.js +126 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/mcp/tools-state.js +2 -0
- package/dist/organization/index.d.ts +121 -0
- package/dist/organization/index.js +413 -0
- package/dist/runtime/claude-sync-target.d.ts +49 -0
- package/dist/runtime/claude-sync-target.js +280 -0
- package/dist/runtime/codex-adapter.d.ts +254 -0
- package/dist/runtime/codex-adapter.js +1204 -0
- package/dist/runtime/codex-sync-target.d.ts +33 -0
- package/dist/runtime/codex-sync-target.js +241 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/output-artifact-registry.d.ts +1 -1
- package/dist/runtime/output-artifact-registry.js +2 -0
- package/dist/runtime/settings-adapter.d.ts +1 -1
- package/dist/runtime/settings-adapter.js +1 -1
- package/dist/runtime/skill-adapter.d.ts +5 -1
- package/dist/runtime/skill-adapter.js +120 -34
- package/dist/runtime/sync-artifact-registry.d.ts +99 -0
- package/dist/runtime/sync-artifact-registry.js +194 -0
- package/dist/runtime/sync-target-plan.d.ts +45 -0
- package/dist/runtime/sync-target-plan.js +19 -0
- package/dist/schema/controller-registry.d.ts +8 -1
- package/dist/schema/controller-registry.js +8 -0
- package/dist/schema/types.d.ts +35 -23
- package/dist/schema/types.js +10 -1
- package/dist/schema/validate.js +22 -22
- package/dist/schema/validators/controllers.js +38 -20
- package/dist/schema/workflow-authoring-contract.d.ts +29 -0
- package/dist/schema/workflow-authoring-contract.js +109 -0
- package/dist/schema/yaml-parser.js +18 -0
- package/dist/templates/api-backend.dna.yaml +19 -0
- package/dist/templates/brainstorming-first.dna.yaml +19 -0
- package/dist/templates/code-cleanup.dna.yaml +19 -0
- package/dist/templates/code-review-pipeline.dna.yaml +18 -0
- package/dist/templates/documentation-writer.dna.yaml +19 -0
- package/dist/templates/flutter-behavior-lock.dna.yaml +19 -0
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +19 -0
- package/dist/templates/flutter-rewrite-diagnosis-review.dna.yaml +210 -0
- package/dist/templates/flutter-rewrite-fix-review-loop.dna.yaml +260 -0
- package/dist/templates/flutter-rewrite-new.dna.yaml +506 -0
- package/dist/templates/flutter-rewrite.dna.yaml +20 -0
- package/dist/templates/frontend-quality.dna.yaml +19 -0
- package/dist/templates/full-pipeline.dna.yaml +20 -0
- package/dist/templates/marketplace.d.ts +92 -0
- package/dist/templates/marketplace.js +502 -0
- package/dist/templates/metadata.d.ts +25 -0
- package/dist/templates/metadata.js +136 -3
- package/dist/templates/mobile-dev.dna.yaml +19 -0
- package/dist/templates/multi-agent-handoff-coordination.dna.yaml +156 -0
- package/dist/templates/multi-perspective-review.dna.yaml +19 -0
- package/dist/templates/persistent-executor.dna.yaml +19 -0
- package/dist/templates/qa-loop.dna.yaml +18 -0
- package/dist/templates/release-evidence-gate.dna.yaml +131 -0
- package/dist/templates/requirements-gate.dna.yaml +19 -0
- package/dist/templates/research-orchestration.dna.yaml +19 -0
- package/dist/templates/root-cause-analysis.dna.yaml +19 -0
- package/dist/templates/safe-refactoring.dna.yaml +321 -0
- package/dist/templates/secure-dev.dna.yaml +19 -0
- package/dist/templates/subagent-parallel.dna.yaml +19 -0
- package/dist/templates/systematic-debugging.dna.yaml +19 -0
- package/dist/templates/tdd-strict.dna.yaml +19 -0
- package/dist/templates/ui-visual-regression-qa.dna.yaml +133 -0
- package/dist/templates/workflow-author.dna.yaml +302 -0
- package/dist/templates/workflow-blocked-scope-triage.dna.yaml +242 -0
- package/dist/templates/workflow-completion-audit.dna.yaml +254 -0
- package/dist/templates/workflow-cross-boundary-resource-approval.dna.yaml +249 -0
- package/dist/templates/workflow-evidence-review.dna.yaml +198 -0
- package/dist/templates/workflow-module-inventory.dna.yaml +257 -0
- package/dist/templates/workflow-runtime-smoke-review.dna.yaml +252 -0
- package/dist/templates/workflow-verification-command-routing.dna.yaml +278 -0
- package/dist/workflow/authoring-packet.d.ts +67 -0
- package/dist/workflow/authoring-packet.js +218 -0
- package/dist/workflow/draft.d.ts +159 -0
- package/dist/workflow/draft.js +800 -0
- package/dist/workflow/index.d.ts +3 -0
- package/dist/workflow/index.js +3 -0
- package/dist/workflow/suggestion-review.d.ts +65 -0
- package/dist/workflow/suggestion-review.js +245 -0
- package/package.json +1 -1
- package/spec/README.md +15 -48
- package/spec/codex-adapter-contract.md +176 -0
- package/spec/sync-target-plan.md +158 -0
- package/spec/workflow-capability-ir.md +153 -0
- package/spec/agent-dispatch-fix.md +0 -116
- package/spec/control-plane-convergence-handoff-2026-04-24.md +0 -432
- package/spec/flutter-rewrite-template-optimization.md +0 -301
- package/spec/foundation-hardening.md +0 -178
- package/spec/hooks-infra-harness-hardening.md +0 -741
- package/spec/multi-config.md +0 -172
- package/spec/parallel-isolation.md +0 -268
- package/spec/template-version-namespace-fix.md +0 -653
- package/spec/workflow-pipeline.md +0 -101
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { parseYAML } from "../schema/yaml-parser.js";
|
|
4
|
+
import { activeEvidenceFollowupReviewRoute, buildWorkflowAssetEvidenceIndex, listWorkflowAssets } from "../assets/index.js";
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function stringValue(value) {
|
|
9
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function stringArray(value) {
|
|
12
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
13
|
+
}
|
|
14
|
+
function slug(value) {
|
|
15
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "org";
|
|
16
|
+
}
|
|
17
|
+
function orgPath(projectDir) {
|
|
18
|
+
return join(projectDir, ".dna", "org.yaml");
|
|
19
|
+
}
|
|
20
|
+
function quote(value) {
|
|
21
|
+
return /^[A-Za-z0-9_./:-]+$/.test(value) ? value : JSON.stringify(value);
|
|
22
|
+
}
|
|
23
|
+
function profileToYaml(profile) {
|
|
24
|
+
const lines = [
|
|
25
|
+
`schema_version: ${profile.schema_version}`,
|
|
26
|
+
"organization:",
|
|
27
|
+
` id: ${quote(profile.organization.id)}`,
|
|
28
|
+
` name: ${quote(profile.organization.name)}`,
|
|
29
|
+
"projects:",
|
|
30
|
+
];
|
|
31
|
+
for (const project of profile.projects) {
|
|
32
|
+
lines.push(` - id: ${quote(project.id)}`);
|
|
33
|
+
lines.push(` name: ${quote(project.name)}`);
|
|
34
|
+
lines.push(` path: ${quote(project.path)}`);
|
|
35
|
+
if (project.assets.length === 0) {
|
|
36
|
+
lines.push(" assets: []");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lines.push(" assets:");
|
|
40
|
+
for (const asset of project.assets) {
|
|
41
|
+
lines.push(` - asset: ${quote(asset.asset)}`);
|
|
42
|
+
lines.push(` status: ${asset.status}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
lines.push(` sync_targets: [${project.syncTargets.map(quote).join(", ")}]`);
|
|
46
|
+
}
|
|
47
|
+
return `${lines.join("\n")}\n`;
|
|
48
|
+
}
|
|
49
|
+
function parseAssets(value) {
|
|
50
|
+
if (!Array.isArray(value))
|
|
51
|
+
return [];
|
|
52
|
+
return value
|
|
53
|
+
.filter((entry) => isRecord(entry))
|
|
54
|
+
.flatMap((entry) => {
|
|
55
|
+
const asset = stringValue(entry.asset);
|
|
56
|
+
if (!asset)
|
|
57
|
+
return [];
|
|
58
|
+
const status = entry.status === "active" || entry.status === "paused" ? entry.status : "planned";
|
|
59
|
+
return [{ asset, status }];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function normalizeProfile(parsed, projectDir) {
|
|
63
|
+
if (!isRecord(parsed) || !isRecord(parsed.organization))
|
|
64
|
+
return undefined;
|
|
65
|
+
const id = stringValue(parsed.organization.id);
|
|
66
|
+
const name = stringValue(parsed.organization.name);
|
|
67
|
+
if (!id || !name)
|
|
68
|
+
return undefined;
|
|
69
|
+
const projects = Array.isArray(parsed.projects) ? parsed.projects : [];
|
|
70
|
+
return {
|
|
71
|
+
schema_version: "intentdna.organization.v1",
|
|
72
|
+
organization: { id, name },
|
|
73
|
+
projects: projects
|
|
74
|
+
.filter((entry) => isRecord(entry))
|
|
75
|
+
.flatMap((entry) => {
|
|
76
|
+
const projectId = stringValue(entry.id);
|
|
77
|
+
const projectName = stringValue(entry.name);
|
|
78
|
+
if (!projectId || !projectName)
|
|
79
|
+
return [];
|
|
80
|
+
return [{
|
|
81
|
+
id: projectId,
|
|
82
|
+
name: projectName,
|
|
83
|
+
path: stringValue(entry.path) ?? ".",
|
|
84
|
+
assets: parseAssets(entry.assets),
|
|
85
|
+
syncTargets: stringArray(entry.sync_targets),
|
|
86
|
+
}];
|
|
87
|
+
})
|
|
88
|
+
.concat(projects.length === 0 ? [{
|
|
89
|
+
id: "local-project",
|
|
90
|
+
name: "Local Project",
|
|
91
|
+
path: relative(projectDir, projectDir) || ".",
|
|
92
|
+
assets: [],
|
|
93
|
+
syncTargets: [],
|
|
94
|
+
}] : []),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export async function readOrganizationProfile(options) {
|
|
98
|
+
const projectDir = options?.projectDir ?? process.cwd();
|
|
99
|
+
const path = orgPath(projectDir);
|
|
100
|
+
try {
|
|
101
|
+
const parsed = parseYAML(await readFile(path, "utf-8"));
|
|
102
|
+
return { path, profile: normalizeProfile(parsed, projectDir) };
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return { path };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export async function createOrganizationProfile(input) {
|
|
109
|
+
const projectDir = input.projectDir ?? process.cwd();
|
|
110
|
+
const organizationName = input.orgName.trim() || "IntentDNA Organization";
|
|
111
|
+
const projectName = input.projectName?.trim() || "Local Project";
|
|
112
|
+
const profile = {
|
|
113
|
+
schema_version: "intentdna.organization.v1",
|
|
114
|
+
organization: {
|
|
115
|
+
id: slug(organizationName),
|
|
116
|
+
name: organizationName,
|
|
117
|
+
},
|
|
118
|
+
projects: [{
|
|
119
|
+
id: slug(projectName),
|
|
120
|
+
name: projectName,
|
|
121
|
+
path: ".",
|
|
122
|
+
assets: input.asset ? [{ asset: input.asset, status: "planned" }] : [],
|
|
123
|
+
syncTargets: input.syncTarget ? [input.syncTarget] : [],
|
|
124
|
+
}],
|
|
125
|
+
};
|
|
126
|
+
const yaml = profileToYaml(profile);
|
|
127
|
+
const path = orgPath(projectDir);
|
|
128
|
+
await mkdir(join(projectDir, ".dna"), { recursive: true });
|
|
129
|
+
await writeFile(path, yaml, "utf-8");
|
|
130
|
+
return { path, profile, yaml };
|
|
131
|
+
}
|
|
132
|
+
export function isOrganizationAssetStatus(value) {
|
|
133
|
+
return value === "planned" || value === "active" || value === "paused";
|
|
134
|
+
}
|
|
135
|
+
function normalizeAssetStatus(value) {
|
|
136
|
+
return value && isOrganizationAssetStatus(value) ? value : "planned";
|
|
137
|
+
}
|
|
138
|
+
export async function bindOrganizationAsset(input) {
|
|
139
|
+
const projectDir = input.projectDir ?? process.cwd();
|
|
140
|
+
const { path, profile } = await readOrganizationProfile({ projectDir });
|
|
141
|
+
if (!profile)
|
|
142
|
+
return undefined;
|
|
143
|
+
const asset = input.asset.trim();
|
|
144
|
+
if (!asset)
|
|
145
|
+
return undefined;
|
|
146
|
+
const targetProject = input.project
|
|
147
|
+
? profile.projects.find((project) => project.id === input.project || project.name === input.project)
|
|
148
|
+
: profile.projects[0];
|
|
149
|
+
if (!targetProject)
|
|
150
|
+
return undefined;
|
|
151
|
+
const status = normalizeAssetStatus(input.status);
|
|
152
|
+
const existing = targetProject.assets.find((binding) => binding.asset === asset);
|
|
153
|
+
const addedAsset = existing === undefined;
|
|
154
|
+
if (existing) {
|
|
155
|
+
existing.status = status;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
targetProject.assets.push({ asset, status });
|
|
159
|
+
}
|
|
160
|
+
const addedSyncTargets = [];
|
|
161
|
+
if (input.syncTarget && !targetProject.syncTargets.includes(input.syncTarget)) {
|
|
162
|
+
targetProject.syncTargets.push(input.syncTarget);
|
|
163
|
+
addedSyncTargets.push(input.syncTarget);
|
|
164
|
+
}
|
|
165
|
+
const yaml = profileToYaml(profile);
|
|
166
|
+
await mkdir(join(projectDir, ".dna"), { recursive: true });
|
|
167
|
+
await writeFile(path, yaml, "utf-8");
|
|
168
|
+
return {
|
|
169
|
+
path,
|
|
170
|
+
profile,
|
|
171
|
+
yaml,
|
|
172
|
+
projectId: targetProject.id,
|
|
173
|
+
asset,
|
|
174
|
+
status,
|
|
175
|
+
addedAsset,
|
|
176
|
+
addedSyncTargets,
|
|
177
|
+
guidance: {
|
|
178
|
+
bindingEffect: addedAsset
|
|
179
|
+
? "Added this asset binding to the project; existing asset bindings were left unchanged."
|
|
180
|
+
: "Updated this asset binding in place; other project asset bindings were left unchanged.",
|
|
181
|
+
reviewCommand: "dna org review",
|
|
182
|
+
rolloutCommand: "dna org rollout",
|
|
183
|
+
replacementBoundary: "Binding an accepted version does not automatically pause, replace, configure, sync, or deploy older bindings; manager review and rollout decide the next safe action.",
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function configuredAssetNames(assets) {
|
|
188
|
+
return new Set(assets.filter((asset) => asset.status.configured).map((asset) => asset.name));
|
|
189
|
+
}
|
|
190
|
+
function evidenceGate(index) {
|
|
191
|
+
if (!index)
|
|
192
|
+
return "unknown";
|
|
193
|
+
return index.totals.continue > 0 || index.totals.blocked > 0 || index.totals.requestChanges > 0 || index.totals.remaining > 0
|
|
194
|
+
? "open"
|
|
195
|
+
: "clear";
|
|
196
|
+
}
|
|
197
|
+
function syncCommand(targets) {
|
|
198
|
+
return targets.some((target) => target === "codex" || target === "codex_cli") ? "dna sync --codex" : "dna sync";
|
|
199
|
+
}
|
|
200
|
+
function assetDiffReceiptPath(from, to) {
|
|
201
|
+
const slug = `${from}-to-${to}`.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset-diff";
|
|
202
|
+
return `.dna/reports/asset-diff-${slug}.json`;
|
|
203
|
+
}
|
|
204
|
+
function predecessorAsset(assetName, assets) {
|
|
205
|
+
const activeFollowupMatch = /^(.*)-active-followup$/.exec(assetName);
|
|
206
|
+
if (activeFollowupMatch)
|
|
207
|
+
return assets.find((asset) => asset.name === activeFollowupMatch[1]);
|
|
208
|
+
const versionMatch = /^(.*)-v\d+$/.exec(assetName);
|
|
209
|
+
if (versionMatch)
|
|
210
|
+
return assets.find((asset) => asset.name === versionMatch[1]);
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
function predecessorReviewEvidence(assetName, assets) {
|
|
214
|
+
const predecessor = predecessorAsset(assetName, assets);
|
|
215
|
+
if (!predecessor)
|
|
216
|
+
return [];
|
|
217
|
+
const receiptPathHint = assetDiffReceiptPath(predecessor.name, assetName);
|
|
218
|
+
return [{
|
|
219
|
+
mode: "local_asset_diff_review_evidence",
|
|
220
|
+
authority: "manager_review_handoff_only",
|
|
221
|
+
receiptCommand: `dna assets diff ${predecessor.name} ${assetName} --json > ${receiptPathHint}`,
|
|
222
|
+
receiptPathHint,
|
|
223
|
+
boundaries: [
|
|
224
|
+
"Predecessor diff evidence is a local manager review handoff only; it does not adopt, bind, rollout, sync, publish, or approve runtime behavior.",
|
|
225
|
+
"Manager acceptance of high-risk diffs remains a separate human decision before organization binding or publication readiness.",
|
|
226
|
+
"Runtime sync remains a later local dna action after adoption, organization binding, and rollout review.",
|
|
227
|
+
],
|
|
228
|
+
}];
|
|
229
|
+
}
|
|
230
|
+
function successorReviewEvidence(assetName, assets) {
|
|
231
|
+
const followup = assets.find((asset) => asset.name === `${assetName}-active-followup`);
|
|
232
|
+
if (!followup)
|
|
233
|
+
return [];
|
|
234
|
+
const receiptPathHint = assetDiffReceiptPath(assetName, followup.name);
|
|
235
|
+
return [{
|
|
236
|
+
mode: "local_asset_diff_review_evidence",
|
|
237
|
+
authority: "manager_review_handoff_only",
|
|
238
|
+
receiptCommand: `dna assets diff ${assetName} ${followup.name} --json > ${receiptPathHint}`,
|
|
239
|
+
receiptPathHint,
|
|
240
|
+
boundaries: [
|
|
241
|
+
"Successor diff evidence is a local manager review handoff only; it does not replace the source asset or approve runtime behavior.",
|
|
242
|
+
"The follow-up draft remains inactive until a human reviews the diff, adopts the asset, updates organization binding, and reruns rollout.",
|
|
243
|
+
"Runtime sync remains a later local dna action after adoption, organization binding, and rollout review.",
|
|
244
|
+
],
|
|
245
|
+
}];
|
|
246
|
+
}
|
|
247
|
+
function gateMetadata(asset, assetName, assets, options) {
|
|
248
|
+
return {
|
|
249
|
+
assetVersion: asset?.version,
|
|
250
|
+
contentHash: asset?.contentHash,
|
|
251
|
+
evidencePosture: asset?.status.evidencePosture ?? "unknown",
|
|
252
|
+
reviewRefs: asset?.evidence.map((ref) => ({
|
|
253
|
+
label: ref.label,
|
|
254
|
+
path: ref.path,
|
|
255
|
+
kind: ref.kind,
|
|
256
|
+
description: ref.description,
|
|
257
|
+
})) ?? [],
|
|
258
|
+
reviewEvidence: [
|
|
259
|
+
...predecessorReviewEvidence(assetName, assets),
|
|
260
|
+
...(options?.includeSuccessorReviewEvidence ? successorReviewEvidence(assetName, assets) : []),
|
|
261
|
+
],
|
|
262
|
+
reviewCommand: `dna assets inspect ${assetName} --evidence`,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function assetGate(params) {
|
|
266
|
+
const gate = evidenceGate(params.evidence);
|
|
267
|
+
const metadata = gateMetadata(params.asset, params.binding.asset, params.assets, {
|
|
268
|
+
includeSuccessorReviewEvidence: gate === "open",
|
|
269
|
+
});
|
|
270
|
+
const configured = params.asset?.status.configured === true;
|
|
271
|
+
const configurationPosture = params.asset ? params.asset.status.configurationPosture : "missing";
|
|
272
|
+
if (!params.asset) {
|
|
273
|
+
return {
|
|
274
|
+
asset: params.binding.asset,
|
|
275
|
+
...metadata,
|
|
276
|
+
bindingStatus: params.binding.status,
|
|
277
|
+
configured: false,
|
|
278
|
+
configurationPosture,
|
|
279
|
+
syncTargets: params.syncTargets,
|
|
280
|
+
evidenceGate: gate,
|
|
281
|
+
syncReady: false,
|
|
282
|
+
nextCommand: `dna assets list`,
|
|
283
|
+
reason: "The organization profile references an asset that is not in the local asset catalog.",
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (gate === "open") {
|
|
287
|
+
const followup = activeEvidenceFollowupReviewRoute(params.binding.asset, params.assets, params.evidence);
|
|
288
|
+
return {
|
|
289
|
+
asset: params.binding.asset,
|
|
290
|
+
...metadata,
|
|
291
|
+
bindingStatus: params.binding.status,
|
|
292
|
+
configured,
|
|
293
|
+
configurationPosture,
|
|
294
|
+
syncTargets: params.syncTargets,
|
|
295
|
+
evidenceGate: gate,
|
|
296
|
+
syncReady: false,
|
|
297
|
+
nextCommand: followup.currentCommand,
|
|
298
|
+
reason: `Dogfood evidence still has active work. ${followup.reason}`,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (!configured) {
|
|
302
|
+
return {
|
|
303
|
+
asset: params.binding.asset,
|
|
304
|
+
...metadata,
|
|
305
|
+
bindingStatus: params.binding.status,
|
|
306
|
+
configured,
|
|
307
|
+
configurationPosture,
|
|
308
|
+
syncTargets: params.syncTargets,
|
|
309
|
+
evidenceGate: gate,
|
|
310
|
+
syncReady: false,
|
|
311
|
+
nextCommand: `dna init --template ${params.binding.asset}`,
|
|
312
|
+
reason: "The asset is declared in the organization profile but has not been configured in this project.",
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (params.binding.status !== "active") {
|
|
316
|
+
return {
|
|
317
|
+
asset: params.binding.asset,
|
|
318
|
+
...metadata,
|
|
319
|
+
bindingStatus: params.binding.status,
|
|
320
|
+
configured,
|
|
321
|
+
configurationPosture,
|
|
322
|
+
syncTargets: params.syncTargets,
|
|
323
|
+
evidenceGate: gate,
|
|
324
|
+
syncReady: false,
|
|
325
|
+
nextCommand: `dna org bind --asset ${params.binding.asset} --status active --sync-target codex`,
|
|
326
|
+
reason: "The asset is configured but the organization binding is not active.",
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (params.syncTargets.length === 0) {
|
|
330
|
+
return {
|
|
331
|
+
asset: params.binding.asset,
|
|
332
|
+
...metadata,
|
|
333
|
+
bindingStatus: params.binding.status,
|
|
334
|
+
configured,
|
|
335
|
+
configurationPosture,
|
|
336
|
+
syncTargets: params.syncTargets,
|
|
337
|
+
evidenceGate: gate,
|
|
338
|
+
syncReady: false,
|
|
339
|
+
nextCommand: `dna org bind --asset ${params.binding.asset} --status active --sync-target codex`,
|
|
340
|
+
reason: "The asset is active but no sync target is declared for the project.",
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
asset: params.binding.asset,
|
|
345
|
+
...metadata,
|
|
346
|
+
bindingStatus: params.binding.status,
|
|
347
|
+
configured,
|
|
348
|
+
configurationPosture,
|
|
349
|
+
syncTargets: params.syncTargets,
|
|
350
|
+
evidenceGate: gate,
|
|
351
|
+
syncReady: true,
|
|
352
|
+
nextCommand: syncCommand(params.syncTargets),
|
|
353
|
+
reason: "The asset is configured, active, evidence is not blocking, and at least one sync target is declared.",
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export async function reviewOrganization(options) {
|
|
357
|
+
const projectDir = options?.projectDir ?? process.cwd();
|
|
358
|
+
const { path, profile } = await readOrganizationProfile({ projectDir });
|
|
359
|
+
if (!profile)
|
|
360
|
+
return undefined;
|
|
361
|
+
const assets = await listWorkflowAssets({ projectDir });
|
|
362
|
+
const configured = configuredAssetNames(assets);
|
|
363
|
+
const assetsByName = new Map(assets.map((asset) => [asset.name, asset]));
|
|
364
|
+
const evidence = await Promise.all(assets
|
|
365
|
+
.filter((asset) => profile.projects.some((project) => project.assets.some((binding) => binding.asset === asset.name)))
|
|
366
|
+
.map((asset) => buildWorkflowAssetEvidenceIndex(asset.name, { projectDir })));
|
|
367
|
+
const evidenceIndexes = evidence.filter((entry) => entry !== undefined);
|
|
368
|
+
const evidenceByAsset = new Map(evidenceIndexes.map((entry) => [entry.asset, entry]));
|
|
369
|
+
const projects = profile.projects.map((project) => {
|
|
370
|
+
const declared = project.assets.map((asset) => asset.asset);
|
|
371
|
+
const configuredAssets = declared.filter((asset) => configured.has(asset));
|
|
372
|
+
return {
|
|
373
|
+
id: project.id,
|
|
374
|
+
name: project.name,
|
|
375
|
+
path: project.path,
|
|
376
|
+
configuredAssets,
|
|
377
|
+
plannedAssets: declared,
|
|
378
|
+
missingAssets: declared.filter((asset) => !configured.has(asset)),
|
|
379
|
+
syncTargets: project.syncTargets,
|
|
380
|
+
assetGates: project.assets.map((binding) => assetGate({
|
|
381
|
+
binding,
|
|
382
|
+
asset: assetsByName.get(binding.asset),
|
|
383
|
+
assets,
|
|
384
|
+
evidence: evidenceByAsset.get(binding.asset),
|
|
385
|
+
syncTargets: project.syncTargets,
|
|
386
|
+
})),
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
const nextActions = [];
|
|
390
|
+
for (const project of projects) {
|
|
391
|
+
for (const gate of project.assetGates)
|
|
392
|
+
nextActions.push(`${project.name}/${gate.asset}: ${gate.nextCommand}`);
|
|
393
|
+
if (project.syncTargets.length === 0) {
|
|
394
|
+
nextActions.push(`Declare at least one sync target for project ${project.name}.`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
profilePath: path,
|
|
399
|
+
profile,
|
|
400
|
+
totals: {
|
|
401
|
+
projects: projects.length,
|
|
402
|
+
declaredAssets: projects.reduce((sum, project) => sum + project.plannedAssets.length, 0),
|
|
403
|
+
configuredAssets: projects.reduce((sum, project) => sum + project.configuredAssets.length, 0),
|
|
404
|
+
missingAssets: projects.reduce((sum, project) => sum + project.missingAssets.length, 0),
|
|
405
|
+
syncTargets: projects.reduce((sum, project) => sum + project.syncTargets.length, 0),
|
|
406
|
+
dogfoodEvidenceModules: evidenceIndexes.reduce((sum, index) => sum + index.totals.modules, 0),
|
|
407
|
+
dogfoodEvidencePass: evidenceIndexes.reduce((sum, index) => sum + index.totals.pass, 0),
|
|
408
|
+
dogfoodEvidenceRemaining: evidenceIndexes.reduce((sum, index) => sum + index.totals.remaining, 0),
|
|
409
|
+
},
|
|
410
|
+
projects,
|
|
411
|
+
nextActions,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ConstraintIR, RoleDef } from "../schema/types.js";
|
|
2
|
+
import type { SyncOutputArtifactId } from "./output-artifact-registry.js";
|
|
3
|
+
import { type CompiledIRFile } from "./plugin-adapter.js";
|
|
4
|
+
import { type DNASettings, type HookEventKey } from "./settings-adapter.js";
|
|
5
|
+
import type { AdapterCapabilityReport, CapabilityReportEntry, PlannedArtifact, SyncTargetPlan } from "./sync-target-plan.js";
|
|
6
|
+
export type ClaudeSyncMode = "plugin" | "bin";
|
|
7
|
+
export interface ClaudeCorePlannedArtifact extends PlannedArtifact {
|
|
8
|
+
artifact: SyncOutputArtifactId;
|
|
9
|
+
}
|
|
10
|
+
export type ClaudeCoreCapabilityReportEntry = CapabilityReportEntry;
|
|
11
|
+
export type ClaudeCoreAdapterCapabilityReport = AdapterCapabilityReport & {
|
|
12
|
+
adapter: "claude_code";
|
|
13
|
+
runtime: "claude_code";
|
|
14
|
+
};
|
|
15
|
+
export interface ClaudeSettingsHookRegistration {
|
|
16
|
+
path: string;
|
|
17
|
+
enabledEvents: HookEventKey[];
|
|
18
|
+
settings: DNASettings;
|
|
19
|
+
}
|
|
20
|
+
export interface ClaudeCoreSyncTargetPlanOptions {
|
|
21
|
+
projectDir: string;
|
|
22
|
+
ir: ConstraintIR;
|
|
23
|
+
roles?: Record<string, RoleDef>;
|
|
24
|
+
mode: ClaudeSyncMode;
|
|
25
|
+
settingsPath?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClaudeCoreSyncTargetPlan extends SyncTargetPlan {
|
|
28
|
+
target: "claude_code";
|
|
29
|
+
compiledIR: CompiledIRFile;
|
|
30
|
+
compiledIRPath: string;
|
|
31
|
+
legacyHooksDir: string;
|
|
32
|
+
mcpConfigPath: string;
|
|
33
|
+
settingsHookRegistration?: ClaudeSettingsHookRegistration;
|
|
34
|
+
pluginSkipsSettings: boolean;
|
|
35
|
+
plannedArtifacts: ClaudeCorePlannedArtifact[];
|
|
36
|
+
capabilityReport: ClaudeCoreAdapterCapabilityReport;
|
|
37
|
+
}
|
|
38
|
+
export interface ExecuteClaudeCoreSyncTargetPlanResult {
|
|
39
|
+
messages: string[];
|
|
40
|
+
removedLegacyHooks: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove legacy bash hook scripts written by previous Intent DNA versions.
|
|
44
|
+
* Only removes files that contain the sentinel comment.
|
|
45
|
+
*/
|
|
46
|
+
export declare function removeLegacyBashHooks(hooksDir: string): Promise<string[]>;
|
|
47
|
+
export declare function createClaudeCoreSyncTargetPlan(options: ClaudeCoreSyncTargetPlanOptions): ClaudeCoreSyncTargetPlan;
|
|
48
|
+
export declare function executeClaudeCoreSyncTargetPlan(plan: ClaudeCoreSyncTargetPlan, projectDir: string): Promise<ExecuteClaudeCoreSyncTargetPlanResult>;
|
|
49
|
+
export declare function formatClaudeCoreSyncTargetPlanDryRun(plan: ClaudeCoreSyncTargetPlan): string;
|