@opengsd/gsd-core 1.7.0-rc.3 → 1.7.0-rc.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.ja-JP.md +4 -3
- package/README.ko-KR.md +4 -3
- package/README.md +4 -3
- package/README.pt-BR.md +4 -3
- package/README.zh-CN.md +4 -3
- package/bin/install.js +33 -15
- package/commands/gsd/map-codebase.md +4 -4
- package/commands/gsd/ns-project.md +2 -1
- package/commands/gsd/onboard.md +46 -0
- package/gsd-core/bin/gsd-tools.cjs +47 -2
- package/gsd-core/bin/lib/capability-registry.cjs +467 -49
- package/gsd-core/bin/lib/capability-state.cjs +117 -18
- package/gsd-core/bin/lib/capability-writer.cjs +11 -1
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +136 -0
- package/gsd-core/bin/lib/claude-orchestration.cjs +404 -0
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/command-aliases.cjs +8 -0
- package/gsd-core/bin/lib/init-command-router.cjs +4 -0
- package/gsd-core/bin/lib/init.cjs +25 -69
- package/gsd-core/bin/lib/install-engine.cjs +8 -5
- package/gsd-core/bin/lib/install-profiles.cjs +2 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +7 -1
- package/gsd-core/bin/lib/model-resolver.cjs +69 -4
- package/gsd-core/bin/lib/onboard-projection.cjs +309 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/surface.cjs +51 -30
- package/gsd-core/bin/shared/model-catalog.json +5 -0
- package/gsd-core/references/gsd-run-resolver.md +8 -0
- package/gsd-core/templates/project.md +1 -1
- package/gsd-core/workflows/do.md +4 -3
- package/gsd-core/workflows/help/modes/brief.md +2 -1
- package/gsd-core/workflows/help/modes/default.md +2 -1
- package/gsd-core/workflows/help/modes/full.md +11 -1
- package/gsd-core/workflows/help/modes/topic.md +1 -1
- package/gsd-core/workflows/onboard.md +277 -0
- package/gsd-core/workflows/quick.md +20 -0
- package/package.json +1 -1
- package/scripts/gen-golden-install-parity-zcode.cjs +77 -0
- package/scripts/run-tests.cjs +5 -3
- package/scripts/sync-runtime-launcher.cjs +20 -2
- package/skills/gsd-map-codebase/SKILL.md +3 -3
- package/skills/gsd-ns-project/SKILL.md +1 -0
- package/skills/gsd-onboard/SKILL.md +46 -0
|
@@ -45,7 +45,7 @@ const configLoaderMod = require("./config-loader.cjs");
|
|
|
45
45
|
const { loadConfig } = configLoaderMod;
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
47
|
const installProfilesMod = require("./install-profiles.cjs");
|
|
48
|
-
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires } = installProfilesMod;
|
|
48
|
+
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents } = installProfilesMod;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
50
|
const surfaceMod = require("./surface.cjs");
|
|
51
51
|
const { resolveSurface } = surfaceMod;
|
|
@@ -282,22 +282,90 @@ function _loadInstalledSkillsManifest(configDir) {
|
|
|
282
282
|
}
|
|
283
283
|
return manifest;
|
|
284
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* #1858 — Build a skill dependency manifest from a FLAT commands/gsd-<stem>.md
|
|
287
|
+
* source layout (the Claude local project install shape, where the `gsd-`
|
|
288
|
+
* prefix is baked into each filename at the commands/ level and there is no
|
|
289
|
+
* commands/gsd/ subdir). Strips the `gsd-` prefix so stems match the nested
|
|
290
|
+
* loader's output (gsd-validate-phase.md → validate-phase, same as nested
|
|
291
|
+
* validate-phase.md).
|
|
292
|
+
*
|
|
293
|
+
* Map shape is identical to loadSkillsManifest: each stem maps to its
|
|
294
|
+
* `requires` deps (parsed via the same shared parseRequires) and carries a
|
|
295
|
+
* companion `_calls_agents_<stem>` key (parsed via parseCallsAgents) so the
|
|
296
|
+
* flat and nested paths cannot drift.
|
|
297
|
+
*
|
|
298
|
+
* Returns an empty Map when the parent directory does not exist or contains
|
|
299
|
+
* no gsd-*.md files (so _resolveManifest can use size>0 as the "flat layout
|
|
300
|
+
* present" signal and fall through to the installed-skills branch otherwise).
|
|
301
|
+
*/
|
|
302
|
+
function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
303
|
+
const manifest = new Map();
|
|
304
|
+
let entries;
|
|
305
|
+
try {
|
|
306
|
+
entries = node_fs_1.default.readdirSync(commandsParentDir, { withFileTypes: true });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return manifest;
|
|
310
|
+
}
|
|
311
|
+
for (const entry of entries) {
|
|
312
|
+
if (!entry.isFile())
|
|
313
|
+
continue;
|
|
314
|
+
if (!entry.name.startsWith('gsd-'))
|
|
315
|
+
continue;
|
|
316
|
+
if (!entry.name.endsWith('.md'))
|
|
317
|
+
continue;
|
|
318
|
+
// Strip 'gsd-' prefix (4 chars) and '.md' suffix (3 chars) → stem.
|
|
319
|
+
const stem = entry.name.slice(4, -3);
|
|
320
|
+
if (!stem)
|
|
321
|
+
continue;
|
|
322
|
+
// Mirror loadSkillsManifest's try/catch structure exactly: wrap read +
|
|
323
|
+
// parse + set together so an unreadable file OR a thrown parser degrades
|
|
324
|
+
// both keys to [] (parity; closes the latent catch-scope drift a reviewer
|
|
325
|
+
// flagged — both parsers are non-throwing today, but the structural
|
|
326
|
+
// match future-proofs the "identical Map shape" contract).
|
|
327
|
+
try {
|
|
328
|
+
const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsParentDir, entry.name), 'utf8');
|
|
329
|
+
manifest.set(stem, parseRequires(content));
|
|
330
|
+
manifest.set(`_calls_agents_${stem}`, parseCallsAgents(content));
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
manifest.set(stem, []);
|
|
334
|
+
manifest.set(`_calls_agents_${stem}`, []);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return manifest;
|
|
338
|
+
}
|
|
285
339
|
/**
|
|
286
340
|
* Resolve the skill dependency manifest for capability-state resolution.
|
|
287
341
|
*
|
|
288
|
-
* Resolution order
|
|
289
|
-
* 1. If commandsGsdDir exists, load from source
|
|
290
|
-
*
|
|
342
|
+
* Resolution order:
|
|
343
|
+
* 1. If commandsGsdDir exists, load from the nested source layout
|
|
344
|
+
* (repo-checkout behavior: <repo>/commands/gsd/*.md).
|
|
345
|
+
* 2. #1858 — otherwise, if the flat source layout is present (gsd-<stem>.md
|
|
346
|
+
* files in dirname(commandsGsdDir)), load from there. This is the Claude
|
|
347
|
+
* local project install shape where commands/gsd/ does not exist but
|
|
348
|
+
* commands/gsd-<stem>.md files do.
|
|
349
|
+
* 3. #1160 — otherwise, fall back to installed skills at
|
|
350
|
+
* configDir/skills/gsd-[stem]/SKILL.md.
|
|
291
351
|
*
|
|
292
|
-
* In an installed runtime
|
|
293
|
-
*
|
|
294
|
-
* materialise the full-sentinel to an empty Set, making every
|
|
295
|
-
* unsurfaced even when the skill was physically installed
|
|
352
|
+
* In an installed runtime both source trees are absent; only the skills/
|
|
353
|
+
* layout exists. Returning an empty manifest caused resolveSurface to
|
|
354
|
+
* materialise the full-sentinel to an empty Set, making every skill-bearing
|
|
355
|
+
* capability appear unsurfaced even when the skill was physically installed
|
|
356
|
+
* (#1160) or authored as a flat command file (#1858).
|
|
296
357
|
*/
|
|
297
358
|
function _resolveManifest(commandsGsdDir, configDir) {
|
|
298
359
|
if (node_fs_1.default.existsSync(commandsGsdDir)) {
|
|
299
360
|
return loadSkillsManifest(commandsGsdDir);
|
|
300
361
|
}
|
|
362
|
+
// #1858: flat source layout — gsd-<stem>.md files at dirname(commandsGsdDir).
|
|
363
|
+
// Only claim the flat branch when it actually has gsd-*.md files; otherwise
|
|
364
|
+
// fall through to the installed-skills branch (a commands/ dir with no gsd
|
|
365
|
+
// files must not shadow an installed skills/ tree).
|
|
366
|
+
const flat = _loadFlatCommandsGsdManifest(node_path_1.default.dirname(commandsGsdDir));
|
|
367
|
+
if (flat.size > 0)
|
|
368
|
+
return flat;
|
|
301
369
|
return _loadInstalledSkillsManifest(configDir);
|
|
302
370
|
}
|
|
303
371
|
/**
|
|
@@ -334,7 +402,7 @@ function _resolveManifest(commandsGsdDir, configDir) {
|
|
|
334
402
|
* @param raw Whether to emit raw JSON (io.output raw mode)
|
|
335
403
|
* @param _options Reserved for future use
|
|
336
404
|
*/
|
|
337
|
-
function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
405
|
+
function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride, runtimeOverride) {
|
|
338
406
|
const warnings = [];
|
|
339
407
|
// Resolve runtimeConfigDir using the canonical runtime-homes resolver.
|
|
340
408
|
// When not provided, the active runtime is detected via the canonical
|
|
@@ -350,13 +418,40 @@ function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
|
350
418
|
try {
|
|
351
419
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
352
420
|
const runtimeHomes = require('./runtime-homes.cjs');
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
|
|
421
|
+
// #2003: an explicit --runtime override bypasses the persisted-runtime
|
|
422
|
+
// fallback (GSD_RUNTIME → config.runtime → 'claude') so, e.g., a repo with
|
|
423
|
+
// persisted runtime:"codex" resolves the Claude config dir when the operator
|
|
424
|
+
// is driving from Claude Code. Canonicalize via runtime-name-policy (handles
|
|
425
|
+
// aliases like codex-app → codex); if canonicalization yields nothing, fall
|
|
426
|
+
// through to the persisted-runtime resolution below. Mirrors the update-
|
|
427
|
+
// context / effort sync precedent (read/diagnostic paths accepting both
|
|
428
|
+
// --config-dir and --runtime).
|
|
429
|
+
if (typeof runtimeOverride === 'string' && runtimeOverride.trim() !== '') {
|
|
430
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
431
|
+
const runtimeNamePolicy = require('./runtime-name-policy.cjs');
|
|
432
|
+
const canonical = runtimeNamePolicy.canonicalizeRuntimeName(runtimeOverride);
|
|
433
|
+
if (canonical) {
|
|
434
|
+
resolvedConfigDir = runtimeHomes.getGlobalConfigDir(canonical);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// #2003: unknown runtime override — warn (don't silently ignore the
|
|
438
|
+
// explicit input) and fall through to persisted-runtime resolution.
|
|
439
|
+
// Avoids a silent-wrong-result on this diagnostic command for typos
|
|
440
|
+
// (e.g. "cluade") or runtimes known to runtime-homes but not yet to
|
|
441
|
+
// the alias manifest (e.g. "grok"). The warning surfaces via the
|
|
442
|
+
// `warnings[]` channel consumed by cmdCapabilityState/cmdLoopRenderHooks.
|
|
443
|
+
warnings.push(`--runtime "${runtimeOverride}" is not a known runtime; falling back to auto-detected/persisted runtime resolution`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!resolvedConfigDir) {
|
|
447
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
448
|
+
const runtimeSlash = require('./runtime-slash.cjs');
|
|
449
|
+
// Detect the active runtime via GSD_RUNTIME → config.runtime → 'claude'.
|
|
450
|
+
// resolveRuntime reads config.json directly (no side effects) and returns
|
|
451
|
+
// a lowercased canonical runtime name.
|
|
452
|
+
const detectedRuntime = runtimeSlash.resolveRuntime(cwd);
|
|
453
|
+
resolvedConfigDir = runtimeHomes.getGlobalConfigDir(detectedRuntime);
|
|
454
|
+
}
|
|
360
455
|
}
|
|
361
456
|
catch {
|
|
362
457
|
// Defensive fallback: use ~/.claude if the canonical resolver throws.
|
|
@@ -450,8 +545,11 @@ function resolveCapabilityRuntimeState(cwd, runtimeConfigDir, configOverride) {
|
|
|
450
545
|
capabilities: result.capabilities,
|
|
451
546
|
};
|
|
452
547
|
}
|
|
453
|
-
function cmdCapabilityState(cwd, runtimeConfigDir, raw,
|
|
454
|
-
|
|
548
|
+
function cmdCapabilityState(cwd, runtimeConfigDir, raw, options = {}) {
|
|
549
|
+
// #2003: thread an explicit --runtime override so the config-dir resolution
|
|
550
|
+
// bypasses the persisted-runtime fallback (GSD_RUNTIME → config.runtime).
|
|
551
|
+
const runtimeOverride = typeof options['runtime'] === 'string' ? options['runtime'] : undefined;
|
|
552
|
+
const result = resolveCapabilityRuntimeState(cwd, runtimeConfigDir, undefined, runtimeOverride);
|
|
455
553
|
for (const warning of result.warnings) {
|
|
456
554
|
coreError(`capability state: ${warning}`);
|
|
457
555
|
}
|
|
@@ -491,6 +589,7 @@ module.exports = {
|
|
|
491
589
|
// Exported for tests
|
|
492
590
|
_resolveCommandsGsdDir,
|
|
493
591
|
_loadInstalledSkillsManifest,
|
|
592
|
+
_loadFlatCommandsGsdManifest,
|
|
494
593
|
_resolveManifest,
|
|
495
594
|
_isSafePropKey,
|
|
496
595
|
};
|
|
@@ -250,8 +250,18 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
250
250
|
const layout = runtimeArtifactLayout.resolveRuntimeArtifactLayout(runtime, resolvedConfigDir, scope);
|
|
251
251
|
const commandsGsdDir = _resolveCommandsGsdDir();
|
|
252
252
|
const manifest = _resolveManifest(commandsGsdDir, resolvedConfigDir);
|
|
253
|
+
// #1575: applySurface now accepts opts.resolveAttribution so surface-path
|
|
254
|
+
// agents get the same Co-Authored-By trailer as the install path. The
|
|
255
|
+
// resolver is not threaded here yet — the CLI command handler does not have
|
|
256
|
+
// access to getCommitAttribution (which lives in bin/install.js). Until that
|
|
257
|
+
// is refactored into a shared module, surface-path agents for descriptor-
|
|
258
|
+
// driven runtimes will lack the Co-Authored-By trailer that install adds.
|
|
259
|
+
// Parity is proven when resolveAttribution IS provided (see
|
|
260
|
+
// tests/issue-1575-agent-descriptor-parity.test.cjs).
|
|
253
261
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
254
|
-
applySurface(resolvedConfigDir, layout, manifest, undefined, registry
|
|
262
|
+
applySurface(resolvedConfigDir, layout, manifest, undefined, registry, opts?.materialize?.resolveAttribution
|
|
263
|
+
? { resolveAttribution: opts.materialize.resolveAttribution }
|
|
264
|
+
: undefined);
|
|
255
265
|
}
|
|
256
266
|
catch (err) {
|
|
257
267
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude orchestration command router — CLI dispatcher for
|
|
4
|
+
* `gsd-tools claude-orchestration <subcommand>`.
|
|
5
|
+
*
|
|
6
|
+
* #1143 — thin CLI adapter over the pure `claude-orchestration.cjs` module.
|
|
7
|
+
* Lets execute-phase (or any orchestrator) invoke the Workflow-backend
|
|
8
|
+
* detection and the Workflow-script emitter through the standard capability
|
|
9
|
+
* command surface (ADR-959) instead of a bare `require()`.
|
|
10
|
+
*
|
|
11
|
+
* Router signature: { args, cwd, raw, error } — identical to the other host
|
|
12
|
+
* routers; discovered by dispatchCapabilityCommand via the registry's
|
|
13
|
+
* commandFamilies index.
|
|
14
|
+
*
|
|
15
|
+
* Subcommands:
|
|
16
|
+
* detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]
|
|
17
|
+
* Resolves whether the Workflow backend should activate. `--runtime`
|
|
18
|
+
* defaults to the GSD_RUNTIME env var (or 'unknown'). Reads the
|
|
19
|
+
* `claude_orchestration.*` keys from .planning/config.json. Emits
|
|
20
|
+
* { available, backend, reason }.
|
|
21
|
+
*
|
|
22
|
+
* emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>]
|
|
23
|
+
* Reads a wave/plan manifest JSON file and emits the generated Workflow
|
|
24
|
+
* script + summary. The manifest shape matches emitWorkflowScript's input:
|
|
25
|
+
* { waves: [{ id, plans: [{ id, brief, files_modified: string[] }] }] }.
|
|
26
|
+
*/
|
|
27
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
28
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
|
+
};
|
|
30
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
31
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
const io = require("./io.cjs");
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
|
+
const core = require("./claude-orchestration.cjs");
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const configLoader = require("./config-loader.cjs");
|
|
38
|
+
const { output } = io;
|
|
39
|
+
const { detectWorkflowBackend, emitWorkflowScript } = core;
|
|
40
|
+
const CAPABLE_HOST = { dispatch: { nested: true, background: true } };
|
|
41
|
+
function usage(error) {
|
|
42
|
+
error('Usage: gsd-tools claude-orchestration <detect-backend|emit-workflow> [...]\n' +
|
|
43
|
+
' detect-backend [--runtime <id>] [--agent-sdk-version <ver>] [--no-nested-dispatch]\n' +
|
|
44
|
+
' emit-workflow --waves <path> --run-id <id> [--phase-dir <dir>] [--budget <n>]');
|
|
45
|
+
}
|
|
46
|
+
function argValue(args, flag) {
|
|
47
|
+
const i = args.indexOf(flag);
|
|
48
|
+
return i !== -1 && i + 1 < args.length ? args[i + 1] : undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect whether the Workflow backend should activate for the current/given
|
|
52
|
+
* runtime. Reads `claude_orchestration.*` from the project config; runtime and
|
|
53
|
+
* SDK version come from flags (the orchestrator already knows these) or env.
|
|
54
|
+
*/
|
|
55
|
+
function cmdDetectBackend(args, cwd, raw) {
|
|
56
|
+
const runtimeId = argValue(args, '--runtime') || process.env['GSD_RUNTIME'] || 'unknown';
|
|
57
|
+
const agentSdkVersion = argValue(args, '--agent-sdk-version');
|
|
58
|
+
const noNested = args.includes('--no-nested-dispatch');
|
|
59
|
+
const hostIntegration = noNested ? { dispatch: { nested: false, background: true } } : CAPABLE_HOST;
|
|
60
|
+
// Resolve the claude_orchestration.* slice from the project config (federated
|
|
61
|
+
// keys are merged by loadConfig as a nested object). A config read failure
|
|
62
|
+
// degrades to inline — it must not break the core loop.
|
|
63
|
+
let claudeSlice = {};
|
|
64
|
+
try {
|
|
65
|
+
const loaded = configLoader.loadConfig(cwd);
|
|
66
|
+
const slice = loaded['claude_orchestration'];
|
|
67
|
+
if (slice && typeof slice === 'object' && !Array.isArray(slice)) {
|
|
68
|
+
claudeSlice = slice;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
claudeSlice = {};
|
|
73
|
+
}
|
|
74
|
+
// Flatten the nested slice into the dotted-key shape detectWorkflowBackend expects.
|
|
75
|
+
const flatConfig = {};
|
|
76
|
+
for (const k of Object.keys(claudeSlice)) {
|
|
77
|
+
flatConfig['claude_orchestration.' + k] = claudeSlice[k];
|
|
78
|
+
}
|
|
79
|
+
const result = detectWorkflowBackend({ runtimeId, hostIntegration, config: flatConfig, agentSdkVersion });
|
|
80
|
+
output(result, raw);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Emit a Workflow script from a wave/plan manifest file.
|
|
84
|
+
*/
|
|
85
|
+
function cmdEmitWorkflow(args, _cwd, raw, error) {
|
|
86
|
+
const wavesPath = argValue(args, '--waves');
|
|
87
|
+
const runId = argValue(args, '--run-id');
|
|
88
|
+
const phaseDir = argValue(args, '--phase-dir') || '.planning/phases/current';
|
|
89
|
+
const budgetRaw = argValue(args, '--budget');
|
|
90
|
+
if (!wavesPath) {
|
|
91
|
+
error('emit-workflow requires --waves <path>');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!runId) {
|
|
95
|
+
error('emit-workflow requires --run-id <id>');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let waves;
|
|
99
|
+
try {
|
|
100
|
+
const content = node_fs_1.default.readFileSync(node_path_1.default.resolve(wavesPath), 'utf8');
|
|
101
|
+
const parsed = JSON.parse(content);
|
|
102
|
+
waves = parsed['waves'];
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
error('emit-workflow: could not read/parse --waves file "' + wavesPath + '": ' + (e instanceof Error ? e.message : String(e)));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const budgetTokens = budgetRaw !== undefined ? parseInt(budgetRaw, 10) : undefined;
|
|
109
|
+
const budget = (typeof budgetTokens === 'number' && !Number.isNaN(budgetTokens)) ? budgetTokens : undefined;
|
|
110
|
+
const result = emitWorkflowScript({
|
|
111
|
+
phaseDir,
|
|
112
|
+
runId,
|
|
113
|
+
waves: waves,
|
|
114
|
+
budgetTokens: budget,
|
|
115
|
+
});
|
|
116
|
+
if (!result.ok) {
|
|
117
|
+
error('emit-workflow: ' + result.reason);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
output({ script: result.script, summary: result.summary }, raw);
|
|
121
|
+
}
|
|
122
|
+
function routeClaudeOrchestrationCommand(opts) {
|
|
123
|
+
const { args, cwd, raw, error } = opts;
|
|
124
|
+
// args[0] is the family ('claude-orchestration'); the subcommand is args[1].
|
|
125
|
+
const subcommand = args[1];
|
|
126
|
+
if (subcommand === 'detect-backend') {
|
|
127
|
+
cmdDetectBackend(args, cwd, raw);
|
|
128
|
+
}
|
|
129
|
+
else if (subcommand === 'emit-workflow') {
|
|
130
|
+
cmdEmitWorkflow(args, cwd, raw, error);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
usage(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
module.exports = { routeClaudeOrchestrationCommand };
|