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.
Files changed (183) hide show
  1. package/.claude/commands/workflow-skill.md +130 -0
  2. package/.claude/skills/ccw-chain/SKILL.md +44 -71
  3. package/.claude/skills/ccw-chain/chains/ccw-cycle.json +13 -3
  4. package/.claude/skills/ccw-chain/chains/ccw-exploration.json +33 -22
  5. package/.claude/skills/ccw-chain/chains/ccw-issue.json +23 -12
  6. package/.claude/skills/ccw-chain/chains/ccw-lightweight.json +23 -9
  7. package/.claude/skills/ccw-chain/chains/ccw-main.json +15 -2
  8. package/.claude/skills/ccw-chain/chains/ccw-standard.json +28 -16
  9. package/.claude/skills/ccw-chain/chains/ccw-team.json +7 -2
  10. package/.claude/skills/ccw-chain/chains/ccw-with-file.json +25 -9
  11. package/.claude/skills/chain-loader/phases/01-analyze-skill.md +53 -53
  12. package/.claude/skills/chain-loader/specs/chain-schema.md +30 -3
  13. package/.claude/skills/chain-loader/templates/chain-json.md +63 -63
  14. package/.claude/skills/workflow-plan/SKILL.md +1 -0
  15. package/.claude/skills/workflow-plan/phases/01-session-discovery.md +19 -2
  16. package/.claude/skills/workflow-plan/phases/02-context-gathering.md +2 -2
  17. package/.claude/skills/workflow-plan/phases/04-task-generation.md +9 -1
  18. package/.codex/skills/analyze-with-file/SKILL.md +383 -134
  19. package/.codex/skills/brainstorm/SKILL.md +3 -3
  20. package/.codex/skills/brainstorm-with-file/SKILL.md +208 -88
  21. package/.codex/skills/clean/SKILL.md +1 -1
  22. package/.codex/skills/csv-wave-pipeline/SKILL.md +2 -2
  23. package/.codex/skills/investigate/orchestrator.md +24 -0
  24. package/.codex/skills/issue-discover/SKILL.md +374 -361
  25. package/.codex/skills/issue-discover/phases/01-issue-new.md +1 -1
  26. package/.codex/skills/issue-discover/phases/02-discover.md +2 -2
  27. package/.codex/skills/issue-discover/phases/03-discover-by-prompt.md +1 -1
  28. package/.codex/skills/issue-discover/phases/04-quick-execute.md +2 -2
  29. package/.codex/skills/parallel-dev-cycle/SKILL.md +44 -37
  30. package/.codex/skills/project-documentation-workflow/SKILL.md +1 -1
  31. package/.codex/skills/review-cycle/SKILL.md +31 -12
  32. package/.codex/skills/roadmap-with-file/SKILL.md +141 -133
  33. package/.codex/skills/security-audit/orchestrator.md +29 -0
  34. package/.codex/skills/session-sync/SKILL.md +1 -1
  35. package/.codex/skills/ship/orchestrator.md +24 -0
  36. package/.codex/skills/spec-add/SKILL.md +5 -5
  37. package/.codex/skills/spec-generator/SKILL.md +33 -2
  38. package/.codex/skills/spec-generator/phases/01-5-requirement-clarification.md +3 -3
  39. package/.codex/skills/spec-generator/phases/01-discovery.md +1 -1
  40. package/.codex/skills/spec-generator/phases/02-product-brief.md +1 -1
  41. package/.codex/skills/spec-generator/phases/03-requirements.md +1 -1
  42. package/.codex/skills/spec-generator/phases/04-architecture.md +1 -1
  43. package/.codex/skills/spec-generator/phases/05-epics-stories.md +1 -1
  44. package/.codex/skills/spec-generator/phases/06-readiness-check.md +1 -1
  45. package/.codex/skills/spec-generator/phases/07-issue-export.md +1 -1
  46. package/.codex/skills/spec-setup/SKILL.md +669 -669
  47. package/.codex/skills/team-arch-opt/specs/team-config.json +1 -1
  48. package/.codex/skills/team-brainstorm/SKILL.md +259 -259
  49. package/.codex/skills/team-coordinate/SKILL.md +359 -359
  50. package/.codex/skills/team-coordinate/roles/coordinator/commands/monitor.md +1 -1
  51. package/.codex/skills/team-designer/SKILL.md +27 -1
  52. package/.codex/skills/team-designer/phases/01-requirements-analysis.md +2 -2
  53. package/.codex/skills/team-designer/phases/02-scaffold-generation.md +1 -1
  54. package/.codex/skills/team-designer/phases/04-validation.md +1 -1
  55. package/.codex/skills/team-executor/SKILL.md +218 -218
  56. package/.codex/skills/team-frontend/SKILL.md +227 -227
  57. package/.codex/skills/team-frontend-debug/SKILL.md +278 -278
  58. package/.codex/skills/team-frontend-debug/roles/coordinator/commands/analyze.md +2 -2
  59. package/.codex/skills/team-interactive-craft/SKILL.md +220 -220
  60. package/.codex/skills/team-interactive-craft/roles/coordinator/role.md +209 -209
  61. package/.codex/skills/team-issue/SKILL.md +269 -269
  62. package/.codex/skills/team-issue/roles/coordinator/role.md +1 -1
  63. package/.codex/skills/team-lifecycle-v4/SKILL.md +305 -305
  64. package/.codex/skills/team-motion-design/SKILL.md +222 -222
  65. package/.codex/skills/team-motion-design/roles/coordinator/role.md +210 -210
  66. package/.codex/skills/team-perf-opt/SKILL.md +258 -258
  67. package/.codex/skills/team-perf-opt/specs/team-config.json +1 -1
  68. package/.codex/skills/team-planex/SKILL.md +216 -216
  69. package/.codex/skills/team-quality-assurance/SKILL.md +229 -229
  70. package/.codex/skills/team-review/SKILL.md +227 -227
  71. package/.codex/skills/team-roadmap-dev/SKILL.md +238 -238
  72. package/.codex/skills/team-roadmap-dev/roles/coordinator/commands/roadmap-discuss.md +5 -5
  73. package/.codex/skills/team-tech-debt/SKILL.md +206 -206
  74. package/.codex/skills/team-tech-debt/roles/coordinator/commands/monitor.md +1 -1
  75. package/.codex/skills/team-testing/SKILL.md +237 -237
  76. package/.codex/skills/team-ui-polish/SKILL.md +218 -218
  77. package/.codex/skills/team-ui-polish/roles/coordinator/role.md +213 -213
  78. package/.codex/skills/team-uidesign/SKILL.md +219 -219
  79. package/.codex/skills/team-uidesign/roles/coordinator/role.md +2 -2
  80. package/.codex/skills/team-ultra-analyze/SKILL.md +260 -260
  81. package/.codex/skills/team-ultra-analyze/roles/coordinator/commands/monitor.md +1 -1
  82. package/.codex/skills/team-ultra-analyze/roles/coordinator/role.md +1 -1
  83. package/.codex/skills/team-ux-improve/SKILL.md +227 -227
  84. package/.codex/skills/team-ux-improve/roles/coordinator/role.md +1 -1
  85. package/.codex/skills/team-ux-improve/specs/team-config.json +1 -1
  86. package/.codex/skills/team-visual-a11y/SKILL.md +319 -319
  87. package/.codex/skills/team-visual-a11y/roles/coordinator/role.md +213 -213
  88. package/.codex/skills/workflow-execute/SKILL.md +5 -5
  89. package/.codex/skills/workflow-lite-planex/SKILL.md +3 -3
  90. package/.codex/skills/workflow-plan/SKILL.md +3 -3
  91. package/.codex/skills/workflow-tdd-plan/SKILL.md +4 -4
  92. package/.codex/skills/workflow-test-fix-cycle/SKILL.md +403 -402
  93. package/ccw/dist/cli.d.ts.map +1 -1
  94. package/ccw/dist/cli.js +16 -0
  95. package/ccw/dist/cli.js.map +1 -1
  96. package/ccw/dist/commands/chain-loader.d.ts +2 -0
  97. package/ccw/dist/commands/chain-loader.d.ts.map +1 -0
  98. package/ccw/dist/commands/chain-loader.js +11 -0
  99. package/ccw/dist/commands/chain-loader.js.map +1 -0
  100. package/ccw/dist/commands/install.d.ts.map +1 -1
  101. package/ccw/dist/commands/install.js +52 -1
  102. package/ccw/dist/commands/install.js.map +1 -1
  103. package/ccw/dist/commands/launcher.d.ts +2 -0
  104. package/ccw/dist/commands/launcher.d.ts.map +1 -0
  105. package/ccw/dist/commands/launcher.js +434 -0
  106. package/ccw/dist/commands/launcher.js.map +1 -0
  107. package/ccw/dist/tools/chain-loader.d.ts.map +1 -1
  108. package/ccw/dist/tools/chain-loader.js +457 -45
  109. package/ccw/dist/tools/chain-loader.js.map +1 -1
  110. package/ccw/dist/tools/skill-context-loader.d.ts.map +1 -1
  111. package/ccw/dist/tools/skill-context-loader.js +12 -26
  112. package/ccw/dist/tools/skill-context-loader.js.map +1 -1
  113. package/ccw/dist/types/chain-types.d.ts +41 -1
  114. package/ccw/dist/types/chain-types.d.ts.map +1 -1
  115. package/ccw/dist/utils/chain-visualizer.d.ts +13 -0
  116. package/ccw/dist/utils/chain-visualizer.d.ts.map +1 -0
  117. package/ccw/dist/utils/chain-visualizer.js +164 -0
  118. package/ccw/dist/utils/chain-visualizer.js.map +1 -0
  119. package/package.json +1 -1
  120. package/.claude/commands/cli/cli-init.md +0 -441
  121. package/.claude/commands/cli/codex-review.md +0 -361
  122. package/.claude/commands/flow-create.md +0 -663
  123. package/.claude/skills/ccw-chain/phases/analyze-with-file.md +0 -788
  124. package/.claude/skills/ccw-chain/phases/brainstorm/SKILL.md +0 -408
  125. package/.claude/skills/ccw-chain/phases/brainstorm/phases/01-mode-routing.md +0 -207
  126. package/.claude/skills/ccw-chain/phases/brainstorm/phases/02-artifacts.md +0 -567
  127. package/.claude/skills/ccw-chain/phases/brainstorm/phases/03-role-analysis.md +0 -748
  128. package/.claude/skills/ccw-chain/phases/brainstorm/phases/04-synthesis.md +0 -827
  129. package/.claude/skills/ccw-chain/phases/brainstorm-with-file.md +0 -482
  130. package/.claude/skills/ccw-chain/phases/collaborative-plan-with-file.md +0 -639
  131. package/.claude/skills/ccw-chain/phases/debug-with-file.md +0 -656
  132. package/.claude/skills/ccw-chain/phases/integration-test-cycle.md +0 -936
  133. package/.claude/skills/ccw-chain/phases/issue-convert-to-plan.md +0 -720
  134. package/.claude/skills/ccw-chain/phases/issue-discover.md +0 -483
  135. package/.claude/skills/ccw-chain/phases/issue-execute.md +0 -629
  136. package/.claude/skills/ccw-chain/phases/issue-from-brainstorm.md +0 -382
  137. package/.claude/skills/ccw-chain/phases/issue-plan.md +0 -343
  138. package/.claude/skills/ccw-chain/phases/issue-queue.md +0 -464
  139. package/.claude/skills/ccw-chain/phases/refactor-cycle.md +0 -852
  140. package/.claude/skills/ccw-chain/phases/review-cycle/SKILL.md +0 -132
  141. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-fix.md +0 -760
  142. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-module.md +0 -764
  143. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-session.md +0 -775
  144. package/.claude/skills/ccw-chain/phases/roadmap-with-file.md +0 -544
  145. package/.claude/skills/ccw-chain/phases/spec-generator/SKILL.md +0 -338
  146. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-5-requirement-clarification.md +0 -404
  147. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-discovery.md +0 -257
  148. package/.claude/skills/ccw-chain/phases/spec-generator/phases/02-product-brief.md +0 -274
  149. package/.claude/skills/ccw-chain/phases/spec-generator/phases/03-requirements.md +0 -184
  150. package/.claude/skills/ccw-chain/phases/spec-generator/phases/04-architecture.md +0 -248
  151. package/.claude/skills/ccw-chain/phases/spec-generator/phases/05-epics-stories.md +0 -178
  152. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-5-auto-fix.md +0 -144
  153. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-readiness-check.md +0 -480
  154. package/.claude/skills/ccw-chain/phases/team-planex.md +0 -123
  155. package/.claude/skills/ccw-chain/phases/ui-design-explore-auto.md +0 -678
  156. package/.claude/skills/ccw-chain/phases/unified-execute-with-file.md +0 -870
  157. package/.claude/skills/ccw-chain/phases/workflow-execute/SKILL.md +0 -625
  158. package/.claude/skills/ccw-chain/phases/workflow-execute/phases/06-review.md +0 -215
  159. package/.claude/skills/ccw-chain/phases/workflow-lite-plan.md +0 -616
  160. package/.claude/skills/ccw-chain/phases/workflow-multi-cli-plan.md +0 -424
  161. package/.claude/skills/ccw-chain/phases/workflow-plan/SKILL.md +0 -466
  162. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/01-session-discovery.md +0 -99
  163. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/02-context-gathering.md +0 -338
  164. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/03-conflict-resolution.md +0 -422
  165. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/04-task-generation.md +0 -440
  166. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/05-plan-verify.md +0 -395
  167. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/06-replan.md +0 -594
  168. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/SKILL.md +0 -527
  169. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/01-session-discovery.md +0 -57
  170. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/02-context-gathering.md +0 -407
  171. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/03-test-coverage-analysis.md +0 -172
  172. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/04-conflict-resolution.md +0 -426
  173. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/05-tdd-task-generation.md +0 -473
  174. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/06-tdd-structure-validation.md +0 -189
  175. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/07-tdd-verify.md +0 -635
  176. package/.claude/skills/ccw-chain/phases/workflow-test-fix/SKILL.md +0 -482
  177. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/01-session-start.md +0 -60
  178. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/02-test-context-gather.md +0 -493
  179. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/03-test-concept-enhanced.md +0 -150
  180. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/04-test-task-generate.md +0 -346
  181. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/05-test-cycle-execute.md +0 -538
  182. package/.claude/skills/ccw-chain/specs/intent-patterns.md +0 -60
  183. 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, auto-detect from cwd)
