intentdna 1.8.7 → 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/candidate-promotion.d.ts +15 -0
- package/dist/cli/commands/candidate-promotion.js +197 -0
- package/dist/cli/commands/run-lifecycle.js +1 -0
- package/dist/cli/commands/run.js +76 -42
- package/dist/cli/commands/setup.d.ts +10 -0
- package/dist/cli/commands/setup.js +129 -36
- package/dist/cli/commands/source-assets.d.ts +32 -0
- package/dist/cli/commands/source-assets.js +154 -0
- package/dist/cli/commands/sync.js +52 -12
- package/dist/cli/commands/templates.js +59 -37
- package/dist/cli/index.js +161 -5
- package/dist/compiler/cascade.js +1 -1
- package/dist/hooks/cli.d.ts +6 -0
- package/dist/hooks/cli.js +118 -10
- 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/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/evaluator.d.ts +13 -0
- package/dist/runtime/evaluator.js +46 -0
- package/dist/runtime/index.d.ts +8 -4
- package/dist/runtime/index.js +4 -2
- package/dist/runtime/plugin-adapter.d.ts +14 -1
- package/dist/runtime/plugin-adapter.js +23 -9
- package/dist/runtime/run-contracts.d.ts +5 -3
- package/dist/runtime/run-contracts.js +11 -1
- package/dist/runtime/run-controller.d.ts +15 -1
- package/dist/runtime/run-controller.js +235 -94
- package/dist/runtime/skill-adapter.js +24 -53
- package/dist/runtime/verifier.js +101 -2
- package/dist/runtime/workflow-plan-adapter.d.ts +2 -1
- package/dist/runtime/workflow-plan-adapter.js +78 -11
- 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 +9 -0
- package/dist/schema/validate.js +29 -1
- package/dist/sources/authoring-packet.d.ts +8 -0
- package/dist/sources/authoring-packet.js +85 -0
- package/dist/sources/discovery.d.ts +7 -0
- package/dist/sources/discovery.js +150 -0
- package/dist/sources/license.d.ts +5 -0
- package/dist/sources/license.js +94 -0
- package/dist/sources/manifest.d.ts +11 -0
- package/dist/sources/manifest.js +84 -0
- package/dist/sources/skill-markdown.d.ts +6 -0
- package/dist/sources/skill-markdown.js +174 -0
- package/dist/sources/types.d.ts +125 -0
- package/dist/sources/types.js +1 -0
- package/dist/templates/asset-refinery.dna.yaml +192 -0
- package/dist/templates/flutter-behavior-lock.dna.yaml +17 -0
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +11 -0
- package/dist/templates/flutter-rewrite.dna.yaml +154 -28
- package/dist/templates/metadata.js +2 -0
- package/dist/templates/multi-agent-handoff-coordination.dna.yaml +102 -37
- package/dist/templates/persistent-executor.dna.yaml +61 -3
- package/dist/templates/research-improvement.dna.yaml +295 -0
- package/dist/templates/subagent-parallel.dna.yaml +90 -17
- package/dist/templates/workflow-evidence-review.dna.yaml +8 -0
- package/dist/workflow/candidate-projection.d.ts +33 -0
- package/dist/workflow/candidate-projection.js +70 -0
- package/package.json +3 -1
- package/scripts/sync-release-plugin.cjs +87 -0
|
@@ -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
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DiscoveredSkillSource, SourceSnapshot } from "../../sources/types.js";
|
|
2
|
+
export interface SourceAssetsOptions {
|
|
3
|
+
homeDir?: string;
|
|
4
|
+
projectDir?: string;
|
|
5
|
+
sourceDirs?: readonly string[];
|
|
6
|
+
customRoots?: readonly string[];
|
|
7
|
+
sourceId?: string;
|
|
8
|
+
snapshot?: boolean;
|
|
9
|
+
packet?: boolean;
|
|
10
|
+
write?: string;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
generatedAt?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SourceAssetsCatalog {
|
|
15
|
+
schema_version: "intentdna.source_catalog.v1";
|
|
16
|
+
generated_at: string;
|
|
17
|
+
sources: DiscoveredSkillSource[];
|
|
18
|
+
network_performed: false;
|
|
19
|
+
mutation_performed: false;
|
|
20
|
+
runtime_authority: false;
|
|
21
|
+
}
|
|
22
|
+
export interface SourceSnapshotCollection {
|
|
23
|
+
schema_version: "intentdna.source_snapshot_collection.v1";
|
|
24
|
+
generated_at: string;
|
|
25
|
+
snapshots: SourceSnapshot[];
|
|
26
|
+
network_performed: false;
|
|
27
|
+
mutation_performed: false;
|
|
28
|
+
runtime_authority: false;
|
|
29
|
+
}
|
|
30
|
+
export declare function buildSourceAssetsCatalog(options?: SourceAssetsOptions): Promise<SourceAssetsCatalog>;
|
|
31
|
+
export declare function buildSourceAssetsSnapshots(options?: SourceAssetsOptions): Promise<SourceSnapshotCollection>;
|
|
32
|
+
export declare function runSourceAssets(options?: SourceAssetsOptions): Promise<number>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { lstat, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { buildSourceAuthoringPacket } from "../../sources/authoring-packet.js";
|
|
4
|
+
import { discoverSkillSources } from "../../sources/discovery.js";
|
|
5
|
+
import { createSourceSnapshot } from "../../sources/manifest.js";
|
|
6
|
+
function unique(values) {
|
|
7
|
+
return [...new Set(values)];
|
|
8
|
+
}
|
|
9
|
+
function inside(root, target) {
|
|
10
|
+
const rel = relative(root, target);
|
|
11
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
12
|
+
}
|
|
13
|
+
function selectSource(sources, selector, preferredRoots = []) {
|
|
14
|
+
const exact = sources.find((source) => source.source_id === selector);
|
|
15
|
+
if (exact)
|
|
16
|
+
return exact;
|
|
17
|
+
const named = sources.filter((source) => source.name === selector);
|
|
18
|
+
for (const preferredRoot of preferredRoots) {
|
|
19
|
+
const root = resolve(preferredRoot);
|
|
20
|
+
const preferred = named.find((source) => {
|
|
21
|
+
const skillDir = resolve(source.skill_dir);
|
|
22
|
+
return skillDir === root || inside(root, skillDir);
|
|
23
|
+
});
|
|
24
|
+
if (preferred)
|
|
25
|
+
return preferred;
|
|
26
|
+
}
|
|
27
|
+
return named[0];
|
|
28
|
+
}
|
|
29
|
+
async function assertNoSymlinkedParents(root, target) {
|
|
30
|
+
const parentRelative = relative(root, dirname(target));
|
|
31
|
+
let current = root;
|
|
32
|
+
for (const segment of parentRelative.split(sep)) {
|
|
33
|
+
if (!segment)
|
|
34
|
+
continue;
|
|
35
|
+
current = resolve(current, segment);
|
|
36
|
+
try {
|
|
37
|
+
const metadata = await lstat(current);
|
|
38
|
+
if (metadata.isSymbolicLink()) {
|
|
39
|
+
throw new Error(`Source artifact parent must not be a symbolic link: ${current}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code === "ENOENT")
|
|
44
|
+
break;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function writePacketOrSnapshot(payload, projectDir, destination) {
|
|
50
|
+
const authoringRoot = resolve(projectDir, ".dna", "authoring");
|
|
51
|
+
const target = resolve(projectDir, destination);
|
|
52
|
+
if (!inside(authoringRoot, target)) {
|
|
53
|
+
throw new Error(`Source artifacts may only be written inside ${authoringRoot}`);
|
|
54
|
+
}
|
|
55
|
+
await assertNoSymlinkedParents(projectDir, target);
|
|
56
|
+
await mkdir(dirname(target), { recursive: true });
|
|
57
|
+
await assertNoSymlinkedParents(projectDir, target);
|
|
58
|
+
await writeFile(target, `${JSON.stringify(payload, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
59
|
+
return target;
|
|
60
|
+
}
|
|
61
|
+
export async function buildSourceAssetsCatalog(options = {}) {
|
|
62
|
+
const generatedAt = options.generatedAt ?? new Date().toISOString();
|
|
63
|
+
const sources = await discoverSkillSources({
|
|
64
|
+
homeDir: options.homeDir,
|
|
65
|
+
projectDir: options.projectDir,
|
|
66
|
+
customRoots: unique([...(options.sourceDirs ?? []), ...(options.customRoots ?? [])]),
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
schema_version: "intentdna.source_catalog.v1",
|
|
70
|
+
generated_at: generatedAt,
|
|
71
|
+
sources,
|
|
72
|
+
network_performed: false,
|
|
73
|
+
mutation_performed: false,
|
|
74
|
+
runtime_authority: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export async function buildSourceAssetsSnapshots(options = {}) {
|
|
78
|
+
const catalog = await buildSourceAssetsCatalog(options);
|
|
79
|
+
const snapshots = await Promise.all(catalog.sources.map((source) => createSourceSnapshot({
|
|
80
|
+
sourceId: source.source_id,
|
|
81
|
+
skillDir: source.skill_dir,
|
|
82
|
+
generatedAt: catalog.generated_at,
|
|
83
|
+
})));
|
|
84
|
+
return {
|
|
85
|
+
schema_version: "intentdna.source_snapshot_collection.v1",
|
|
86
|
+
generated_at: catalog.generated_at,
|
|
87
|
+
snapshots,
|
|
88
|
+
network_performed: false,
|
|
89
|
+
mutation_performed: false,
|
|
90
|
+
runtime_authority: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function runSourceAssets(options = {}) {
|
|
94
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
95
|
+
try {
|
|
96
|
+
const catalog = await buildSourceAssetsCatalog({ ...options, projectDir });
|
|
97
|
+
let payload = catalog;
|
|
98
|
+
if (options.sourceId) {
|
|
99
|
+
const source = selectSource(catalog.sources, options.sourceId, options.sourceDirs);
|
|
100
|
+
if (!source) {
|
|
101
|
+
process.stderr.write(`Unknown source asset: ${options.sourceId}\n`);
|
|
102
|
+
return 2;
|
|
103
|
+
}
|
|
104
|
+
if (!options.packet) {
|
|
105
|
+
process.stderr.write("Error: selecting a source requires --packet.\n");
|
|
106
|
+
return 2;
|
|
107
|
+
}
|
|
108
|
+
payload = await buildSourceAuthoringPacket({
|
|
109
|
+
skillDir: source.skill_dir,
|
|
110
|
+
projectDir,
|
|
111
|
+
generatedAt: catalog.generated_at,
|
|
112
|
+
sourceId: source.source_id,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (options.packet) {
|
|
116
|
+
process.stderr.write("Error: --packet requires a source ID or source name.\n");
|
|
117
|
+
return 2;
|
|
118
|
+
}
|
|
119
|
+
else if (options.snapshot) {
|
|
120
|
+
payload = await buildSourceAssetsSnapshots({ ...options, projectDir, generatedAt: catalog.generated_at });
|
|
121
|
+
}
|
|
122
|
+
if (options.write) {
|
|
123
|
+
if (payload.schema_version === "intentdna.source_catalog.v1") {
|
|
124
|
+
process.stderr.write("Error: --write requires --snapshot or --packet.\n");
|
|
125
|
+
return 2;
|
|
126
|
+
}
|
|
127
|
+
const written = await writePacketOrSnapshot(payload, projectDir, options.write);
|
|
128
|
+
if (options.json) {
|
|
129
|
+
process.stdout.write(`${JSON.stringify({ written, payload }, null, 2)}\n`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
process.stderr.write(`Wrote source artifact: ${written}\n`);
|
|
133
|
+
}
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
if (options.json) {
|
|
137
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
138
|
+
}
|
|
139
|
+
else if (payload.schema_version === "intentdna.source_catalog.v1") {
|
|
140
|
+
process.stderr.write(`Source assets: ${payload.sources.length}\n`);
|
|
141
|
+
for (const source of payload.sources) {
|
|
142
|
+
process.stderr.write(` - ${source.source_id} (${source.skill_markdown_path})\n`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
147
|
+
}
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
152
|
+
return 2;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -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
|
}
|