claude-code-workflow 7.2.30 → 7.3.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/commands/workflow-skill.md +130 -0
- package/.claude/skills/ccw-chain/SKILL.md +44 -71
- package/.claude/skills/ccw-chain/chains/ccw-cycle.json +13 -3
- package/.claude/skills/ccw-chain/chains/ccw-exploration.json +33 -22
- package/.claude/skills/ccw-chain/chains/ccw-issue.json +23 -12
- package/.claude/skills/ccw-chain/chains/ccw-lightweight.json +23 -9
- package/.claude/skills/ccw-chain/chains/ccw-main.json +15 -2
- package/.claude/skills/ccw-chain/chains/ccw-standard.json +28 -16
- package/.claude/skills/ccw-chain/chains/ccw-team.json +7 -2
- package/.claude/skills/ccw-chain/chains/ccw-with-file.json +25 -9
- package/.claude/skills/chain-loader/phases/01-analyze-skill.md +53 -53
- package/.claude/skills/chain-loader/specs/chain-schema.md +30 -3
- package/.claude/skills/chain-loader/templates/chain-json.md +63 -63
- package/.claude/skills/workflow-plan/SKILL.md +1 -0
- package/.claude/skills/workflow-plan/phases/01-session-discovery.md +19 -2
- package/.claude/skills/workflow-plan/phases/02-context-gathering.md +2 -2
- package/.claude/skills/workflow-plan/phases/04-task-generation.md +9 -1
- package/.codex/skills/analyze-with-file/SKILL.md +383 -134
- package/.codex/skills/brainstorm/SKILL.md +3 -3
- package/.codex/skills/brainstorm-with-file/SKILL.md +208 -88
- package/.codex/skills/clean/SKILL.md +1 -1
- package/.codex/skills/csv-wave-pipeline/SKILL.md +2 -2
- package/.codex/skills/investigate/orchestrator.md +24 -0
- package/.codex/skills/issue-discover/SKILL.md +374 -361
- package/.codex/skills/issue-discover/phases/01-issue-new.md +1 -1
- package/.codex/skills/issue-discover/phases/02-discover.md +2 -2
- package/.codex/skills/issue-discover/phases/03-discover-by-prompt.md +1 -1
- package/.codex/skills/issue-discover/phases/04-quick-execute.md +2 -2
- package/.codex/skills/parallel-dev-cycle/SKILL.md +44 -37
- package/.codex/skills/project-documentation-workflow/SKILL.md +1 -1
- package/.codex/skills/review-cycle/SKILL.md +31 -12
- package/.codex/skills/roadmap-with-file/SKILL.md +141 -133
- package/.codex/skills/security-audit/orchestrator.md +29 -0
- package/.codex/skills/session-sync/SKILL.md +1 -1
- package/.codex/skills/ship/orchestrator.md +24 -0
- package/.codex/skills/spec-add/SKILL.md +5 -5
- package/.codex/skills/spec-generator/SKILL.md +33 -2
- package/.codex/skills/spec-generator/phases/01-5-requirement-clarification.md +3 -3
- package/.codex/skills/spec-generator/phases/01-discovery.md +1 -1
- package/.codex/skills/spec-generator/phases/02-product-brief.md +1 -1
- package/.codex/skills/spec-generator/phases/03-requirements.md +1 -1
- package/.codex/skills/spec-generator/phases/04-architecture.md +1 -1
- package/.codex/skills/spec-generator/phases/05-epics-stories.md +1 -1
- package/.codex/skills/spec-generator/phases/06-readiness-check.md +1 -1
- package/.codex/skills/spec-generator/phases/07-issue-export.md +1 -1
- package/.codex/skills/spec-setup/SKILL.md +669 -669
- package/.codex/skills/team-arch-opt/specs/team-config.json +1 -1
- package/.codex/skills/team-brainstorm/SKILL.md +259 -259
- package/.codex/skills/team-coordinate/SKILL.md +359 -359
- package/.codex/skills/team-coordinate/roles/coordinator/commands/monitor.md +1 -1
- package/.codex/skills/team-designer/SKILL.md +27 -1
- package/.codex/skills/team-designer/phases/01-requirements-analysis.md +2 -2
- package/.codex/skills/team-designer/phases/02-scaffold-generation.md +1 -1
- package/.codex/skills/team-designer/phases/04-validation.md +1 -1
- package/.codex/skills/team-executor/SKILL.md +218 -218
- package/.codex/skills/team-frontend/SKILL.md +227 -227
- package/.codex/skills/team-frontend-debug/SKILL.md +278 -278
- package/.codex/skills/team-frontend-debug/roles/coordinator/commands/analyze.md +2 -2
- package/.codex/skills/team-interactive-craft/SKILL.md +220 -220
- package/.codex/skills/team-interactive-craft/roles/coordinator/role.md +209 -209
- package/.codex/skills/team-issue/SKILL.md +269 -269
- package/.codex/skills/team-issue/roles/coordinator/role.md +1 -1
- package/.codex/skills/team-lifecycle-v4/SKILL.md +305 -305
- package/.codex/skills/team-motion-design/SKILL.md +222 -222
- package/.codex/skills/team-motion-design/roles/coordinator/role.md +210 -210
- package/.codex/skills/team-perf-opt/SKILL.md +258 -258
- package/.codex/skills/team-perf-opt/specs/team-config.json +1 -1
- package/.codex/skills/team-planex/SKILL.md +216 -216
- package/.codex/skills/team-quality-assurance/SKILL.md +229 -229
- package/.codex/skills/team-review/SKILL.md +227 -227
- package/.codex/skills/team-roadmap-dev/SKILL.md +238 -238
- package/.codex/skills/team-roadmap-dev/roles/coordinator/commands/roadmap-discuss.md +5 -5
- package/.codex/skills/team-tech-debt/SKILL.md +206 -206
- package/.codex/skills/team-tech-debt/roles/coordinator/commands/monitor.md +1 -1
- package/.codex/skills/team-testing/SKILL.md +237 -237
- package/.codex/skills/team-ui-polish/SKILL.md +218 -218
- package/.codex/skills/team-ui-polish/roles/coordinator/role.md +213 -213
- package/.codex/skills/team-uidesign/SKILL.md +219 -219
- package/.codex/skills/team-uidesign/roles/coordinator/role.md +2 -2
- package/.codex/skills/team-ultra-analyze/SKILL.md +260 -260
- package/.codex/skills/team-ultra-analyze/roles/coordinator/commands/monitor.md +1 -1
- package/.codex/skills/team-ultra-analyze/roles/coordinator/role.md +1 -1
- package/.codex/skills/team-ux-improve/SKILL.md +227 -227
- package/.codex/skills/team-ux-improve/roles/coordinator/role.md +1 -1
- package/.codex/skills/team-ux-improve/specs/team-config.json +1 -1
- package/.codex/skills/team-visual-a11y/SKILL.md +319 -319
- package/.codex/skills/team-visual-a11y/roles/coordinator/role.md +213 -213
- package/.codex/skills/workflow-execute/SKILL.md +5 -5
- package/.codex/skills/workflow-lite-planex/SKILL.md +3 -3
- package/.codex/skills/workflow-plan/SKILL.md +3 -3
- package/.codex/skills/workflow-tdd-plan/SKILL.md +4 -4
- package/.codex/skills/workflow-test-fix-cycle/SKILL.md +403 -402
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +16 -0
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/chain-loader.d.ts +2 -0
- package/ccw/dist/commands/chain-loader.d.ts.map +1 -0
- package/ccw/dist/commands/chain-loader.js +11 -0
- package/ccw/dist/commands/chain-loader.js.map +1 -0
- package/ccw/dist/commands/install.d.ts.map +1 -1
- package/ccw/dist/commands/install.js +52 -1
- package/ccw/dist/commands/install.js.map +1 -1
- package/ccw/dist/commands/launcher.d.ts +2 -0
- package/ccw/dist/commands/launcher.d.ts.map +1 -0
- package/ccw/dist/commands/launcher.js +434 -0
- package/ccw/dist/commands/launcher.js.map +1 -0
- package/ccw/dist/tools/chain-loader.d.ts.map +1 -1
- package/ccw/dist/tools/chain-loader.js +457 -45
- package/ccw/dist/tools/chain-loader.js.map +1 -1
- package/ccw/dist/tools/skill-context-loader.d.ts.map +1 -1
- package/ccw/dist/tools/skill-context-loader.js +12 -26
- package/ccw/dist/tools/skill-context-loader.js.map +1 -1
- package/ccw/dist/types/chain-types.d.ts +41 -1
- package/ccw/dist/types/chain-types.d.ts.map +1 -1
- package/ccw/dist/utils/chain-visualizer.d.ts +13 -0
- package/ccw/dist/utils/chain-visualizer.d.ts.map +1 -0
- package/ccw/dist/utils/chain-visualizer.js +164 -0
- package/ccw/dist/utils/chain-visualizer.js.map +1 -0
- package/package.json +1 -1
- package/.claude/commands/cli/cli-init.md +0 -441
- package/.claude/commands/cli/codex-review.md +0 -361
- package/.claude/commands/flow-create.md +0 -663
- package/.claude/skills/ccw-chain/phases/analyze-with-file.md +0 -788
- package/.claude/skills/ccw-chain/phases/brainstorm/SKILL.md +0 -408
- package/.claude/skills/ccw-chain/phases/brainstorm/phases/01-mode-routing.md +0 -207
- package/.claude/skills/ccw-chain/phases/brainstorm/phases/02-artifacts.md +0 -567
- package/.claude/skills/ccw-chain/phases/brainstorm/phases/03-role-analysis.md +0 -748
- package/.claude/skills/ccw-chain/phases/brainstorm/phases/04-synthesis.md +0 -827
- package/.claude/skills/ccw-chain/phases/brainstorm-with-file.md +0 -482
- package/.claude/skills/ccw-chain/phases/collaborative-plan-with-file.md +0 -639
- package/.claude/skills/ccw-chain/phases/debug-with-file.md +0 -656
- package/.claude/skills/ccw-chain/phases/integration-test-cycle.md +0 -936
- package/.claude/skills/ccw-chain/phases/issue-convert-to-plan.md +0 -720
- package/.claude/skills/ccw-chain/phases/issue-discover.md +0 -483
- package/.claude/skills/ccw-chain/phases/issue-execute.md +0 -629
- package/.claude/skills/ccw-chain/phases/issue-from-brainstorm.md +0 -382
- package/.claude/skills/ccw-chain/phases/issue-plan.md +0 -343
- package/.claude/skills/ccw-chain/phases/issue-queue.md +0 -464
- package/.claude/skills/ccw-chain/phases/refactor-cycle.md +0 -852
- package/.claude/skills/ccw-chain/phases/review-cycle/SKILL.md +0 -132
- package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-fix.md +0 -760
- package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-module.md +0 -764
- package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-session.md +0 -775
- package/.claude/skills/ccw-chain/phases/roadmap-with-file.md +0 -544
- package/.claude/skills/ccw-chain/phases/spec-generator/SKILL.md +0 -338
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-5-requirement-clarification.md +0 -404
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-discovery.md +0 -257
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/02-product-brief.md +0 -274
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/03-requirements.md +0 -184
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/04-architecture.md +0 -248
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/05-epics-stories.md +0 -178
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-5-auto-fix.md +0 -144
- package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-readiness-check.md +0 -480
- package/.claude/skills/ccw-chain/phases/team-planex.md +0 -123
- package/.claude/skills/ccw-chain/phases/ui-design-explore-auto.md +0 -678
- package/.claude/skills/ccw-chain/phases/unified-execute-with-file.md +0 -870
- package/.claude/skills/ccw-chain/phases/workflow-execute/SKILL.md +0 -625
- package/.claude/skills/ccw-chain/phases/workflow-execute/phases/06-review.md +0 -215
- package/.claude/skills/ccw-chain/phases/workflow-lite-plan.md +0 -616
- package/.claude/skills/ccw-chain/phases/workflow-multi-cli-plan.md +0 -424
- package/.claude/skills/ccw-chain/phases/workflow-plan/SKILL.md +0 -466
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/01-session-discovery.md +0 -99
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/02-context-gathering.md +0 -338
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/03-conflict-resolution.md +0 -422
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/04-task-generation.md +0 -440
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/05-plan-verify.md +0 -395
- package/.claude/skills/ccw-chain/phases/workflow-plan/phases/06-replan.md +0 -594
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/SKILL.md +0 -527
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/01-session-discovery.md +0 -57
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/02-context-gathering.md +0 -407
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/03-test-coverage-analysis.md +0 -172
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/04-conflict-resolution.md +0 -426
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/05-tdd-task-generation.md +0 -473
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/06-tdd-structure-validation.md +0 -189
- package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/07-tdd-verify.md +0 -635
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/SKILL.md +0 -482
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/01-session-start.md +0 -60
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/02-test-context-gather.md +0 -493
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/03-test-concept-enhanced.md +0 -150
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/04-test-task-generate.md +0 -346
- package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/05-test-cycle-execute.md +0 -538
- package/.claude/skills/ccw-chain/specs/intent-patterns.md +0 -60
- package/.claude/skills/team-edict.zip +0 -0
|
@@ -5,36 +5,43 @@
|
|
|
5
5
|
* Commands: list, start, next, done, status, content, complete
|
|
6
6
|
*/
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
+
import { renderChainTopology, renderChainProgress } from '../utils/chain-visualizer.js';
|
|
8
9
|
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, } from 'fs';
|
|
9
10
|
import { resolve, join, dirname } from 'path';
|
|
10
11
|
import { homedir } from 'os';
|
|
11
12
|
// ─── Params ──────────────────────────────────────────────────
|
|
12
13
|
const ParamsSchema = z.object({
|
|
13
|
-
cmd: z.enum(['list', 'start', 'next', 'done', 'status', 'content', 'complete']),
|
|
14
|
+
cmd: z.enum(['list', 'start', 'next', 'done', 'status', 'content', 'complete', 'inspect', 'visualize']),
|
|
14
15
|
skill: z.string().optional(),
|
|
15
16
|
chain: z.string().optional(),
|
|
16
17
|
session_id: z.string().optional(),
|
|
17
18
|
choice: z.number().optional(),
|
|
19
|
+
node: z.string().optional(), // start from specific node
|
|
20
|
+
entry_name: z.string().optional(), // select named entry
|
|
18
21
|
});
|
|
19
22
|
// ─── Tool Schema ─────────────────────────────────────────────
|
|
20
23
|
export const schema = {
|
|
21
24
|
name: 'chain_loader',
|
|
22
25
|
description: `Progressive skill chain loader. Auto-detects skill from cwd (cd to skill dir first) or pass skill explicitly.
|
|
23
|
-
list: List chains. Params: skill? (string
|
|
24
|
-
|
|
26
|
+
list: List chains with triggers/entries. Params: skill? (string)
|
|
27
|
+
inspect: Show chain node graph with topology. Params: chain (string), skill? (string)
|
|
28
|
+
start: Start chain session (resolves preload, initializes variables). Params: chain (string), skill? (string), node? (string), entry_name? (string)
|
|
25
29
|
next: Read current node (idempotent if active, advance if completed). Params: session_id (string)
|
|
26
30
|
done: Complete current node and advance. Params: session_id (string), choice? (number, 1-based for decisions)
|
|
27
|
-
status: Query session state. Params: session_id (string)
|
|
31
|
+
status: Query session state with variables and preload info. Params: session_id (string)
|
|
28
32
|
content: Get all loaded content. Params: session_id (string)
|
|
29
|
-
complete: Mark session completed. Params: session_id (string)
|
|
33
|
+
complete: Mark session completed. Params: session_id (string)
|
|
34
|
+
visualize: Show live execution progress with chain nesting. Params: session_id (string)`,
|
|
30
35
|
inputSchema: {
|
|
31
36
|
type: 'object',
|
|
32
37
|
properties: {
|
|
33
|
-
cmd: { type: 'string', description: 'Command: list|start|next|done|status|content|complete' },
|
|
38
|
+
cmd: { type: 'string', description: 'Command: list|inspect|start|next|done|status|content|complete|visualize' },
|
|
34
39
|
skill: { type: 'string', description: 'Skill name (optional, auto-detected from cwd if omitted)' },
|
|
35
|
-
chain: { type: 'string', description: 'Chain name (for start)' },
|
|
40
|
+
chain: { type: 'string', description: 'Chain name (for start/inspect)' },
|
|
36
41
|
session_id: { type: 'string', description: 'Session ID (for next/done/status/content/complete)' },
|
|
37
42
|
choice: { type: 'number', description: 'Decision choice index (1-based, for done on decision nodes)' },
|
|
43
|
+
node: { type: 'string', description: 'Start from specific node ID (for start, bypasses entry)' },
|
|
44
|
+
entry_name: { type: 'string', description: 'Named entry point (for start, looks up in chain entries)' },
|
|
38
45
|
},
|
|
39
46
|
required: ['cmd'],
|
|
40
47
|
},
|
|
@@ -51,12 +58,14 @@ export async function handler(params) {
|
|
|
51
58
|
try {
|
|
52
59
|
switch (p.cmd) {
|
|
53
60
|
case 'list': return cmdList(p);
|
|
61
|
+
case 'inspect': return cmdInspect(p);
|
|
54
62
|
case 'start': return cmdStart(p);
|
|
55
63
|
case 'next': return cmdNext(p);
|
|
56
64
|
case 'done': return cmdDone(p);
|
|
57
65
|
case 'status': return cmdStatus(p);
|
|
58
66
|
case 'content': return cmdContent(p);
|
|
59
67
|
case 'complete': return cmdComplete(p);
|
|
68
|
+
case 'visualize': return cmdVisualize(p);
|
|
60
69
|
default:
|
|
61
70
|
return { success: false, error: `Unknown command: ${p.cmd}` };
|
|
62
71
|
}
|
|
@@ -85,6 +94,8 @@ function cmdList(p) {
|
|
|
85
94
|
name: chain.name,
|
|
86
95
|
description: chain.description,
|
|
87
96
|
node_count: Object.keys(chain.nodes).length,
|
|
97
|
+
...(chain.triggers ? { triggers: chain.triggers } : {}),
|
|
98
|
+
...(chain.entries ? { entries: chain.entries } : {}),
|
|
88
99
|
});
|
|
89
100
|
}
|
|
90
101
|
catch { /* skip invalid chain files */ }
|
|
@@ -92,6 +103,57 @@ function cmdList(p) {
|
|
|
92
103
|
}
|
|
93
104
|
return { success: true, result: { chains: results, total: results.length } };
|
|
94
105
|
}
|
|
106
|
+
// ─── inspect ────────────────────────────────────────────────
|
|
107
|
+
function cmdInspect(p) {
|
|
108
|
+
if (!p.chain)
|
|
109
|
+
return { success: false, error: 'chain is required for inspect' };
|
|
110
|
+
const resolved = resolveSkill(p.skill);
|
|
111
|
+
if (!resolved) {
|
|
112
|
+
return { success: false, error: p.skill
|
|
113
|
+
? `Skill not found: ${p.skill}`
|
|
114
|
+
: 'Cannot auto-detect skill from cwd. Pass skill explicitly.' };
|
|
115
|
+
}
|
|
116
|
+
const { skillName, skillPath } = resolved;
|
|
117
|
+
let chainPath = join(skillPath, 'chains', `${p.chain}.json`);
|
|
118
|
+
if (!existsSync(chainPath)) {
|
|
119
|
+
const crossPath = findChainAcrossSkills(p.chain);
|
|
120
|
+
if (!crossPath) {
|
|
121
|
+
return { success: false, error: `Chain not found: ${p.chain} in skill ${skillName}` };
|
|
122
|
+
}
|
|
123
|
+
chainPath = crossPath;
|
|
124
|
+
}
|
|
125
|
+
const chain = loadChainJson(chainPath);
|
|
126
|
+
const nodeList = Object.entries(chain.nodes).map(([id, node]) => {
|
|
127
|
+
const edges = [];
|
|
128
|
+
if (node.type === 'step') {
|
|
129
|
+
if (node.next)
|
|
130
|
+
edges.push(node.next);
|
|
131
|
+
}
|
|
132
|
+
else if (node.type === 'decision') {
|
|
133
|
+
for (const c of node.choices)
|
|
134
|
+
edges.push(c.next);
|
|
135
|
+
}
|
|
136
|
+
else if (node.type === 'delegate') {
|
|
137
|
+
if (node.next)
|
|
138
|
+
edges.push(node.next);
|
|
139
|
+
edges.push(`[delegate:${node.chain}]`);
|
|
140
|
+
}
|
|
141
|
+
return { id, type: node.type, name: node.name, next: edges };
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
result: {
|
|
146
|
+
chain_id: chain.chain_id,
|
|
147
|
+
name: chain.name,
|
|
148
|
+
description: chain.description,
|
|
149
|
+
...(chain.triggers ? { triggers: chain.triggers } : {}),
|
|
150
|
+
entries: chain.entries || (chain.entry ? [{ name: 'default', node: chain.entry, description: 'Default entry' }] : []),
|
|
151
|
+
node_count: nodeList.length,
|
|
152
|
+
nodes: nodeList,
|
|
153
|
+
topology: renderChainTopology(chain),
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
95
157
|
// ─── start ───────────────────────────────────────────────────
|
|
96
158
|
function cmdStart(p) {
|
|
97
159
|
if (!p.chain)
|
|
@@ -104,18 +166,40 @@ function cmdStart(p) {
|
|
|
104
166
|
: 'Cannot auto-detect skill from cwd. Pass skill explicitly or cd to a skill directory (must contain chains/ dir).' };
|
|
105
167
|
}
|
|
106
168
|
const { skillName, skillPath } = resolved;
|
|
107
|
-
|
|
169
|
+
let chainPath = join(skillPath, 'chains', `${p.chain}.json`);
|
|
108
170
|
if (!existsSync(chainPath)) {
|
|
109
|
-
|
|
171
|
+
const crossPath = findChainAcrossSkills(p.chain);
|
|
172
|
+
if (!crossPath) {
|
|
173
|
+
return { success: false, error: `Chain not found: ${p.chain} in skill ${skillName}` };
|
|
174
|
+
}
|
|
175
|
+
chainPath = crossPath;
|
|
110
176
|
}
|
|
111
177
|
const chain = loadChainJson(chainPath);
|
|
112
|
-
|
|
113
|
-
|
|
178
|
+
// Resolve entry point: explicit node > entry_name > entries[0] > entry
|
|
179
|
+
const startNode = p.node
|
|
180
|
+
|| (p.entry_name && chain.entries?.find(e => e.name === p.entry_name)?.node)
|
|
181
|
+
|| (chain.entries?.[0]?.node)
|
|
182
|
+
|| chain.entry;
|
|
183
|
+
if (!startNode) {
|
|
184
|
+
return { success: false, error: 'No entry point found. Provide node, entry_name, or define entry/entries in chain.' };
|
|
185
|
+
}
|
|
186
|
+
if (!chain.nodes[startNode]) {
|
|
187
|
+
return { success: false, error: `Node "${startNode}" not found in chain "${chain.chain_id}"` };
|
|
114
188
|
}
|
|
115
189
|
// Generate session ID
|
|
116
190
|
const now = new Date();
|
|
117
191
|
const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '');
|
|
118
192
|
const sessionId = `CL-${skillName}-${timeStr}`;
|
|
193
|
+
// Initialize variables from chain defaults
|
|
194
|
+
const variables = {};
|
|
195
|
+
if (chain.variables) {
|
|
196
|
+
for (const [key, def] of Object.entries(chain.variables)) {
|
|
197
|
+
if (def.default !== undefined)
|
|
198
|
+
variables[key] = def.default;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Resolve preloads (normalize object format { key: source } to array format)
|
|
202
|
+
const preloaded = resolvePreloads(normalizePreload(chain.preload), skillPath);
|
|
119
203
|
// Create session
|
|
120
204
|
const session = {
|
|
121
205
|
session_id: sessionId,
|
|
@@ -123,20 +207,22 @@ function cmdStart(p) {
|
|
|
123
207
|
skill_path: skillPath,
|
|
124
208
|
status: 'active',
|
|
125
209
|
current_chain: chain.chain_id,
|
|
126
|
-
current_node:
|
|
210
|
+
current_node: startNode,
|
|
127
211
|
node_status: 'active',
|
|
128
212
|
chain_stack: [],
|
|
129
213
|
history: [],
|
|
130
214
|
loaded_content: [],
|
|
215
|
+
variables,
|
|
216
|
+
preloaded,
|
|
131
217
|
started_at: now.toISOString(),
|
|
132
218
|
updated_at: now.toISOString(),
|
|
133
219
|
};
|
|
134
220
|
// Load entry node content
|
|
135
|
-
const entryNode = chain.nodes[
|
|
221
|
+
const entryNode = chain.nodes[startNode];
|
|
136
222
|
const nodeContent = loadNodeContent(entryNode, skillPath);
|
|
137
223
|
// Record in history
|
|
138
224
|
session.history.push({
|
|
139
|
-
node_id:
|
|
225
|
+
node_id: startNode,
|
|
140
226
|
chain_id: chain.chain_id,
|
|
141
227
|
node_type: entryNode.type,
|
|
142
228
|
node_status: 'active',
|
|
@@ -145,7 +231,7 @@ function cmdStart(p) {
|
|
|
145
231
|
// Add to loaded content
|
|
146
232
|
if (nodeContent) {
|
|
147
233
|
session.loaded_content.push({
|
|
148
|
-
node_id:
|
|
234
|
+
node_id: startNode,
|
|
149
235
|
chain_id: chain.chain_id,
|
|
150
236
|
content: nodeContent,
|
|
151
237
|
loaded_at: now.toISOString(),
|
|
@@ -157,8 +243,10 @@ function cmdStart(p) {
|
|
|
157
243
|
result: {
|
|
158
244
|
session_id: sessionId,
|
|
159
245
|
current_chain: chain.chain_id,
|
|
160
|
-
current_node:
|
|
246
|
+
current_node: startNode,
|
|
161
247
|
node_status: 'active',
|
|
248
|
+
preloaded_keys: preloaded.map(p => p.key),
|
|
249
|
+
variables,
|
|
162
250
|
...formatNodeOutput(entryNode, nodeContent),
|
|
163
251
|
},
|
|
164
252
|
};
|
|
@@ -221,6 +309,13 @@ function cmdStatus(p) {
|
|
|
221
309
|
const session = loadSession(p.session_id);
|
|
222
310
|
if (!session)
|
|
223
311
|
return { success: false, error: `Session not found: ${p.session_id}` };
|
|
312
|
+
// Count total nodes in current chain
|
|
313
|
+
let currentChainTotalNodes = 0;
|
|
314
|
+
try {
|
|
315
|
+
const chain = loadChainFromSession(session);
|
|
316
|
+
currentChainTotalNodes = Object.keys(chain.nodes).length;
|
|
317
|
+
}
|
|
318
|
+
catch { /* skip */ }
|
|
224
319
|
return {
|
|
225
320
|
success: true,
|
|
226
321
|
result: {
|
|
@@ -230,9 +325,13 @@ function cmdStatus(p) {
|
|
|
230
325
|
current_chain: session.current_chain,
|
|
231
326
|
current_node: session.current_node,
|
|
232
327
|
node_status: session.node_status,
|
|
328
|
+
nesting_depth: session.chain_stack.length,
|
|
329
|
+
current_chain_total_nodes: currentChainTotalNodes,
|
|
233
330
|
chain_stack_depth: session.chain_stack.length,
|
|
234
331
|
history_length: session.history.length,
|
|
235
332
|
loaded_count: session.loaded_content.length,
|
|
333
|
+
preloaded_keys: (session.preloaded || []).map(p => p.key),
|
|
334
|
+
variables: session.variables || {},
|
|
236
335
|
started_at: session.started_at,
|
|
237
336
|
updated_at: session.updated_at,
|
|
238
337
|
},
|
|
@@ -299,25 +398,56 @@ function advanceToNext(session, choice) {
|
|
|
299
398
|
else if (currentNode.type === 'delegate') {
|
|
300
399
|
// Push current chain onto stack and switch to sub-chain
|
|
301
400
|
const subChainId = currentNode.chain;
|
|
302
|
-
|
|
401
|
+
let subChainPath = join(session.skill_path, 'chains', `${subChainId}.json`);
|
|
303
402
|
if (!existsSync(subChainPath)) {
|
|
304
|
-
|
|
403
|
+
const crossPath = findChainAcrossSkills(subChainId);
|
|
404
|
+
if (!crossPath) {
|
|
405
|
+
return { success: false, error: `Delegate chain not found: ${subChainId}` };
|
|
406
|
+
}
|
|
407
|
+
subChainPath = crossPath;
|
|
305
408
|
}
|
|
306
409
|
const subChain = loadChainJson(subChainPath);
|
|
307
|
-
//
|
|
410
|
+
// Snapshot parent variables and push frame
|
|
411
|
+
const variablesSnapshot = { ...(session.variables || {}) };
|
|
308
412
|
session.chain_stack.push({
|
|
309
413
|
chain_id: session.current_chain,
|
|
310
414
|
return_node: currentNode.next || null,
|
|
415
|
+
variables_snapshot: variablesSnapshot,
|
|
311
416
|
});
|
|
312
|
-
//
|
|
417
|
+
// Pass variables to child scope
|
|
418
|
+
const childVars = {};
|
|
419
|
+
// Initialize from child chain defaults
|
|
420
|
+
if (subChain.variables) {
|
|
421
|
+
for (const [key, def] of Object.entries(subChain.variables)) {
|
|
422
|
+
if (def.default !== undefined)
|
|
423
|
+
childVars[key] = def.default;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Override with passed parent variables
|
|
427
|
+
const keysToPass = currentNode.pass_variables || Object.keys(session.variables || {});
|
|
428
|
+
for (const key of keysToPass) {
|
|
429
|
+
if (key in (session.variables || {})) {
|
|
430
|
+
childVars[key] = session.variables[key];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
session.variables = childVars;
|
|
434
|
+
// Resolve entry: entry_name from delegate node > chain entries > chain entry
|
|
435
|
+
let subEntry;
|
|
436
|
+
if (currentNode.entry_name && subChain.entries) {
|
|
437
|
+
const found = subChain.entries.find(e => e.name === currentNode.entry_name);
|
|
438
|
+
subEntry = found?.node || resolveChainEntry(subChain);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
subEntry = resolveChainEntry(subChain);
|
|
442
|
+
}
|
|
313
443
|
session.current_chain = subChain.chain_id;
|
|
314
|
-
session.current_node =
|
|
444
|
+
session.current_node = subEntry;
|
|
315
445
|
session.node_status = 'active';
|
|
316
446
|
session.updated_at = new Date().toISOString();
|
|
317
|
-
const entryNode = subChain.nodes[
|
|
447
|
+
const entryNode = subChain.nodes[subEntry];
|
|
318
448
|
const content = loadNodeContent(entryNode, session.skill_path);
|
|
319
449
|
session.history.push({
|
|
320
|
-
node_id:
|
|
450
|
+
node_id: subEntry,
|
|
321
451
|
chain_id: subChain.chain_id,
|
|
322
452
|
node_type: entryNode.type,
|
|
323
453
|
node_status: 'active',
|
|
@@ -325,7 +455,7 @@ function advanceToNext(session, choice) {
|
|
|
325
455
|
});
|
|
326
456
|
if (content) {
|
|
327
457
|
session.loaded_content.push({
|
|
328
|
-
node_id:
|
|
458
|
+
node_id: subEntry,
|
|
329
459
|
chain_id: subChain.chain_id,
|
|
330
460
|
content,
|
|
331
461
|
loaded_at: new Date().toISOString(),
|
|
@@ -340,6 +470,7 @@ function advanceToNext(session, choice) {
|
|
|
340
470
|
current_node: session.current_node,
|
|
341
471
|
node_status: 'active',
|
|
342
472
|
delegate_depth: session.chain_stack.length,
|
|
473
|
+
variables: session.variables,
|
|
343
474
|
...formatNodeOutput(entryNode, content),
|
|
344
475
|
},
|
|
345
476
|
};
|
|
@@ -347,19 +478,24 @@ function advanceToNext(session, choice) {
|
|
|
347
478
|
// Handle cross-chain routing (→chain-name)
|
|
348
479
|
if (nextNodeId && nextNodeId.startsWith('→')) {
|
|
349
480
|
const targetChainId = nextNodeId.slice(1);
|
|
350
|
-
|
|
481
|
+
let targetChainPath = join(session.skill_path, 'chains', `${targetChainId}.json`);
|
|
351
482
|
if (!existsSync(targetChainPath)) {
|
|
352
|
-
|
|
483
|
+
const crossPath = findChainAcrossSkills(targetChainId);
|
|
484
|
+
if (!crossPath) {
|
|
485
|
+
return { success: false, error: `Cross-chain target not found: ${targetChainId}` };
|
|
486
|
+
}
|
|
487
|
+
targetChainPath = crossPath;
|
|
353
488
|
}
|
|
354
489
|
const targetChain = loadChainJson(targetChainPath);
|
|
490
|
+
const targetEntry = resolveChainEntry(targetChain);
|
|
355
491
|
session.current_chain = targetChain.chain_id;
|
|
356
|
-
session.current_node =
|
|
492
|
+
session.current_node = targetEntry;
|
|
357
493
|
session.node_status = 'active';
|
|
358
494
|
session.updated_at = new Date().toISOString();
|
|
359
|
-
const entryNode = targetChain.nodes[
|
|
495
|
+
const entryNode = targetChain.nodes[targetEntry];
|
|
360
496
|
const content = loadNodeContent(entryNode, session.skill_path);
|
|
361
497
|
session.history.push({
|
|
362
|
-
node_id:
|
|
498
|
+
node_id: targetEntry,
|
|
363
499
|
chain_id: targetChain.chain_id,
|
|
364
500
|
node_type: entryNode.type,
|
|
365
501
|
node_status: 'active',
|
|
@@ -367,7 +503,7 @@ function advanceToNext(session, choice) {
|
|
|
367
503
|
});
|
|
368
504
|
if (content) {
|
|
369
505
|
session.loaded_content.push({
|
|
370
|
-
node_id:
|
|
506
|
+
node_id: targetEntry,
|
|
371
507
|
chain_id: targetChain.chain_id,
|
|
372
508
|
content,
|
|
373
509
|
loaded_at: new Date().toISOString(),
|
|
@@ -392,8 +528,26 @@ function advanceToNext(session, choice) {
|
|
|
392
528
|
const frame = session.chain_stack.pop();
|
|
393
529
|
if (frame.return_node) {
|
|
394
530
|
// Return to parent chain's return node
|
|
395
|
-
|
|
531
|
+
let parentChainPath = join(session.skill_path, 'chains', `${frame.chain_id}.json`);
|
|
532
|
+
if (!existsSync(parentChainPath)) {
|
|
533
|
+
const crossPath = findChainAcrossSkills(frame.chain_id);
|
|
534
|
+
if (crossPath)
|
|
535
|
+
parentChainPath = crossPath;
|
|
536
|
+
}
|
|
396
537
|
const parentChain = loadChainJson(parentChainPath);
|
|
538
|
+
// Receive variables from child back to parent
|
|
539
|
+
const childVars = { ...(session.variables || {}) };
|
|
540
|
+
const parentVars = { ...(frame.variables_snapshot || {}) };
|
|
541
|
+
// Find the delegate node that spawned this child to get receive_variables
|
|
542
|
+
const delegateNode = findDelegateNodeForChain(parentChain, session.current_chain);
|
|
543
|
+
const receiveKeys = delegateNode?.receive_variables;
|
|
544
|
+
if (receiveKeys) {
|
|
545
|
+
for (const key of receiveKeys) {
|
|
546
|
+
if (key in childVars)
|
|
547
|
+
parentVars[key] = childVars[key];
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
session.variables = parentVars;
|
|
397
551
|
session.current_chain = frame.chain_id;
|
|
398
552
|
session.current_node = frame.return_node;
|
|
399
553
|
session.node_status = 'active';
|
|
@@ -424,6 +578,7 @@ function advanceToNext(session, choice) {
|
|
|
424
578
|
current_node: session.current_node,
|
|
425
579
|
node_status: 'active',
|
|
426
580
|
returned_from_delegate: true,
|
|
581
|
+
variables: session.variables,
|
|
427
582
|
...formatNodeOutput(returnNode, content),
|
|
428
583
|
},
|
|
429
584
|
};
|
|
@@ -480,25 +635,35 @@ function advanceToNext(session, choice) {
|
|
|
480
635
|
},
|
|
481
636
|
};
|
|
482
637
|
}
|
|
638
|
+
// ─── Entry Resolver ─────────────────────────────────────────
|
|
639
|
+
function resolveChainEntry(chain) {
|
|
640
|
+
return chain.entries?.[0]?.node || chain.entry || Object.keys(chain.nodes)[0];
|
|
641
|
+
}
|
|
483
642
|
// ─── Node Helpers ────────────────────────────────────────────
|
|
484
643
|
function loadNodeContent(node, skillPath) {
|
|
485
644
|
if (node.type === 'step') {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
645
|
+
const parts = [];
|
|
646
|
+
// 1. content_files: multiple file references, resolved in order
|
|
647
|
+
if (node.content_files?.length) {
|
|
648
|
+
for (const ref of node.content_files) {
|
|
649
|
+
const content = resolveFileRef(ref, skillPath);
|
|
650
|
+
if (content !== null)
|
|
651
|
+
parts.push(content);
|
|
492
652
|
}
|
|
493
|
-
return `[Content not found: ${fullPath}]`;
|
|
494
653
|
}
|
|
654
|
+
// 2. content_ref: single file reference (backward compat)
|
|
655
|
+
if (node.content_ref) {
|
|
656
|
+
const content = resolveFileRef(node.content_ref, skillPath);
|
|
657
|
+
if (content !== null)
|
|
658
|
+
parts.push(content);
|
|
659
|
+
}
|
|
660
|
+
// 3. content_inline: direct text with embedded @ref expansion
|
|
495
661
|
if (node.content_inline) {
|
|
496
|
-
|
|
662
|
+
parts.push(resolveInlineRefs(node.content_inline, skillPath));
|
|
497
663
|
}
|
|
498
|
-
return null;
|
|
664
|
+
return parts.length > 0 ? parts.join('\n\n') : null;
|
|
499
665
|
}
|
|
500
666
|
if (node.type === 'decision') {
|
|
501
|
-
// Return prompt as content
|
|
502
667
|
return node.prompt;
|
|
503
668
|
}
|
|
504
669
|
if (node.type === 'delegate') {
|
|
@@ -506,6 +671,93 @@ function loadNodeContent(node, skillPath) {
|
|
|
506
671
|
}
|
|
507
672
|
return null;
|
|
508
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Resolve a single @file reference to its content.
|
|
676
|
+
* Supports: @path/file.md (skill-relative), @~/path/file.md (home-relative)
|
|
677
|
+
*/
|
|
678
|
+
function resolveFileRef(ref, skillPath) {
|
|
679
|
+
try {
|
|
680
|
+
const raw = ref.replace(/^@/, '');
|
|
681
|
+
if (raw.startsWith('~/')) {
|
|
682
|
+
const fullPath = resolve(homedir(), raw.slice(2));
|
|
683
|
+
if (existsSync(fullPath))
|
|
684
|
+
return readFileSync(fullPath, 'utf-8');
|
|
685
|
+
}
|
|
686
|
+
else if (raw.startsWith('skills/')) {
|
|
687
|
+
// @skills/workflow-plan/phases/01.md → .claude/skills/workflow-plan/phases/01.md
|
|
688
|
+
const relPath = raw.slice(7);
|
|
689
|
+
const projectPath = resolve(process.cwd(), '.claude', 'skills', relPath);
|
|
690
|
+
if (existsSync(projectPath))
|
|
691
|
+
return readFileSync(projectPath, 'utf-8');
|
|
692
|
+
const userPath = resolve(homedir(), '.claude', 'skills', relPath);
|
|
693
|
+
if (existsSync(userPath))
|
|
694
|
+
return readFileSync(userPath, 'utf-8');
|
|
695
|
+
}
|
|
696
|
+
else if (raw.startsWith('commands/')) {
|
|
697
|
+
// @commands/workflow/analyze-with-file.md → .claude/commands/workflow/analyze-with-file.md
|
|
698
|
+
const relPath = raw.slice(9);
|
|
699
|
+
const projectPath = resolve(process.cwd(), '.claude', 'commands', relPath);
|
|
700
|
+
if (existsSync(projectPath))
|
|
701
|
+
return readFileSync(projectPath, 'utf-8');
|
|
702
|
+
const userPath = resolve(homedir(), '.claude', 'commands', relPath);
|
|
703
|
+
if (existsSync(userPath))
|
|
704
|
+
return readFileSync(userPath, 'utf-8');
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
const fullPath = resolve(skillPath, raw);
|
|
708
|
+
if (existsSync(fullPath))
|
|
709
|
+
return readFileSync(fullPath, 'utf-8');
|
|
710
|
+
}
|
|
711
|
+
return `[Content not found: ${ref}]`;
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
return `[Error loading: ${ref}]`;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Expand embedded @file references in inline content.
|
|
719
|
+
* Pattern: @path/to/file.md (must end with file extension)
|
|
720
|
+
* Supports: @skill-relative/path.md, @~/home-relative/path.md
|
|
721
|
+
* Skips @-references inside code blocks.
|
|
722
|
+
*/
|
|
723
|
+
function resolveInlineRefs(text, skillPath) {
|
|
724
|
+
// Match @skills/path.ext, @~/path/file.ext, or @path/file.ext — must have at least one / and end with .ext
|
|
725
|
+
return text.replace(/@((?:skills|commands|~)?\/[\w./-]+\.\w+)/g, (match, refPath) => {
|
|
726
|
+
try {
|
|
727
|
+
let fullPath;
|
|
728
|
+
if (refPath.startsWith('skills/')) {
|
|
729
|
+
const relPath = refPath.slice(7);
|
|
730
|
+
const projectPath = resolve(process.cwd(), '.claude', 'skills', relPath);
|
|
731
|
+
if (existsSync(projectPath))
|
|
732
|
+
return readFileSync(projectPath, 'utf-8');
|
|
733
|
+
const userPath = resolve(homedir(), '.claude', 'skills', relPath);
|
|
734
|
+
if (existsSync(userPath))
|
|
735
|
+
return readFileSync(userPath, 'utf-8');
|
|
736
|
+
return match;
|
|
737
|
+
}
|
|
738
|
+
else if (refPath.startsWith('commands/')) {
|
|
739
|
+
const relPath = refPath.slice(9);
|
|
740
|
+
const projectPath = resolve(process.cwd(), '.claude', 'commands', relPath);
|
|
741
|
+
if (existsSync(projectPath))
|
|
742
|
+
return readFileSync(projectPath, 'utf-8');
|
|
743
|
+
const userPath = resolve(homedir(), '.claude', 'commands', relPath);
|
|
744
|
+
if (existsSync(userPath))
|
|
745
|
+
return readFileSync(userPath, 'utf-8');
|
|
746
|
+
return match;
|
|
747
|
+
}
|
|
748
|
+
else if (refPath.startsWith('~/')) {
|
|
749
|
+
fullPath = resolve(homedir(), refPath.slice(2));
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
fullPath = resolve(skillPath, refPath.startsWith('/') ? refPath.slice(1) : refPath);
|
|
753
|
+
}
|
|
754
|
+
if (existsSync(fullPath))
|
|
755
|
+
return readFileSync(fullPath, 'utf-8');
|
|
756
|
+
}
|
|
757
|
+
catch { /* fall through */ }
|
|
758
|
+
return match; // leave unresolved references as-is
|
|
759
|
+
});
|
|
760
|
+
}
|
|
509
761
|
function formatNodeOutput(node, content) {
|
|
510
762
|
const output = {
|
|
511
763
|
type: node.type,
|
|
@@ -555,10 +807,12 @@ function loadChainJson(filePath) {
|
|
|
555
807
|
}
|
|
556
808
|
function loadChainFromSession(session) {
|
|
557
809
|
const chainPath = join(session.skill_path, 'chains', `${session.current_chain}.json`);
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
810
|
+
if (existsSync(chainPath))
|
|
811
|
+
return loadChainJson(chainPath);
|
|
812
|
+
const crossPath = findChainAcrossSkills(session.current_chain);
|
|
813
|
+
if (crossPath)
|
|
814
|
+
return loadChainJson(crossPath);
|
|
815
|
+
throw new Error(`Chain file not found: ${chainPath}`);
|
|
562
816
|
}
|
|
563
817
|
function getSessionDir(sessionId) {
|
|
564
818
|
return resolve(process.cwd(), SESSIONS_BASE, sessionId);
|
|
@@ -609,7 +863,9 @@ function discoverSkillDirs(filterSkill) {
|
|
|
609
863
|
const results = [];
|
|
610
864
|
const searchLocations = [
|
|
611
865
|
join(process.cwd(), '.claude', 'skills'),
|
|
866
|
+
join(process.cwd(), '.claude', 'workflow-skills'),
|
|
612
867
|
join(homedir(), '.claude', 'skills'),
|
|
868
|
+
join(homedir(), '.claude', 'workflow-skills'),
|
|
613
869
|
];
|
|
614
870
|
for (const base of searchLocations) {
|
|
615
871
|
if (!existsSync(base))
|
|
@@ -628,10 +884,146 @@ function discoverSkillDirs(filterSkill) {
|
|
|
628
884
|
}
|
|
629
885
|
return results;
|
|
630
886
|
}
|
|
887
|
+
// ─── visualize ─────────────────────────────────────────────
|
|
888
|
+
function cmdVisualize(p) {
|
|
889
|
+
if (!p.session_id)
|
|
890
|
+
return { success: false, error: 'session_id is required for visualize' };
|
|
891
|
+
const session = loadSession(p.session_id);
|
|
892
|
+
if (!session)
|
|
893
|
+
return { success: false, error: `Session not found: ${p.session_id}` };
|
|
894
|
+
// Load all chains referenced in session (current + stack)
|
|
895
|
+
const chains = new Map();
|
|
896
|
+
const chainIds = new Set([session.current_chain]);
|
|
897
|
+
for (const frame of session.chain_stack)
|
|
898
|
+
chainIds.add(frame.chain_id);
|
|
899
|
+
for (const chainId of chainIds) {
|
|
900
|
+
try {
|
|
901
|
+
const chainPath = join(session.skill_path, 'chains', `${chainId}.json`);
|
|
902
|
+
if (existsSync(chainPath)) {
|
|
903
|
+
chains.set(chainId, loadChainJson(chainPath));
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
const crossPath = findChainAcrossSkills(chainId);
|
|
907
|
+
if (crossPath)
|
|
908
|
+
chains.set(chainId, loadChainJson(crossPath));
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
catch { /* skip unloadable */ }
|
|
912
|
+
}
|
|
913
|
+
const visualization = renderChainProgress(session, chains);
|
|
914
|
+
const completed = session.history.filter(h => h.node_status === 'completed').length;
|
|
915
|
+
let total = 0;
|
|
916
|
+
for (const chain of chains.values())
|
|
917
|
+
total += Object.keys(chain.nodes).length;
|
|
918
|
+
return {
|
|
919
|
+
success: true,
|
|
920
|
+
result: {
|
|
921
|
+
visualization,
|
|
922
|
+
nesting_depth: session.chain_stack.length,
|
|
923
|
+
progress: { completed, total },
|
|
924
|
+
},
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
// ─── Preload Resolver ─────────────────────────────────────
|
|
928
|
+
/**
|
|
929
|
+
* Normalize preload from either format:
|
|
930
|
+
* - Array: [{ key, source, required? }] (typed format)
|
|
931
|
+
* - Object: { key: source } (shorthand in chain JSON)
|
|
932
|
+
*/
|
|
933
|
+
function normalizePreload(preload) {
|
|
934
|
+
if (!preload)
|
|
935
|
+
return [];
|
|
936
|
+
if (Array.isArray(preload))
|
|
937
|
+
return preload;
|
|
938
|
+
if (typeof preload === 'object') {
|
|
939
|
+
return Object.entries(preload).map(([key, source]) => ({
|
|
940
|
+
key,
|
|
941
|
+
source: typeof source === 'string' ? source : String(source),
|
|
942
|
+
}));
|
|
943
|
+
}
|
|
944
|
+
return [];
|
|
945
|
+
}
|
|
946
|
+
function resolvePreloads(entries, skillPath) {
|
|
947
|
+
const results = [];
|
|
948
|
+
for (const entry of entries) {
|
|
949
|
+
const content = resolvePreloadSource(entry.source, skillPath);
|
|
950
|
+
if (content !== null) {
|
|
951
|
+
results.push({
|
|
952
|
+
key: entry.key,
|
|
953
|
+
content,
|
|
954
|
+
source: entry.source,
|
|
955
|
+
loaded_at: new Date().toISOString(),
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
else if (entry.required !== false) {
|
|
959
|
+
results.push({
|
|
960
|
+
key: entry.key,
|
|
961
|
+
content: `[WARN: Failed to load required preload: ${entry.source}]`,
|
|
962
|
+
source: entry.source,
|
|
963
|
+
loaded_at: new Date().toISOString(),
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return results;
|
|
968
|
+
}
|
|
969
|
+
function resolvePreloadSource(source, skillPath) {
|
|
970
|
+
try {
|
|
971
|
+
if (source.startsWith('$env:')) {
|
|
972
|
+
const varName = source.slice(5);
|
|
973
|
+
return process.env[varName] || null;
|
|
974
|
+
}
|
|
975
|
+
if (source.startsWith('memory:')) {
|
|
976
|
+
const memFile = source.slice(7);
|
|
977
|
+
const memPath = resolve(process.cwd(), 'memory', memFile);
|
|
978
|
+
if (existsSync(memPath))
|
|
979
|
+
return readFileSync(memPath, 'utf-8');
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
if (source.startsWith('@~/')) {
|
|
983
|
+
const relPath = source.slice(3);
|
|
984
|
+
const fullPath = resolve(homedir(), relPath);
|
|
985
|
+
if (existsSync(fullPath))
|
|
986
|
+
return readFileSync(fullPath, 'utf-8');
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
if (source.startsWith('@skills/')) {
|
|
990
|
+
const relPath = source.slice(8);
|
|
991
|
+
const projectPath = resolve(process.cwd(), '.claude', 'skills', relPath);
|
|
992
|
+
if (existsSync(projectPath))
|
|
993
|
+
return readFileSync(projectPath, 'utf-8');
|
|
994
|
+
const userPath = resolve(homedir(), '.claude', 'skills', relPath);
|
|
995
|
+
if (existsSync(userPath))
|
|
996
|
+
return readFileSync(userPath, 'utf-8');
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
if (source.startsWith('@')) {
|
|
1000
|
+
const relPath = source.slice(1);
|
|
1001
|
+
const fullPath = resolve(skillPath, relPath);
|
|
1002
|
+
if (existsSync(fullPath))
|
|
1003
|
+
return readFileSync(fullPath, 'utf-8');
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
// ─── Delegate Helper ─────────────────────────────────────
|
|
1013
|
+
function findDelegateNodeForChain(parentChain, childChainId) {
|
|
1014
|
+
for (const node of Object.values(parentChain.nodes)) {
|
|
1015
|
+
if (node.type === 'delegate' && node.chain === childChainId) {
|
|
1016
|
+
return node;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
631
1021
|
function findSkillPath(skillName) {
|
|
632
1022
|
const searchLocations = [
|
|
633
1023
|
join(process.cwd(), '.claude', 'skills', skillName),
|
|
1024
|
+
join(process.cwd(), '.claude', 'workflow-skills', skillName),
|
|
634
1025
|
join(homedir(), '.claude', 'skills', skillName),
|
|
1026
|
+
join(homedir(), '.claude', 'workflow-skills', skillName),
|
|
635
1027
|
];
|
|
636
1028
|
for (const path of searchLocations) {
|
|
637
1029
|
if (existsSync(path))
|
|
@@ -639,4 +1031,24 @@ function findSkillPath(skillName) {
|
|
|
639
1031
|
}
|
|
640
1032
|
return null;
|
|
641
1033
|
}
|
|
1034
|
+
function findChainAcrossSkills(chainId) {
|
|
1035
|
+
const bases = [
|
|
1036
|
+
join(process.cwd(), '.claude', 'workflow-skills'),
|
|
1037
|
+
join(process.cwd(), '.claude', 'skills'),
|
|
1038
|
+
join(homedir(), '.claude', 'workflow-skills'),
|
|
1039
|
+
join(homedir(), '.claude', 'skills'),
|
|
1040
|
+
];
|
|
1041
|
+
for (const base of bases) {
|
|
1042
|
+
if (!existsSync(base))
|
|
1043
|
+
continue;
|
|
1044
|
+
for (const entry of readdirSync(base, { withFileTypes: true })) {
|
|
1045
|
+
if (!entry.isDirectory())
|
|
1046
|
+
continue;
|
|
1047
|
+
const path = join(base, entry.name, 'chains', `${chainId}.json`);
|
|
1048
|
+
if (existsSync(path))
|
|
1049
|
+
return path;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
642
1054
|
//# sourceMappingURL=chain-loader.js.map
|