24
- start: Start chain session. Params: chain (string), skill? (string, auto-detect from cwd)
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
- const chainPath = join(skillPath, 'chains', `${p.chain}.json`);
169
+ let chainPath = join(skillPath, 'chains', `${p.chain}.json`);
108
170
  if (!existsSync(chainPath)) {
109
- return { success: false, error: `Chain not found: ${p.chain} in skill ${skillName}` };
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
- if (!chain.nodes[chain.entry]) {
113
- return { success: false, error: `Entry node "${chain.entry}" not found in chain` };
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: chain.entry,
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[chain.entry];
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: chain.entry,
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: chain.entry,
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: chain.entry,
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
- const subChainPath = join(session.skill_path, 'chains', `${subChainId}.json`);
401
+ let subChainPath = join(session.skill_path, 'chains', `${subChainId}.json`);
303
402
  if (!existsSync(subChainPath)) {
304
- return { success: false, error: `Delegate chain not found: ${subChainId}` };
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
- // Push frame
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
- // Switch to sub-chain
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 = subChain.entry;
444
+ session.current_node = subEntry;
315
445
  session.node_status = 'active';
316
446
  session.updated_at = new Date().toISOString();
317
- const entryNode = subChain.nodes[subChain.entry];
447
+ const entryNode = subChain.nodes[subEntry];
318
448
  const content = loadNodeContent(entryNode, session.skill_path);
319
449
  session.history.push({
320
- node_id: subChain.entry,
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: subChain.entry,
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
- const targetChainPath = join(session.skill_path, 'chains', `${targetChainId}.json`);
481
+ let targetChainPath = join(session.skill_path, 'chains', `${targetChainId}.json`);
351
482
  if (!existsSync(targetChainPath)) {
352
- return { success: false, error: `Cross-chain target not found: ${targetChainId}` };
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 = targetChain.entry;
492
+ session.current_node = targetEntry;
357
493
  session.node_status = 'active';
358
494
  session.updated_at = new Date().toISOString();
359
- const entryNode = targetChain.nodes[targetChain.entry];
495
+ const entryNode = targetChain.nodes[targetEntry];
360
496
  const content = loadNodeContent(entryNode, session.skill_path);
361
497
  session.history.push({
362
- node_id: targetChain.entry,
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: targetChain.entry,
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
- const parentChainPath = join(session.skill_path, 'chains', `${frame.chain_id}.json`);
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
- if (node.content_ref) {
487
- // Resolve @path relative to skill directory
488
- const refPath = node.content_ref.replace(/^@/, '');
489
- const fullPath = resolve(skillPath, refPath);
490
- if (existsSync(fullPath)) {
491
- return readFileSync(fullPath, 'utf-8');
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
- return node.content_inline;
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 (!existsSync(chainPath)) {
559
- throw new Error(`Chain file not found: ${chainPath}`);
560
- }
561
- return loadChainJson(chainPath);
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