intentdna 1.9.0-rc.1 → 1.9.0-rc.2
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/cli/commands/run.js +72 -40
- package/dist/cli/commands/setup.d.ts +10 -0
- package/dist/cli/commands/setup.js +129 -36
- package/dist/cli/commands/sync.js +52 -12
- package/dist/cli/index.js +0 -0
- package/dist/hooks/cli.js +44 -4
- package/dist/hooks/enforcement-boundary.d.ts +1 -1
- package/dist/hooks/protocol.d.ts +1 -1
- package/dist/hooks/state.d.ts +5 -0
- package/dist/hooks/state.js +21 -6
- package/dist/mcp/index.js +0 -0
- package/dist/mcp/tools-state.js +251 -15
- package/dist/runtime/claude-sync-target.d.ts +2 -0
- package/dist/runtime/claude-sync-target.js +1 -1
- package/dist/runtime/diagnosis-contract-verifier.d.ts +11 -0
- package/dist/runtime/diagnosis-contract-verifier.js +417 -0
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/plugin-adapter.d.ts +14 -1
- package/dist/runtime/plugin-adapter.js +23 -9
- package/dist/runtime/skill-adapter.js +24 -53
- package/dist/runtime/workflow-runtime-manifest.d.ts +48 -0
- package/dist/runtime/workflow-runtime-manifest.js +196 -0
- package/dist/runtime/workflow-variable-scopes.d.ts +12 -0
- package/dist/runtime/workflow-variable-scopes.js +55 -0
- package/dist/schema/types.d.ts +4 -0
- package/dist/templates/flutter-behavior-lock.dna.yaml +17 -0
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +11 -0
- package/dist/templates/workflow-evidence-review.dna.yaml +8 -0
- package/package.json +3 -1
- package/scripts/sync-release-plugin.cjs +87 -0
|
@@ -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.0-rc.
|
|
12
|
+
"version": "1.9.0-rc.2",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Samuel"
|
|
15
15
|
},
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"version": "1.9.0-rc.
|
|
28
|
+
"version": "1.9.0-rc.2"
|
|
29
29
|
}
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -10,12 +10,13 @@ import { isAbsolute, join, relative, resolve, sep, } from "node:path";
|
|
|
10
10
|
import { cascadeDNA } from "../../compiler/cascade.js";
|
|
11
11
|
import { compileFromFiles, expandDNAInputFiles, loadDNA, } from "../../compiler/index.js";
|
|
12
12
|
import { compileWorkflow } from "../../compiler/workflow.js";
|
|
13
|
-
import { trustedEvidenceCaptureAttribution } from "../../governance/index.js";
|
|
14
13
|
import { DNAStateManager } from "../../hooks/state-manager.js";
|
|
15
14
|
import { appendEvidenceCaptureEvent, EVIDENCE_CAPTURE_SCHEMA_VERSION, } from "../../hooks/state.js";
|
|
16
15
|
import { compileAllRolesToAgentMD, toKebabCase, writeAgentMDFiles, } from "../../runtime/agent-md.js";
|
|
17
16
|
import { HandoffResolver } from "../../runtime/handoff-resolver.js";
|
|
18
17
|
import { createCompiledIR, writeCompiledIR } from "../../runtime/plugin-adapter.js";
|
|
18
|
+
import { createWorkflowRuntimeManifest, WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES, } from "../../runtime/workflow-runtime-manifest.js";
|
|
19
|
+
import { defaultResolvedVariableScopes, indexWorkflowVariableScopes, resolvedVariablesForNamespace, } from "../../runtime/workflow-variable-scopes.js";
|
|
19
20
|
import { ImmutableResultStore } from "../../runtime/result-store.js";
|
|
20
21
|
import { RunController, } from "../../runtime/run-controller.js";
|
|
21
22
|
import { DurableRunStore, RunStoreError, } from "../../runtime/run-store.js";
|
|
@@ -191,6 +192,8 @@ async function compileRunAssets(metadata) {
|
|
|
191
192
|
for (const file of metadata.dna_files)
|
|
192
193
|
dnas.push(await loadDNA(file));
|
|
193
194
|
const cascaded = cascadeDNA(dnas);
|
|
195
|
+
const variableScopeIndex = indexWorkflowVariableScopes(dnas);
|
|
196
|
+
const defaultVariableScopes = defaultResolvedVariableScopes(variableScopeIndex);
|
|
194
197
|
const ir = await compileFromFiles([...metadata.dna_files], {
|
|
195
198
|
context: metadata.context ?? undefined,
|
|
196
199
|
});
|
|
@@ -199,26 +202,31 @@ async function compileRunAssets(metadata) {
|
|
|
199
202
|
throw new Error(`workflow '${metadata.workflow_name}' not found; available: `
|
|
200
203
|
+ `${Object.keys(cascaded.workflows).join(", ") || "(none)"}`);
|
|
201
204
|
}
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
const runtimePlans = [];
|
|
206
|
+
for (const [workflowKey, workflowDefinition] of Object.entries(cascaded.workflows)) {
|
|
207
|
+
const result = compileWorkflow(workflowDefinition, { workflow_key: workflowKey });
|
|
208
|
+
if (!result.ok || !result.plan) {
|
|
209
|
+
throw new Error(`workflow '${workflowKey}' compilation failed:\n`
|
|
210
|
+
+ result.errors.map((error) => ` ${error.path}: ${error.message}`).join("\n"));
|
|
211
|
+
}
|
|
212
|
+
const namespace = variableScopeIndex.workflowNamespaces.get(workflowKey) ?? "";
|
|
213
|
+
runtimePlans.push({
|
|
214
|
+
plan: result.plan,
|
|
215
|
+
workflow_asset: variableScopeIndex.workflowAssets.get(workflowKey) ?? null,
|
|
216
|
+
resolved_variables: resolvedVariablesForNamespace(defaultVariableScopes, namespace, workflowKey === metadata.workflow_name ? { ...metadata.variables } : {}),
|
|
217
|
+
});
|
|
208
218
|
}
|
|
209
|
-
const plan =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const workflowAsset = attribution.workflowAsset === "unknown"
|
|
214
|
-
? null
|
|
215
|
-
: attribution.workflowAsset;
|
|
219
|
+
const plan = runtimePlans.find((item) => item.plan.workflow_key === metadata.workflow_name)?.plan;
|
|
220
|
+
if (!plan)
|
|
221
|
+
throw new Error(`compiled workflow '${metadata.workflow_name}' is missing from runtime plans`);
|
|
222
|
+
const workflowAsset = runtimePlans.find((item) => item.plan.workflow_key === metadata.workflow_name)?.workflow_asset ?? null;
|
|
216
223
|
if (metadata.workflow_asset !== null
|
|
217
224
|
&& workflowAsset !== metadata.workflow_asset) {
|
|
218
225
|
throw new Error(`workflow asset changed from '${metadata.workflow_asset}' to '${workflowAsset ?? "unknown"}'`);
|
|
219
226
|
}
|
|
220
227
|
return {
|
|
221
228
|
plan,
|
|
229
|
+
runtime_plans: runtimePlans,
|
|
222
230
|
roles: cascaded.roles,
|
|
223
231
|
ir,
|
|
224
232
|
plan_digest: planDigest(plan, cascaded.roles),
|
|
@@ -227,6 +235,20 @@ async function compileRunAssets(metadata) {
|
|
|
227
235
|
}
|
|
228
236
|
async function compileCanonicalRun(metadata) {
|
|
229
237
|
const assets = await compileRunAssets(metadata);
|
|
238
|
+
if (!assets.ir.compiled_ir_hash) {
|
|
239
|
+
throw new Error("Compiled IR hash is required for canonical workflow runtime bindings");
|
|
240
|
+
}
|
|
241
|
+
const workflowRuntime = createWorkflowRuntimeManifest({
|
|
242
|
+
plans: assets.runtime_plans.map((runtimePlan) => ({
|
|
243
|
+
plan: runtimePlan.plan,
|
|
244
|
+
resolvedVariables: runtimePlan.resolved_variables,
|
|
245
|
+
workflowAsset: runtimePlan.workflow_asset ?? undefined,
|
|
246
|
+
additionalRuntimeValues: assets.ir.verifier_specs?.filter((spec) => spec.workflow_name === runtimePlan.plan.workflow_key) ?? [],
|
|
247
|
+
})),
|
|
248
|
+
compiledAt: assets.ir.compiled_at,
|
|
249
|
+
compiledIrHash: assets.ir.compiled_ir_hash,
|
|
250
|
+
sourceDnaIds: assets.ir.source_dna_ids,
|
|
251
|
+
});
|
|
230
252
|
const adapterOptions = {
|
|
231
253
|
roles: assets.roles,
|
|
232
254
|
variables: metadata.variables,
|
|
@@ -236,6 +258,7 @@ async function compileCanonicalRun(metadata) {
|
|
|
236
258
|
};
|
|
237
259
|
return {
|
|
238
260
|
...assets,
|
|
261
|
+
workflow_runtime: workflowRuntime,
|
|
239
262
|
definitions: adaptWorkflowPlan(assets.plan, adapterOptions),
|
|
240
263
|
retry_loop: adaptWorkflowRetryLoop(assets.plan, adapterOptions),
|
|
241
264
|
};
|
|
@@ -243,7 +266,7 @@ async function compileCanonicalRun(metadata) {
|
|
|
243
266
|
async function syncRuntimeArtifacts(compiled, metadata) {
|
|
244
267
|
const agents = compileAllRolesToAgentMD(compiled.roles, compiled.ir);
|
|
245
268
|
await writeAgentMDFiles(agents, metadata.agents_directory);
|
|
246
|
-
await writeCompiledIR(metadata.project_directory, createCompiledIR(compiled.ir, compiled.roles));
|
|
269
|
+
await writeCompiledIR(metadata.project_directory, createCompiledIR(compiled.ir, compiled.roles, compiled.workflow_runtime));
|
|
247
270
|
}
|
|
248
271
|
function projectPathInWorkspace(metadata, workspaceDirectory, projectPath) {
|
|
249
272
|
const projectRelative = relative(metadata.project_directory, projectPath);
|
|
@@ -262,7 +285,7 @@ async function syncAttemptRuntimeArtifacts(compiled, metadata, workspace) {
|
|
|
262
285
|
const agents = compileAllRolesToAgentMD(compiled.roles, compiled.ir);
|
|
263
286
|
await writeAgentMDFiles(agents, agentsDirectory);
|
|
264
287
|
}
|
|
265
|
-
await writeCompiledIR(workspace.working_directory, createCompiledIR(compiled.ir, compiled.roles));
|
|
288
|
+
await writeCompiledIR(workspace.working_directory, createCompiledIR(compiled.ir, compiled.roles, compiled.workflow_runtime));
|
|
266
289
|
}
|
|
267
290
|
function createProvider(metadata) {
|
|
268
291
|
if (metadata.provider.kind === "codex") {
|
|
@@ -462,31 +485,40 @@ function createWorkspaceAwareExecutor(metadata, compiled, runStore) {
|
|
|
462
485
|
let lifecycleRecorded = false;
|
|
463
486
|
try {
|
|
464
487
|
await syncAttemptRuntimeArtifacts(compiled, metadata, workspace);
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
started_at: packet.created_at,
|
|
479
|
-
inputs: { ...metadata.variables },
|
|
480
|
-
resolved_variables: {
|
|
481
|
-
...metadata.variables,
|
|
482
|
-
round: String(packet.workflow_round),
|
|
483
|
-
},
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
catch (error) {
|
|
487
|
-
log(`Workflow evidence state unavailable for ${packet.step_id}; `
|
|
488
|
-
+ `continuing fail-open: ${error instanceof Error ? error.message : String(error)}`);
|
|
488
|
+
const runtimeBinding = compiled.workflow_runtime.workflows[metadata.workflow_name];
|
|
489
|
+
if (!runtimeBinding)
|
|
490
|
+
throw new Error(`Missing runtime binding for ${metadata.workflow_name}`);
|
|
491
|
+
const canonicalArguments = metadata.variables.ARGUMENTS ??
|
|
492
|
+
metadata.variables.arguments ?? metadata.variables.task_id;
|
|
493
|
+
const runtimeInputs = {};
|
|
494
|
+
for (const name of runtimeBinding.runtime_input_refs) {
|
|
495
|
+
const value = WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES.has(name)
|
|
496
|
+
? canonicalArguments
|
|
497
|
+
: metadata.variables[name];
|
|
498
|
+
if (value === undefined)
|
|
499
|
+
throw new Error(`Missing runtime input ${name} for ${metadata.workflow_name}`);
|
|
500
|
+
runtimeInputs[name] = value;
|
|
489
501
|
}
|
|
502
|
+
await state.writeWorkflowState({
|
|
503
|
+
active: true,
|
|
504
|
+
workflow_asset: runtimeBinding.workflow_asset,
|
|
505
|
+
workflow: metadata.workflow_name,
|
|
506
|
+
current_step: packet.step_id,
|
|
507
|
+
current_role: packet.role,
|
|
508
|
+
iteration: packet.workflow_round,
|
|
509
|
+
session_id: packet.worker_session_id,
|
|
510
|
+
worker_session_id: packet.worker_session_id,
|
|
511
|
+
run_id: packet.run_id,
|
|
512
|
+
step_id: packet.step_id,
|
|
513
|
+
attempt_id: packet.attempt_id,
|
|
514
|
+
started_at: packet.created_at,
|
|
515
|
+
inputs: Object.keys(runtimeInputs).length > 0 ? runtimeInputs : undefined,
|
|
516
|
+
resolved_variables: runtimeBinding.resolved_variables,
|
|
517
|
+
resolved_variables_source: "compiled_manifest",
|
|
518
|
+
workflow_runtime_manifest_hash: compiled.workflow_runtime.manifest_hash,
|
|
519
|
+
workflow_runtime_compiled_ir_hash: compiled.workflow_runtime.compiled_ir_hash,
|
|
520
|
+
workflow_inputs_pinned: true,
|
|
521
|
+
});
|
|
490
522
|
const inheritedProcessObserver = options?.onProcessStart;
|
|
491
523
|
let execution = await executeWorkerAttempt(workerPacket, provider, {
|
|
492
524
|
...options,
|
|
@@ -19,4 +19,14 @@ export interface SetupOptions {
|
|
|
19
19
|
yes: boolean;
|
|
20
20
|
upgrade: boolean;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if intentdna is already registered as a plugin.
|
|
24
|
+
*/
|
|
25
|
+
export interface InstalledPluginInfo {
|
|
26
|
+
version: string | null;
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function parseInstalledPluginInfo(output: string): InstalledPluginInfo | null;
|
|
30
|
+
export declare function releaseChannelForVersion(version: string | null): "latest" | "next";
|
|
31
|
+
export declare function shouldInstallLatestVersion(installedVersion: string, latestVersion: string): boolean;
|
|
22
32
|
export declare function runSetup(opts: SetupOptions): Promise<number>;
|
|
@@ -79,21 +79,32 @@ async function resolvePackagePath() {
|
|
|
79
79
|
}
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
export function parseInstalledPluginInfo(output) {
|
|
83
|
+
const pluginId = "intentdna@intentdna";
|
|
84
|
+
const pluginStart = output.indexOf(pluginId);
|
|
85
|
+
if (pluginStart === -1)
|
|
86
|
+
return null;
|
|
87
|
+
const block = output.slice(pluginStart).split(/\n\s*\n/, 1)[0];
|
|
88
|
+
return {
|
|
89
|
+
version: block.match(/Version:\s*([^\s]+)/)?.[1] ?? null,
|
|
90
|
+
enabled: /Status:\s*.*\benabled\b/i.test(block),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function getInstalledPluginInfo() {
|
|
86
94
|
try {
|
|
87
95
|
const result = execSync("claude plugin list 2>/dev/null", {
|
|
88
96
|
encoding: "utf-8",
|
|
89
97
|
timeout: 5000,
|
|
90
98
|
});
|
|
91
|
-
return result
|
|
99
|
+
return parseInstalledPluginInfo(result);
|
|
92
100
|
}
|
|
93
101
|
catch {
|
|
94
|
-
return
|
|
102
|
+
return null;
|
|
95
103
|
}
|
|
96
104
|
}
|
|
105
|
+
function isPluginRegistered() {
|
|
106
|
+
return getInstalledPluginInfo() !== null;
|
|
107
|
+
}
|
|
97
108
|
// ── Main ──────────────────────────────────────────────────
|
|
98
109
|
/**
|
|
99
110
|
* Get the currently installed version of intentdna.
|
|
@@ -111,12 +122,10 @@ function getInstalledVersion() {
|
|
|
111
122
|
return null;
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
*/
|
|
117
|
-
function getLatestVersion() {
|
|
125
|
+
/** Get the version currently published on an npm dist-tag. */
|
|
126
|
+
function getPublishedVersion(channel) {
|
|
118
127
|
try {
|
|
119
|
-
return execSync(
|
|
128
|
+
return execSync(`npm view intentdna@${channel} version 2>/dev/null`, {
|
|
120
129
|
encoding: "utf-8",
|
|
121
130
|
timeout: 10000,
|
|
122
131
|
}).trim();
|
|
@@ -125,8 +134,63 @@ function getLatestVersion() {
|
|
|
125
134
|
return null;
|
|
126
135
|
}
|
|
127
136
|
}
|
|
137
|
+
function parseReleaseVersion(version) {
|
|
138
|
+
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
139
|
+
if (!match)
|
|
140
|
+
return null;
|
|
141
|
+
return {
|
|
142
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
143
|
+
prerelease: match[4]?.split(".") ?? null,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function comparePrerelease(left, right) {
|
|
147
|
+
if (left === null && right === null)
|
|
148
|
+
return 0;
|
|
149
|
+
if (left === null)
|
|
150
|
+
return 1;
|
|
151
|
+
if (right === null)
|
|
152
|
+
return -1;
|
|
153
|
+
const length = Math.max(left.length, right.length);
|
|
154
|
+
for (let index = 0; index < length; index += 1) {
|
|
155
|
+
const leftPart = left[index];
|
|
156
|
+
const rightPart = right[index];
|
|
157
|
+
if (leftPart === undefined)
|
|
158
|
+
return -1;
|
|
159
|
+
if (rightPart === undefined)
|
|
160
|
+
return 1;
|
|
161
|
+
if (leftPart === rightPart)
|
|
162
|
+
continue;
|
|
163
|
+
const leftNumeric = /^\d+$/.test(leftPart);
|
|
164
|
+
const rightNumeric = /^\d+$/.test(rightPart);
|
|
165
|
+
if (leftNumeric && rightNumeric)
|
|
166
|
+
return Number(leftPart) < Number(rightPart) ? -1 : 1;
|
|
167
|
+
if (leftNumeric !== rightNumeric)
|
|
168
|
+
return leftNumeric ? -1 : 1;
|
|
169
|
+
return leftPart < rightPart ? -1 : 1;
|
|
170
|
+
}
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
export function releaseChannelForVersion(version) {
|
|
174
|
+
if (!version)
|
|
175
|
+
return "latest";
|
|
176
|
+
return parseReleaseVersion(version)?.prerelease ? "next" : "latest";
|
|
177
|
+
}
|
|
178
|
+
export function shouldInstallLatestVersion(installedVersion, latestVersion) {
|
|
179
|
+
if (installedVersion === latestVersion)
|
|
180
|
+
return false;
|
|
181
|
+
const installed = parseReleaseVersion(installedVersion);
|
|
182
|
+
const latest = parseReleaseVersion(latestVersion);
|
|
183
|
+
if (!installed || !latest)
|
|
184
|
+
return true;
|
|
185
|
+
for (let index = 0; index < installed.core.length; index += 1) {
|
|
186
|
+
if (installed.core[index] === latest.core[index])
|
|
187
|
+
continue;
|
|
188
|
+
return installed.core[index] < latest.core[index];
|
|
189
|
+
}
|
|
190
|
+
return comparePrerelease(installed.prerelease, latest.prerelease) < 0;
|
|
191
|
+
}
|
|
128
192
|
/**
|
|
129
|
-
* Upgrade flow:
|
|
193
|
+
* Upgrade flow: install from the current release channel → refresh marketplace → update and verify plugin.
|
|
130
194
|
*/
|
|
131
195
|
async function runUpgrade(opts) {
|
|
132
196
|
// Step 1: Detect claude CLI
|
|
@@ -138,20 +202,26 @@ async function runUpgrade(opts) {
|
|
|
138
202
|
log(`Claude Code detected: ${claudeVersion}`);
|
|
139
203
|
// Step 2: Check versions
|
|
140
204
|
const currentVer = getInstalledVersion();
|
|
141
|
-
const
|
|
205
|
+
const releaseChannel = releaseChannelForVersion(currentVer);
|
|
206
|
+
const publishedVer = getPublishedVersion(releaseChannel);
|
|
142
207
|
if (currentVer)
|
|
143
208
|
log(`Current version: ${currentVer}`);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
209
|
+
log(`Target channel: ${releaseChannel}`);
|
|
210
|
+
if (publishedVer)
|
|
211
|
+
log(`Target version: ${publishedVer}`);
|
|
212
|
+
if (!publishedVer) {
|
|
213
|
+
log(`Upgrade failed: could not resolve intentdna@${releaseChannel} from npm.`);
|
|
214
|
+
return 1;
|
|
215
|
+
}
|
|
216
|
+
if (currentVer && !shouldInstallLatestVersion(currentVer, publishedVer)) {
|
|
217
|
+
log(`Already at the ${releaseChannel} channel version or newer; refusing to downgrade.`);
|
|
148
218
|
}
|
|
149
219
|
else {
|
|
150
220
|
// Step 3: npm update
|
|
151
221
|
log("");
|
|
152
|
-
log(
|
|
222
|
+
log(`Updating intentdna from the ${releaseChannel} channel...`);
|
|
153
223
|
try {
|
|
154
|
-
execSync(
|
|
224
|
+
execSync(`npm install -g intentdna@${releaseChannel}`, {
|
|
155
225
|
encoding: "utf-8",
|
|
156
226
|
timeout: 60000,
|
|
157
227
|
stdio: ["inherit", "pipe", "pipe"],
|
|
@@ -162,10 +232,19 @@ async function runUpgrade(opts) {
|
|
|
162
232
|
catch (err) {
|
|
163
233
|
const msg = err instanceof Error ? err.message : String(err);
|
|
164
234
|
log(`npm update failed: ${msg}`);
|
|
165
|
-
log(
|
|
235
|
+
log(`Try manually: npm install -g intentdna@${releaseChannel}`);
|
|
166
236
|
return 1;
|
|
167
237
|
}
|
|
168
238
|
}
|
|
239
|
+
const upgradedVersion = getInstalledVersion();
|
|
240
|
+
if (!upgradedVersion) {
|
|
241
|
+
log("Upgrade failed: could not verify the installed npm package version.");
|
|
242
|
+
return 1;
|
|
243
|
+
}
|
|
244
|
+
if (shouldInstallLatestVersion(upgradedVersion, publishedVer)) {
|
|
245
|
+
log(`Upgrade failed: installed npm version ${upgradedVersion} is older than ${releaseChannel} channel version ${publishedVer}.`);
|
|
246
|
+
return 1;
|
|
247
|
+
}
|
|
169
248
|
// Step 4: Re-resolve package path
|
|
170
249
|
const pkgPath = await resolvePackagePath();
|
|
171
250
|
if (!pkgPath) {
|
|
@@ -173,11 +252,11 @@ async function runUpgrade(opts) {
|
|
|
173
252
|
return 1;
|
|
174
253
|
}
|
|
175
254
|
log(`Package path: ${pkgPath}`);
|
|
176
|
-
// Step 5:
|
|
255
|
+
// Step 5: Refresh marketplace (picks up new plugin.json version)
|
|
177
256
|
try {
|
|
178
257
|
log("");
|
|
179
|
-
log(
|
|
180
|
-
execSync(
|
|
258
|
+
log("Refreshing marketplace: intentdna");
|
|
259
|
+
execSync("claude plugin marketplace update intentdna", {
|
|
181
260
|
encoding: "utf-8",
|
|
182
261
|
timeout: 30000,
|
|
183
262
|
stdio: ["inherit", "pipe", "pipe"],
|
|
@@ -186,35 +265,49 @@ async function runUpgrade(opts) {
|
|
|
186
265
|
}
|
|
187
266
|
catch (err) {
|
|
188
267
|
const msg = err instanceof Error ? err.message : String(err);
|
|
189
|
-
log(`
|
|
268
|
+
log(`Marketplace refresh failed: ${msg}`);
|
|
269
|
+
log(`Try manually: claude plugin marketplace add "${pkgPath}"`);
|
|
270
|
+
return 1;
|
|
190
271
|
}
|
|
191
|
-
// Step 6:
|
|
272
|
+
// Step 6: Update plugin (picks up new hooks.json and MCP runtime)
|
|
192
273
|
try {
|
|
193
|
-
const
|
|
194
|
-
log(`
|
|
195
|
-
execSync(
|
|
274
|
+
const updateCmd = "claude plugin update intentdna@intentdna";
|
|
275
|
+
log(`Updating plugin: ${updateCmd}`);
|
|
276
|
+
execSync(updateCmd, {
|
|
196
277
|
encoding: "utf-8",
|
|
197
278
|
timeout: 30000,
|
|
198
279
|
stdio: ["inherit", "pipe", "pipe"],
|
|
199
280
|
});
|
|
200
|
-
log("Plugin
|
|
281
|
+
log("Plugin updated.");
|
|
201
282
|
}
|
|
202
283
|
catch (err) {
|
|
203
284
|
const msg = err instanceof Error ? err.message : String(err);
|
|
204
|
-
log(`
|
|
285
|
+
log(`Plugin update failed: ${msg}`);
|
|
286
|
+
log(`Try manually: claude plugin install intentdna@intentdna --scope ${opts.scope}`);
|
|
287
|
+
return 1;
|
|
205
288
|
}
|
|
206
289
|
// Step 7: Verify
|
|
207
|
-
|
|
290
|
+
const plugin = getInstalledPluginInfo();
|
|
291
|
+
if (!plugin?.version) {
|
|
208
292
|
log("");
|
|
209
|
-
log("Upgrade
|
|
210
|
-
|
|
293
|
+
log("Upgrade failed: the IntentDNA Claude plugin version could not be verified.");
|
|
294
|
+
log("Try: claude plugin list");
|
|
295
|
+
return 1;
|
|
211
296
|
}
|
|
212
|
-
|
|
297
|
+
if (plugin.version !== upgradedVersion) {
|
|
213
298
|
log("");
|
|
214
|
-
log(
|
|
215
|
-
|
|
299
|
+
log(`Upgrade failed: Claude plugin version ${plugin.version} does not match npm version ${upgradedVersion}.`);
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
if (!plugin.enabled) {
|
|
303
|
+
log("");
|
|
304
|
+
log("Upgrade failed: the version-matched IntentDNA Claude plugin is disabled.");
|
|
216
305
|
return 1;
|
|
217
306
|
}
|
|
307
|
+
log("");
|
|
308
|
+
log(`Upgrade complete: npm and Claude plugin are both ${upgradedVersion}.`);
|
|
309
|
+
log("Restart Claude Code, then run 'dna sync' in each project before resuming workflows.");
|
|
310
|
+
return 0;
|
|
218
311
|
}
|
|
219
312
|
export async function runSetup(opts) {
|
|
220
313
|
// Upgrade mode
|
|
@@ -28,6 +28,8 @@ import { removeWorkflowScripts } from "../../runtime/workflow-runner.js";
|
|
|
28
28
|
import { compileWorkflowToSkill, compileControllerToSkill, writeSkillFiles, removeSkillFiles } from "../../runtime/skill-adapter.js";
|
|
29
29
|
import { getControllerKindMetadata } from "../../schema/controller-registry.js";
|
|
30
30
|
import { createCodexSyncTargetPlan, executeCodexSyncTargetPlan, formatCodexSyncTargetPlanSummary, removeCodexSyncTargetArtifacts } from "../../runtime/codex-sync-target.js";
|
|
31
|
+
import { createWorkflowRuntimeManifest } from "../../runtime/workflow-runtime-manifest.js";
|
|
32
|
+
import { flattenResolvedVariableScopes, indexWorkflowVariableScopes, resolvedVariablesForNamespace, } from "../../runtime/workflow-variable-scopes.js";
|
|
31
33
|
import { trackedOutputsForSyncTargetPlan } from "../../runtime/sync-target-plan.js";
|
|
32
34
|
import { getPackageVersion } from "../util/version.js";
|
|
33
35
|
import { cascadeDNA } from "../../compiler/cascade.js";
|
|
@@ -167,6 +169,24 @@ async function resolveVariables(rawVars, configPath, options) {
|
|
|
167
169
|
}
|
|
168
170
|
return resolved;
|
|
169
171
|
}
|
|
172
|
+
async function resolveVariableScopes(dnas, configPath, options) {
|
|
173
|
+
const index = indexWorkflowVariableScopes(dnas);
|
|
174
|
+
const variableNamespaces = [...index.rawByNamespace.entries()]
|
|
175
|
+
.filter(([, raw]) => Object.keys(raw).length > 0)
|
|
176
|
+
.map(([namespace]) => namespace);
|
|
177
|
+
const resolvedByNamespace = new Map();
|
|
178
|
+
for (const [namespace, raw] of index.rawByNamespace) {
|
|
179
|
+
const writableConfigPath = variableNamespaces.length === 1 ? configPath : null;
|
|
180
|
+
resolvedByNamespace.set(namespace, await resolveVariables(raw, writableConfigPath, options));
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
flat: flattenResolvedVariableScopes(index, resolvedByNamespace),
|
|
184
|
+
workflowNamespaces: index.workflowNamespaces,
|
|
185
|
+
workflowAssets: index.workflowAssets,
|
|
186
|
+
controllerNamespaces: index.controllerNamespaces,
|
|
187
|
+
forNamespace: (namespace) => resolvedVariablesForNamespace(resolvedByNamespace, namespace),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
170
190
|
/**
|
|
171
191
|
* Auto-detect DNA config file in current directory (legacy single-config).
|
|
172
192
|
* Priority: .dna/config.yaml > .dna/config.yml > .dna/config.json > .dna.yaml > .dna.yml > .dna.json
|
|
@@ -783,13 +803,39 @@ export async function runSync(opts) {
|
|
|
783
803
|
trackedByLock: true,
|
|
784
804
|
});
|
|
785
805
|
}
|
|
806
|
+
const cascadedForRuntime = cascadeDNA(loadedDNAs);
|
|
807
|
+
const variableConfigPath = opts.files.length === 1 ? opts.files[0] : null;
|
|
808
|
+
const variableScopes = await resolveVariableScopes(loadedDNAs, variableConfigPath, { dryRun: opts.dryRun });
|
|
809
|
+
const variables = variableScopes.flat;
|
|
810
|
+
const workflowPlans = [];
|
|
811
|
+
for (const [workflowKey, workflowDef] of Object.entries(cascadedForRuntime.workflows)) {
|
|
812
|
+
const result = compileWorkflow(workflowDef, { workflow_key: workflowKey });
|
|
813
|
+
if (!result.ok || !result.plan) {
|
|
814
|
+
throw new Error(`Workflow '${workflowKey}' compilation failed:\n${result.errors.map((error) => ` ${error.path}: ${error.message}`).join("\n")}`);
|
|
815
|
+
}
|
|
816
|
+
workflowPlans.push(result.plan);
|
|
817
|
+
}
|
|
818
|
+
if (!ir.compiled_ir_hash) {
|
|
819
|
+
throw new Error("Compiled IR hash is required for workflow runtime bindings");
|
|
820
|
+
}
|
|
821
|
+
const workflowRuntime = createWorkflowRuntimeManifest({
|
|
822
|
+
plans: workflowPlans.map((plan) => ({
|
|
823
|
+
plan,
|
|
824
|
+
resolvedVariables: variableScopes.forNamespace(variableScopes.workflowNamespaces.get(plan.workflow_key ?? plan.source_workflow) ?? ""),
|
|
825
|
+
workflowAsset: variableScopes.workflowAssets.get(plan.workflow_key ?? plan.source_workflow),
|
|
826
|
+
additionalRuntimeValues: ir.verifier_specs?.filter((spec) => spec.workflow_name === (plan.workflow_key ?? plan.source_workflow)) ?? [],
|
|
827
|
+
})),
|
|
828
|
+
compiledAt: ir.compiled_at,
|
|
829
|
+
compiledIrHash: ir.compiled_ir_hash,
|
|
830
|
+
sourceDnaIds: ir.source_dna_ids,
|
|
831
|
+
});
|
|
786
832
|
// Step 3.5: Execute Claude Code core target plan (compiled IR, MCP, settings hooks)
|
|
787
833
|
{
|
|
788
|
-
const cascadedForCore = cascadeDNA(loadedDNAs);
|
|
789
834
|
const claudeCorePlan = createClaudeCoreSyncTargetPlan({
|
|
790
835
|
projectDir,
|
|
791
836
|
ir,
|
|
792
|
-
roles:
|
|
837
|
+
roles: cascadedForRuntime.roles,
|
|
838
|
+
workflowRuntime,
|
|
793
839
|
mode: opts.plugin ? "plugin" : "bin",
|
|
794
840
|
settingsPath: opts.settingsPath,
|
|
795
841
|
});
|
|
@@ -913,14 +959,6 @@ export async function runSync(opts) {
|
|
|
913
959
|
const workflows = cascadedForSkills.workflows;
|
|
914
960
|
const controllers = cascadedForSkills.controllers;
|
|
915
961
|
const roles = cascadedForSkills.roles;
|
|
916
|
-
// Collect and resolve variables from all DNA files
|
|
917
|
-
const rawVars = {};
|
|
918
|
-
for (const d of dnas) {
|
|
919
|
-
if (d.variables)
|
|
920
|
-
Object.assign(rawVars, d.variables);
|
|
921
|
-
}
|
|
922
|
-
const configPath = opts.files.length === 1 ? opts.files[0] : null;
|
|
923
|
-
const variables = await resolveVariables(rawVars, configPath, { dryRun: opts.dryRun });
|
|
924
962
|
// Step 8: Generate project-root .mcp.json only for Claude Code skills.
|
|
925
963
|
if (skillSurface === "claude_code") {
|
|
926
964
|
const mcpDeps = {};
|
|
@@ -947,14 +985,16 @@ export async function runSync(opts) {
|
|
|
947
985
|
for (const [name, wfDef] of Object.entries(workflows)) {
|
|
948
986
|
const result = compileWorkflow(wfDef, { workflow_key: name });
|
|
949
987
|
if (result.ok) {
|
|
950
|
-
|
|
988
|
+
const workflowVariables = variableScopes.forNamespace(variableScopes.workflowNamespaces.get(name) ?? "");
|
|
989
|
+
skillResults.push(compileWorkflowToSkill(result.plan, roles, ir, workflowVariables, { surface: skillSurface }));
|
|
951
990
|
}
|
|
952
991
|
}
|
|
953
992
|
if (skillSurface === "claude_code") {
|
|
954
993
|
for (const [name, controllerDef] of Object.entries(controllers)) {
|
|
955
994
|
const controllerKind = getControllerKindMetadata(controllerDef.kind);
|
|
956
995
|
if (controllerKind?.supportsSkillGeneration) {
|
|
957
|
-
|
|
996
|
+
const controllerVariables = variableScopes.forNamespace(variableScopes.controllerNamespaces.get(name) ?? "");
|
|
997
|
+
skillResults.push(compileControllerToSkill(name, controllerDef, controllerVariables, workflows));
|
|
958
998
|
}
|
|
959
999
|
}
|
|
960
1000
|
}
|
package/dist/cli/index.js
CHANGED
|
File without changes
|