@claude-collective/cli 0.8.0 → 0.13.1
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/CHANGELOG.md +138 -0
- package/README.md +26 -9
- package/config/skills-matrix.yaml +858 -0
- package/config/stacks.yaml +254 -0
- package/dist/{chunk-TOPAIL5W.js → chunk-3U3R4NCG.js} +2 -2
- package/dist/chunk-3U3R4NCG.js.map +1 -0
- package/dist/{chunk-YKXBGCFD.js → chunk-57Y5RALO.js} +10 -10
- package/dist/chunk-57Y5RALO.js.map +1 -0
- package/dist/{chunk-ED73HCW2.js → chunk-6DCSSORF.js} +37 -88
- package/dist/chunk-6DCSSORF.js.map +1 -0
- package/dist/{chunk-6LS7XO3H.js → chunk-6Q3Y7KVB.js} +2 -2
- package/dist/chunk-6Q3Y7KVB.js.map +1 -0
- package/dist/{chunk-A3J6IAXK.js → chunk-76DWXGQE.js} +4 -2
- package/dist/chunk-76DWXGQE.js.map +1 -0
- package/dist/{chunk-Q6DR5QUH.js → chunk-7Q44DMSP.js} +62 -27
- package/dist/chunk-7Q44DMSP.js.map +1 -0
- package/dist/chunk-ACNBKXXJ.js +321 -0
- package/dist/chunk-ACNBKXXJ.js.map +1 -0
- package/dist/{chunk-QGGSLMO3.js → chunk-B7CCVP6Q.js} +41 -9
- package/dist/chunk-B7CCVP6Q.js.map +1 -0
- package/dist/{chunk-LVKRVFYR.js → chunk-BDLUZVKU.js} +2 -2
- package/dist/chunk-BDLUZVKU.js.map +1 -0
- package/dist/{chunk-HNDT5QRB.js → chunk-CDX4W4DM.js} +3 -3
- package/dist/chunk-CDX4W4DM.js.map +1 -0
- package/dist/{chunk-MYAVQ23U.js → chunk-CJEHB4TB.js} +23 -9
- package/dist/chunk-CJEHB4TB.js.map +1 -0
- package/dist/{chunk-DKGL77IY.js → chunk-CPZOTVCI.js} +15 -14
- package/dist/chunk-CPZOTVCI.js.map +1 -0
- package/dist/{chunk-K7PTOVX4.js → chunk-D237EVNB.js} +32 -3
- package/dist/chunk-D237EVNB.js.map +1 -0
- package/dist/{chunk-Q2LH2DAB.js → chunk-DRXPNNPB.js} +19 -18
- package/dist/chunk-DRXPNNPB.js.map +1 -0
- package/dist/{chunk-Y3V43XCU.js → chunk-E3FJH4TF.js} +12 -8
- package/dist/chunk-E3FJH4TF.js.map +1 -0
- package/dist/{chunk-3HBTELJN.js → chunk-ED4E6Q2T.js} +10 -10
- package/dist/chunk-ED4E6Q2T.js.map +1 -0
- package/dist/{chunk-SYQ7R2JO.js → chunk-EHS3TWWP.js} +3 -3
- package/dist/chunk-EHS3TWWP.js.map +1 -0
- package/dist/{chunk-LQTST4WY.js → chunk-GDH553MV.js} +6 -6
- package/dist/chunk-GDH553MV.js.map +1 -0
- package/dist/{chunk-A65SBAAJ.js → chunk-HLJX2FTL.js} +31 -5
- package/dist/chunk-HLJX2FTL.js.map +1 -0
- package/dist/chunk-I2DSLOXZ.js +75 -0
- package/dist/chunk-I2DSLOXZ.js.map +1 -0
- package/dist/{chunk-SEBPPFUW.js → chunk-I4TPKIYX.js} +33 -18
- package/dist/chunk-I4TPKIYX.js.map +1 -0
- package/dist/{chunk-NGBFJJ7Q.js → chunk-IMDW5ZUP.js} +19 -11
- package/dist/chunk-IMDW5ZUP.js.map +1 -0
- package/dist/{chunk-U4VYHKPM.js → chunk-JIPWV2FX.js} +6 -6
- package/dist/chunk-JIPWV2FX.js.map +1 -0
- package/dist/{chunk-G2FBJOZG.js → chunk-K7EVM5LY.js} +4 -4
- package/dist/chunk-K7EVM5LY.js.map +1 -0
- package/dist/{chunk-MJSFR562.js → chunk-KAAEN2PO.js} +3 -3
- package/dist/chunk-KAAEN2PO.js.map +1 -0
- package/dist/{chunk-FNOYEXUE.js → chunk-LE6IY6IT.js} +22 -17
- package/dist/chunk-LE6IY6IT.js.map +1 -0
- package/dist/{chunk-CIY5UBRB.js → chunk-NDY25DTL.js} +6 -6
- package/dist/chunk-NDY25DTL.js.map +1 -0
- package/dist/{chunk-OLBOTK3O.js → chunk-P26A2K5N.js} +7 -7
- package/dist/chunk-P26A2K5N.js.map +1 -0
- package/dist/{chunk-DHFFRMF6.js → chunk-RTE64SJA.js} +2 -2
- package/dist/chunk-RTE64SJA.js.map +1 -0
- package/dist/{chunk-3ZCB5K33.js → chunk-SGJ23HIP.js} +14 -11
- package/dist/chunk-SGJ23HIP.js.map +1 -0
- package/dist/{chunk-C4ZTIYFR.js → chunk-SVYPSDWY.js} +10 -10
- package/dist/chunk-SVYPSDWY.js.map +1 -0
- package/dist/{chunk-MMDXNZPF.js → chunk-TKFPKEV3.js} +2 -2
- package/dist/chunk-TKFPKEV3.js.map +1 -0
- package/dist/{chunk-M7YCPFIX.js → chunk-UQTEPWU7.js} +2 -2
- package/dist/chunk-UQTEPWU7.js.map +1 -0
- package/dist/{chunk-QESUUPOE.js → chunk-V46GGCCI.js} +80 -27
- package/dist/chunk-V46GGCCI.js.map +1 -0
- package/dist/{chunk-UOWHJ6BE.js → chunk-X6QONICW.js} +6 -3
- package/dist/chunk-X6QONICW.js.map +1 -0
- package/dist/chunk-Y2LW7R3Y.js +23 -0
- package/dist/chunk-Y2LW7R3Y.js.map +1 -0
- package/dist/chunk-Z2CWURZ6.js +78 -0
- package/dist/chunk-Z2CWURZ6.js.map +1 -0
- package/dist/chunk-Z7G4B5HJ.js +377 -0
- package/dist/chunk-Z7G4B5HJ.js.map +1 -0
- package/dist/{chunk-XKEG3SCV.js → chunk-ZENYS6KW.js} +13 -9
- package/dist/chunk-ZENYS6KW.js.map +1 -0
- package/dist/{cli-v2 → cli}/defaults/agent-mappings.yaml +5 -5
- package/dist/commands/build/marketplace.js +9 -9
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +12 -12
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/build/stack.js +15 -15
- package/dist/commands/build/stack.js.map +1 -1
- package/dist/commands/compile.js +21 -21
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/config/get.js +9 -8
- package/dist/commands/config/get.js.map +1 -1
- package/dist/commands/config/index.js +7 -6
- package/dist/commands/config/index.js.map +1 -1
- package/dist/commands/config/path.js +8 -7
- package/dist/commands/config/path.js.map +1 -1
- package/dist/commands/config/set-project.js +9 -8
- package/dist/commands/config/set-project.js.map +1 -1
- package/dist/commands/config/set.js +9 -8
- package/dist/commands/config/set.js.map +1 -1
- package/dist/commands/config/show.js +6 -5
- package/dist/commands/config/unset-project.js +9 -8
- package/dist/commands/config/unset-project.js.map +1 -1
- package/dist/commands/config/unset.js +9 -8
- package/dist/commands/config/unset.js.map +1 -1
- package/dist/commands/diff.js +12 -12
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.js +10 -10
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +52 -49
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +180 -97
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/import/skill.js +339 -0
- package/dist/commands/import/skill.js.map +1 -0
- package/dist/commands/info.js +9 -9
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.js +205 -78
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +9 -9
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/agent.js +19 -21
- package/dist/commands/new/agent.js.map +1 -1
- package/dist/commands/new/skill.js +11 -12
- package/dist/commands/new/skill.js.map +1 -1
- package/dist/commands/outdated.js +12 -12
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/search.js +205 -17
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/test-imports.js +18 -18
- package/dist/commands/test-imports.js.map +1 -1
- package/dist/commands/uninstall.js +58 -78
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +21 -21
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.js +9 -9
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/version/bump.js +8 -8
- package/dist/commands/version/bump.js.map +1 -1
- package/dist/commands/version/index.js +8 -8
- package/dist/commands/version/index.js.map +1 -1
- package/dist/commands/version/set.js +7 -7
- package/dist/commands/version/set.js.map +1 -1
- package/dist/commands/version/show.js +8 -8
- package/dist/commands/version/show.js.map +1 -1
- package/dist/components/common/confirm.js +1 -1
- package/dist/components/common/message.js +1 -1
- package/dist/components/common/message.js.map +1 -1
- package/dist/components/common/spinner.js +1 -1
- package/dist/components/common/spinner.js.map +1 -1
- package/dist/components/skill-search/skill-search.js +10 -0
- package/dist/components/skill-search/skill-search.js.map +1 -0
- package/dist/components/wizard/category-grid.js +1 -1
- package/dist/components/wizard/category-grid.test.js +213 -80
- package/dist/components/wizard/category-grid.test.js.map +1 -1
- package/dist/components/wizard/section-progress.js +1 -1
- package/dist/components/wizard/section-progress.test.js +2 -2
- package/dist/components/wizard/section-progress.test.js.map +1 -1
- package/dist/components/wizard/step-approach.js +4 -3
- package/dist/components/wizard/step-build.js +4 -3
- package/dist/components/wizard/step-build.test.js +29 -17
- package/dist/components/wizard/step-build.test.js.map +1 -1
- package/dist/components/wizard/step-confirm.js +2 -1
- package/dist/components/wizard/step-refine.js +2 -1
- package/dist/components/wizard/step-refine.test.js +4 -3
- package/dist/components/wizard/step-refine.test.js.map +1 -1
- package/dist/components/wizard/step-stack-options.js +3 -3
- package/dist/components/wizard/step-stack.js +3 -3
- package/dist/components/wizard/wizard-footer.js +9 -0
- package/dist/components/wizard/wizard-footer.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +1 -1
- package/dist/components/wizard/wizard.js +14 -12
- package/dist/config/skills-matrix.yaml +858 -0
- package/dist/config/stacks.yaml +254 -0
- package/dist/hooks/init.js +5 -4
- package/dist/hooks/init.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/wizard-store.js +2 -2
- package/dist/stores/wizard-store.test.js +3 -3
- package/dist/stores/wizard-store.test.js.map +1 -1
- package/package.json +4 -2
- package/dist/chunk-3HBTELJN.js.map +0 -1
- package/dist/chunk-3ZCB5K33.js.map +0 -1
- package/dist/chunk-6LS7XO3H.js.map +0 -1
- package/dist/chunk-A3J6IAXK.js.map +0 -1
- package/dist/chunk-A65SBAAJ.js.map +0 -1
- package/dist/chunk-ALEPJ6YN.js +0 -80
- package/dist/chunk-ALEPJ6YN.js.map +0 -1
- package/dist/chunk-C4ZTIYFR.js.map +0 -1
- package/dist/chunk-CIY5UBRB.js.map +0 -1
- package/dist/chunk-DHFFRMF6.js.map +0 -1
- package/dist/chunk-DKGL77IY.js.map +0 -1
- package/dist/chunk-ED73HCW2.js.map +0 -1
- package/dist/chunk-FNOYEXUE.js.map +0 -1
- package/dist/chunk-G2FBJOZG.js.map +0 -1
- package/dist/chunk-HNDT5QRB.js.map +0 -1
- package/dist/chunk-K7PTOVX4.js.map +0 -1
- package/dist/chunk-LQTST4WY.js.map +0 -1
- package/dist/chunk-LVKRVFYR.js.map +0 -1
- package/dist/chunk-M7YCPFIX.js.map +0 -1
- package/dist/chunk-MJSFR562.js.map +0 -1
- package/dist/chunk-MMDXNZPF.js.map +0 -1
- package/dist/chunk-MYAVQ23U.js.map +0 -1
- package/dist/chunk-NGBFJJ7Q.js.map +0 -1
- package/dist/chunk-OLBOTK3O.js.map +0 -1
- package/dist/chunk-PPNTD5LO.js +0 -330
- package/dist/chunk-PPNTD5LO.js.map +0 -1
- package/dist/chunk-Q2LH2DAB.js.map +0 -1
- package/dist/chunk-Q6DR5QUH.js.map +0 -1
- package/dist/chunk-QESUUPOE.js.map +0 -1
- package/dist/chunk-QGGSLMO3.js.map +0 -1
- package/dist/chunk-SEBPPFUW.js.map +0 -1
- package/dist/chunk-SYQ7R2JO.js.map +0 -1
- package/dist/chunk-TOPAIL5W.js.map +0 -1
- package/dist/chunk-U4VYHKPM.js.map +0 -1
- package/dist/chunk-UOWHJ6BE.js.map +0 -1
- package/dist/chunk-XKEG3SCV.js.map +0 -1
- package/dist/chunk-Y3V43XCU.js.map +0 -1
- package/dist/chunk-YKXBGCFD.js.map +0 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
DIRS
|
|
4
|
-
} from "./chunk-A3J6IAXK.js";
|
|
5
2
|
import {
|
|
6
3
|
verbose
|
|
7
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3U3R4NCG.js";
|
|
8
5
|
import {
|
|
9
6
|
directoryExists,
|
|
10
7
|
fileExists,
|
|
11
8
|
glob,
|
|
12
9
|
readFile
|
|
13
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TKFPKEV3.js";
|
|
11
|
+
import {
|
|
12
|
+
CLAUDE_SRC_DIR,
|
|
13
|
+
DIRS
|
|
14
|
+
} from "./chunk-76DWXGQE.js";
|
|
14
15
|
import {
|
|
15
16
|
init_esm_shims
|
|
16
17
|
} from "./chunk-DHET7RCE.js";
|
|
17
18
|
|
|
18
|
-
// src/cli
|
|
19
|
+
// src/cli/lib/loader.ts
|
|
19
20
|
init_esm_shims();
|
|
20
21
|
import { parse as parseYaml } from "yaml";
|
|
21
22
|
import path from "path";
|
|
@@ -55,6 +56,34 @@ async function loadAllAgents(projectRoot) {
|
|
|
55
56
|
}
|
|
56
57
|
return agents;
|
|
57
58
|
}
|
|
59
|
+
async function loadProjectAgents(projectRoot) {
|
|
60
|
+
const agents = {};
|
|
61
|
+
const projectAgentsDir = path.join(projectRoot, CLAUDE_SRC_DIR, "agents");
|
|
62
|
+
if (!await directoryExists(projectAgentsDir)) {
|
|
63
|
+
verbose(`No project agents directory at ${projectAgentsDir}`);
|
|
64
|
+
return agents;
|
|
65
|
+
}
|
|
66
|
+
const files = await glob("**/agent.yaml", projectAgentsDir);
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
const fullPath = path.join(projectAgentsDir, file);
|
|
69
|
+
const content = await readFile(fullPath);
|
|
70
|
+
const config = parseYaml(content);
|
|
71
|
+
const agentPath = path.dirname(file);
|
|
72
|
+
agents[config.id] = {
|
|
73
|
+
title: config.title,
|
|
74
|
+
description: config.description,
|
|
75
|
+
model: config.model,
|
|
76
|
+
tools: config.tools,
|
|
77
|
+
path: agentPath,
|
|
78
|
+
sourceRoot: projectRoot,
|
|
79
|
+
agentBaseDir: `${CLAUDE_SRC_DIR}/agents`,
|
|
80
|
+
// Project agents are in .claude-src/agents/
|
|
81
|
+
skills: config.skills
|
|
82
|
+
};
|
|
83
|
+
verbose(`Loaded project agent: ${config.id} from ${file}`);
|
|
84
|
+
}
|
|
85
|
+
return agents;
|
|
86
|
+
}
|
|
58
87
|
async function buildIdToDirectoryPathMap(skillsDir) {
|
|
59
88
|
const map = {};
|
|
60
89
|
const files = await glob("**/SKILL.md", skillsDir);
|
|
@@ -163,7 +192,7 @@ async function loadPluginSkills(pluginDir) {
|
|
|
163
192
|
return skills;
|
|
164
193
|
}
|
|
165
194
|
|
|
166
|
-
// src/cli
|
|
195
|
+
// src/cli/lib/stacks-loader.ts
|
|
167
196
|
init_esm_shims();
|
|
168
197
|
import { parse as parseYaml2 } from "yaml";
|
|
169
198
|
import path2 from "path";
|
|
@@ -233,7 +262,7 @@ function resolveAgentConfigToSkills(agentConfig, skillAliases) {
|
|
|
233
262
|
return skillRefs;
|
|
234
263
|
}
|
|
235
264
|
|
|
236
|
-
// src/cli
|
|
265
|
+
// src/cli/lib/matrix-loader.ts
|
|
237
266
|
init_esm_shims();
|
|
238
267
|
import { parse as parseYaml3 } from "yaml";
|
|
239
268
|
import path3 from "path";
|
|
@@ -518,6 +547,7 @@ function buildResolvedSkill(skill, matrix, aliases, aliasesReverse, directoryPat
|
|
|
518
547
|
}
|
|
519
548
|
}
|
|
520
549
|
}
|
|
550
|
+
const compatibleWith = skill.compatibleWith.map(resolve);
|
|
521
551
|
return {
|
|
522
552
|
id: skill.id,
|
|
523
553
|
alias: aliasesReverse[skill.id],
|
|
@@ -535,6 +565,7 @@ function buildResolvedSkill(skill, matrix, aliases, aliasesReverse, directoryPat
|
|
|
535
565
|
requiredBy: [],
|
|
536
566
|
alternatives,
|
|
537
567
|
discourages,
|
|
568
|
+
compatibleWith,
|
|
538
569
|
requiresSetup: skill.requiresSetup.map(resolve),
|
|
539
570
|
providesSetupFor: skill.providesSetupFor.map(resolve),
|
|
540
571
|
path: skill.path
|
|
@@ -595,6 +626,7 @@ function resolveSuggestedStacks(matrix, aliases, aliasTargetToSkillId) {
|
|
|
595
626
|
export {
|
|
596
627
|
parseFrontmatter,
|
|
597
628
|
loadAllAgents,
|
|
629
|
+
loadProjectAgents,
|
|
598
630
|
loadSkillsByIds,
|
|
599
631
|
loadPluginSkills,
|
|
600
632
|
loadStacks,
|
|
@@ -604,4 +636,4 @@ export {
|
|
|
604
636
|
extractAllSkills,
|
|
605
637
|
mergeMatrixWithSkills
|
|
606
638
|
};
|
|
607
|
-
//# sourceMappingURL=chunk-
|
|
639
|
+
//# sourceMappingURL=chunk-B7CCVP6Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/loader.ts","../src/cli/lib/stacks-loader.ts","../src/cli/lib/matrix-loader.ts"],"sourcesContent":["import { parse as parseYaml } from \"yaml\";\nimport path from \"path\";\nimport { glob, readFile, directoryExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { CLAUDE_SRC_DIR, DIRS } from \"../consts\";\nimport type {\n AgentDefinition,\n AgentYamlConfig,\n SkillDefinition,\n SkillFrontmatter,\n StackConfig,\n} from \"../types\";\n\nexport type CompileMode = \"dev\";\n\nexport function getDirs(_mode: CompileMode) {\n return DIRS;\n}\n\nconst FRONTMATTER_REGEX = /^---\\n([\\s\\S]*?)\\n---/;\n\nexport function parseFrontmatter(content: string): SkillFrontmatter | null {\n const match = content.match(FRONTMATTER_REGEX);\n if (!match) return null;\n\n const yamlContent = match[1];\n const frontmatter = parseYaml(yamlContent) as SkillFrontmatter;\n\n if (!frontmatter.name || !frontmatter.description) return null;\n return frontmatter;\n}\n\nfunction extractDisplayName(skillId: string): string {\n const withoutCategory = skillId.split(\"/\").pop() || skillId;\n const withoutAuthor = withoutCategory.replace(/\\s*\\(@\\w+\\)$/, \"\").trim();\n return withoutAuthor\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\nexport async function loadAllAgents(\n projectRoot: string,\n): Promise<Record<string, AgentDefinition>> {\n const agents: Record<string, AgentDefinition> = {};\n const agentSourcesDir = path.join(projectRoot, DIRS.agents);\n\n const files = await glob(\"**/agent.yaml\", agentSourcesDir);\n\n for (const file of files) {\n const fullPath = path.join(agentSourcesDir, file);\n const content = await readFile(fullPath);\n const config = parseYaml(content) as AgentYamlConfig;\n const agentPath = path.dirname(file);\n\n agents[config.id] = {\n title: config.title,\n description: config.description,\n model: config.model,\n tools: config.tools,\n path: agentPath,\n sourceRoot: projectRoot,\n skills: config.skills,\n };\n\n verbose(`Loaded agent: ${config.id} from ${file}`);\n }\n\n return agents;\n}\n\n/**\n * Load agents from project's .claude-src/agents/ directory.\n * Returns empty object if directory doesn't exist.\n */\nexport async function loadProjectAgents(\n projectRoot: string,\n): Promise<Record<string, AgentDefinition>> {\n const agents: Record<string, AgentDefinition> = {};\n const projectAgentsDir = path.join(projectRoot, CLAUDE_SRC_DIR, \"agents\");\n\n if (!(await directoryExists(projectAgentsDir))) {\n verbose(`No project agents directory at ${projectAgentsDir}`);\n return agents;\n }\n\n const files = await glob(\"**/agent.yaml\", projectAgentsDir);\n\n for (const file of files) {\n const fullPath = path.join(projectAgentsDir, file);\n const content = await readFile(fullPath);\n const config = parseYaml(content) as AgentYamlConfig;\n const agentPath = path.dirname(file);\n\n agents[config.id] = {\n title: config.title,\n description: config.description,\n model: config.model,\n tools: config.tools,\n path: agentPath,\n sourceRoot: projectRoot,\n agentBaseDir: `${CLAUDE_SRC_DIR}/agents`, // Project agents are in .claude-src/agents/\n skills: config.skills,\n };\n\n verbose(`Loaded project agent: ${config.id} from ${file}`);\n }\n\n return agents;\n}\n\n/** @deprecated Use loadSkillsByIds instead - stacks no longer embed skills */\nexport async function loadStackSkills(\n stackId: string,\n projectRoot: string,\n _mode: CompileMode = \"dev\",\n): Promise<Record<string, SkillDefinition>> {\n const skills: Record<string, SkillDefinition> = {};\n const stackSkillsDir = path.join(projectRoot, DIRS.stacks, stackId, \"skills\");\n\n if (!(await directoryExists(stackSkillsDir))) {\n verbose(`No embedded skills directory for stack ${stackId}`);\n return skills;\n }\n\n const files = await glob(\"**/SKILL.md\", stackSkillsDir);\n\n for (const file of files) {\n const fullPath = path.join(stackSkillsDir, file);\n const content = await readFile(fullPath);\n\n const frontmatter = parseFrontmatter(content);\n if (!frontmatter) {\n console.warn(\n ` Warning: Skipping ${file}: Missing or invalid frontmatter`,\n );\n continue;\n }\n\n const folderPath = file.replace(\"/SKILL.md\", \"\");\n const skillPath = `src/stacks/${stackId}/skills/${folderPath}/`;\n const skillId = frontmatter.name;\n\n skills[skillId] = {\n path: skillPath,\n name: extractDisplayName(frontmatter.name),\n description: frontmatter.description,\n canonicalId: skillId,\n };\n\n verbose(`Loaded stack skill: ${skillId} from ${file}`);\n }\n\n return skills;\n}\n\nasync function buildIdToDirectoryPathMap(\n skillsDir: string,\n): Promise<Record<string, string>> {\n const map: Record<string, string> = {};\n const files = await glob(\"**/SKILL.md\", skillsDir);\n\n for (const file of files) {\n const fullPath = path.join(skillsDir, file);\n const content = await readFile(fullPath);\n const frontmatter = parseFrontmatter(content);\n\n if (frontmatter?.name) {\n const directoryPath = file.replace(\"/SKILL.md\", \"\");\n map[frontmatter.name] = directoryPath;\n map[directoryPath] = directoryPath;\n }\n }\n\n return map;\n}\n\nexport async function loadSkillsByIds(\n skillIds: Array<{ id: string }>,\n projectRoot: string,\n): Promise<Record<string, SkillDefinition>> {\n const skills: Record<string, SkillDefinition> = {};\n const skillsDir = path.join(projectRoot, DIRS.skills);\n\n const idToDirectoryPath = await buildIdToDirectoryPathMap(skillsDir);\n const allSkillIds = Object.keys(idToDirectoryPath);\n const expandedSkillIds: string[] = [];\n\n for (const { id: skillId } of skillIds) {\n if (idToDirectoryPath[skillId]) {\n expandedSkillIds.push(skillId);\n } else {\n const childSkills = allSkillIds.filter((id) => {\n const dirPath = idToDirectoryPath[id];\n return dirPath.startsWith(skillId + \"/\");\n });\n\n if (childSkills.length > 0) {\n expandedSkillIds.push(...childSkills);\n verbose(\n `Expanded directory '${skillId}' to ${childSkills.length} skills`,\n );\n } else {\n console.warn(` Warning: Unknown skill reference '${skillId}'`);\n }\n }\n }\n\n const uniqueSkillIds = [...new Set(expandedSkillIds)];\n\n for (const skillId of uniqueSkillIds) {\n const directoryPath = idToDirectoryPath[skillId];\n if (!directoryPath) {\n console.warn(\n ` Warning: Could not find skill ${skillId}: No matching skill found`,\n );\n continue;\n }\n\n const skillPath = path.join(skillsDir, directoryPath);\n const skillMdPath = path.join(skillPath, \"SKILL.md\");\n\n try {\n const content = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(content);\n\n if (!frontmatter) {\n console.warn(\n ` Warning: Skipping ${skillId}: Missing or invalid frontmatter`,\n );\n continue;\n }\n\n const canonicalId = frontmatter.name;\n const skillDef: SkillDefinition = {\n path: `${DIRS.skills}/${directoryPath}/`,\n name: extractDisplayName(frontmatter.name),\n description: frontmatter.description,\n canonicalId,\n };\n\n skills[canonicalId] = skillDef;\n\n if (directoryPath !== canonicalId) {\n skills[directoryPath] = skillDef;\n }\n\n verbose(`Loaded skill: ${canonicalId} (from ${directoryPath})`);\n } catch (error) {\n console.warn(` Warning: Could not load skill ${skillId}: ${error}`);\n }\n }\n\n return skills;\n}\n\nexport async function loadPluginSkills(\n pluginDir: string,\n): Promise<Record<string, SkillDefinition>> {\n const skills: Record<string, SkillDefinition> = {};\n const pluginSkillsDir = path.join(pluginDir, \"skills\");\n\n if (!(await directoryExists(pluginSkillsDir))) {\n return skills;\n }\n\n const files = await glob(\"**/SKILL.md\", pluginSkillsDir);\n\n for (const file of files) {\n const fullPath = path.join(pluginSkillsDir, file);\n const content = await readFile(fullPath);\n\n const frontmatter = parseFrontmatter(content);\n if (!frontmatter) {\n console.warn(\n ` Warning: Skipping ${file}: Missing or invalid frontmatter`,\n );\n continue;\n }\n\n const folderPath = file.replace(\"/SKILL.md\", \"\");\n const skillPath = `skills/${folderPath}/`;\n const skillId = frontmatter.name;\n\n skills[skillId] = {\n path: skillPath,\n name: extractDisplayName(frontmatter.name),\n description: frontmatter.description,\n canonicalId: skillId,\n };\n\n verbose(`Loaded plugin skill: ${skillId} from ${file}`);\n }\n\n return skills;\n}\n\nconst stackCache = new Map<string, StackConfig>();\n\n/**\n * @deprecated Use loadStackById from stacks-loader.ts instead.\n * This function loads legacy stack configs from src/stacks/{stackId}/config.yaml.\n * The new system uses config/stacks.yaml with agent-centric configuration\n * where skills are defined in each agent's agent.yaml file.\n *\n * Migration path:\n * 1. Use loadStackById() to get stack metadata (agents list)\n * 2. Skills come from agent.yaml files, not stack config\n *\n * @see src/cli/lib/stacks-loader.ts\n */\nexport async function loadStack(\n stackId: string,\n projectRoot: string,\n mode: CompileMode = \"dev\",\n): Promise<StackConfig> {\n const cacheKey = `${mode}:${stackId}`;\n const cached = stackCache.get(cacheKey);\n if (cached) return cached;\n\n const dirs = getDirs(mode);\n const stackPath = path.join(projectRoot, dirs.stacks, stackId, \"config.yaml\");\n\n try {\n const content = await readFile(stackPath);\n const stack = parseYaml(content) as StackConfig;\n stackCache.set(cacheKey, stack);\n verbose(`Loaded stack: ${stack.name} (${stackId})`);\n return stack;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load stack '${stackId}': ${errorMessage}. Expected config at: ${stackPath}`,\n );\n }\n}\n","import { parse as parseYaml } from \"yaml\";\nimport path from \"path\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport type { Stack, StacksConfig, StackAgentConfig } from \"../types-stacks\";\nimport type { SkillReference } from \"../types\";\n\nconst STACKS_FILE = \"config/stacks.yaml\";\n\nconst stacksCache = new Map<string, Stack[]>();\n\n/**\n * Load all stacks from config/stacks.yaml\n * Stacks are simple agent groupings without skill mappings\n */\nexport async function loadStacks(configDir: string): Promise<Stack[]> {\n const cacheKey = configDir;\n const cached = stacksCache.get(cacheKey);\n if (cached) return cached;\n\n const stacksPath = path.join(configDir, STACKS_FILE);\n\n if (!(await fileExists(stacksPath))) {\n verbose(`No stacks file found at ${stacksPath}`);\n return [];\n }\n\n try {\n const content = await readFile(stacksPath);\n const config = parseYaml(content) as StacksConfig;\n\n if (!config.stacks || !Array.isArray(config.stacks)) {\n verbose(`Invalid stacks.yaml format: missing stacks array`);\n return [];\n }\n\n stacksCache.set(cacheKey, config.stacks);\n verbose(`Loaded ${config.stacks.length} stacks from ${stacksPath}`);\n\n return config.stacks;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load stacks from '${stacksPath}': ${errorMessage}`,\n );\n }\n}\n\n/**\n * Load a specific stack by ID\n * Returns null if stack not found\n */\nexport async function loadStackById(\n stackId: string,\n configDir: string,\n): Promise<Stack | null> {\n const stacks = await loadStacks(configDir);\n const stack = stacks.find((s) => s.id === stackId);\n\n if (!stack) {\n verbose(`Stack '${stackId}' not found`);\n return null;\n }\n\n verbose(`Found stack: ${stack.name} (${stackId})`);\n return stack;\n}\n\n/**\n * Subcategories considered \"key\" skills that should be preloaded.\n * These are primary technology choices that define the stack's core.\n */\nconst KEY_SUBCATEGORIES = new Set([\n \"framework\",\n \"api\",\n \"database\",\n \"meta-framework\",\n \"base-framework\",\n \"platform\",\n]);\n\n/**\n * Resolve a single agent's technology config to skill references.\n * Looks up each technology alias in skill_aliases to get full skill ID.\n *\n * @param agentConfig - The agent's technology selections (subcategory -> alias)\n * @param skillAliases - Mapping from technology aliases to full skill IDs\n * @returns Array of SkillReference objects\n */\nexport function resolveAgentConfigToSkills(\n agentConfig: StackAgentConfig,\n skillAliases: Record<string, string>,\n): SkillReference[] {\n const skillRefs: SkillReference[] = [];\n\n for (const [subcategory, technologyAlias] of Object.entries(agentConfig)) {\n const fullSkillId = skillAliases[technologyAlias];\n\n if (!fullSkillId) {\n verbose(\n `Warning: No skill alias found for '${technologyAlias}' (subcategory: ${subcategory}). Skipping.`,\n );\n continue;\n }\n\n const isKeySkill = KEY_SUBCATEGORIES.has(subcategory);\n\n skillRefs.push({\n id: fullSkillId,\n usage: `when working with ${subcategory}`,\n preloaded: isKeySkill,\n });\n }\n\n return skillRefs;\n}\n\n/**\n * Resolve all agents in a stack to their skill references.\n * Takes a Stack and skill_aliases, returns a mapping of agent IDs to their resolved skills.\n *\n * @param stack - The stack definition with agent technology selections\n * @param skillAliases - Mapping from technology aliases to full skill IDs\n * @returns Record mapping agent IDs to their SkillReference arrays\n *\n * @example\n * ```typescript\n * const stack = {\n * id: 'nextjs-fullstack',\n * name: 'Next.js Fullstack',\n * agents: {\n * 'web-developer': { framework: 'react', styling: 'scss-modules' },\n * 'api-developer': { api: 'hono', database: 'drizzle' }\n * }\n * };\n *\n * const aliases = {\n * react: 'web/framework/react (@vince)',\n * hono: 'api/framework/hono (@vince)',\n * // ...\n * };\n *\n * const result = resolveStackSkillsFromAliases(stack, aliases);\n * // Returns:\n * // {\n * // 'web-developer': [{ id: 'web/framework/react (@vince)', usage: '...', preloaded: true }, ...],\n * // 'api-developer': [{ id: 'api/framework/hono (@vince)', usage: '...', preloaded: true }, ...]\n * // }\n * ```\n */\nexport function resolveStackSkillsFromAliases(\n stack: Stack,\n skillAliases: Record<string, string>,\n): Record<string, SkillReference[]> {\n const result: Record<string, SkillReference[]> = {};\n\n for (const [agentId, agentConfig] of Object.entries(stack.agents)) {\n // Empty config {} means agent has no technology-specific skills\n if (Object.keys(agentConfig).length === 0) {\n result[agentId] = [];\n continue;\n }\n\n result[agentId] = resolveAgentConfigToSkills(agentConfig, skillAliases);\n }\n\n verbose(\n `Resolved skills for ${Object.keys(result).length} agents in stack '${stack.id}'`,\n );\n\n return result;\n}\n","import { parse as parseYaml } from \"yaml\";\nimport path from \"path\";\nimport { glob, readFile, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { DIRS } from \"../consts\";\nimport { parseFrontmatter } from \"./loader\";\nimport type {\n SkillsMatrixConfig,\n ExtractedSkillMetadata,\n MergedSkillsMatrix,\n ResolvedSkill,\n ResolvedStack,\n SkillRelation,\n SkillRequirement,\n SkillAlternative,\n} from \"../types-matrix\";\n\ninterface RawMetadata {\n category: string;\n category_exclusive?: boolean;\n author: string;\n version: string;\n cli_name?: string;\n cli_description?: string;\n usage_guidance?: string;\n tags?: string[];\n compatible_with?: string[];\n conflicts_with?: string[];\n requires?: string[];\n requires_setup?: string[];\n provides_setup_for?: string[];\n}\n\nexport async function loadSkillsMatrix(\n configPath: string,\n): Promise<SkillsMatrixConfig> {\n const content = await readFile(configPath);\n const config = parseYaml(content) as SkillsMatrixConfig;\n\n validateMatrixStructure(config, configPath);\n\n verbose(`Loaded skills matrix: ${configPath}`);\n return config;\n}\n\nfunction validateMatrixStructure(\n config: SkillsMatrixConfig,\n configPath: string,\n): void {\n // Note: suggested_stacks removed from required - stacks now defined in config/stacks.yaml (Phase 6)\n const requiredFields = [\n \"version\",\n \"categories\",\n \"relationships\",\n \"skill_aliases\",\n ];\n const missing = requiredFields.filter((field) => !(field in config));\n\n if (missing.length > 0) {\n throw new Error(\n `Skills matrix at ${configPath} is missing required fields: ${missing.join(\", \")}`,\n );\n }\n\n const relationshipFields = [\n \"conflicts\",\n \"recommends\",\n \"requires\",\n \"alternatives\",\n ];\n const missingRelationships = relationshipFields.filter(\n (field) => !config.relationships || !(field in config.relationships),\n );\n\n if (missingRelationships.length > 0) {\n throw new Error(\n `Skills matrix relationships missing required fields: ${missingRelationships.join(\", \")}`,\n );\n }\n\n for (const [categoryId, category] of Object.entries(config.categories)) {\n const requiredCategoryFields = [\n \"id\",\n \"name\",\n \"description\",\n \"exclusive\",\n \"required\",\n \"order\",\n ];\n const missingCategoryFields = requiredCategoryFields.filter(\n (field) => !(field in category),\n );\n\n if (missingCategoryFields.length > 0) {\n throw new Error(\n `Category \"${categoryId}\" missing required fields: ${missingCategoryFields.join(\", \")}`,\n );\n }\n }\n}\n\nexport async function extractAllSkills(\n skillsDir: string,\n): Promise<ExtractedSkillMetadata[]> {\n const skills: ExtractedSkillMetadata[] = [];\n const metadataFiles = await glob(\"**/metadata.yaml\", skillsDir);\n\n for (const metadataFile of metadataFiles) {\n const skillDir = path.dirname(metadataFile);\n const skillMdPath = path.join(skillsDir, skillDir, \"SKILL.md\");\n const metadataPath = path.join(skillsDir, metadataFile);\n\n if (!(await fileExists(skillMdPath))) {\n verbose(`Skipping ${metadataFile}: No SKILL.md found`);\n continue;\n }\n\n const metadataContent = await readFile(metadataPath);\n const metadata = parseYaml(metadataContent) as RawMetadata;\n const skillMdContent = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(skillMdContent);\n\n if (!frontmatter) {\n verbose(`Skipping ${metadataFile}: Invalid SKILL.md frontmatter`);\n continue;\n }\n\n if (!metadata.cli_name) {\n throw new Error(\n `Skill at ${metadataFile} is missing required 'cli_name' field in metadata.yaml`,\n );\n }\n\n const skillId = frontmatter.name;\n\n const extracted: ExtractedSkillMetadata = {\n id: skillId,\n directoryPath: skillDir,\n name: metadata.cli_name,\n description: metadata.cli_description || frontmatter.description,\n usageGuidance: metadata.usage_guidance,\n category: metadata.category,\n categoryExclusive: metadata.category_exclusive ?? true,\n author: metadata.author,\n tags: metadata.tags ?? [],\n compatibleWith: metadata.compatible_with ?? [],\n conflictsWith: metadata.conflicts_with ?? [],\n requires: metadata.requires ?? [],\n requiresSetup: metadata.requires_setup ?? [],\n providesSetupFor: metadata.provides_setup_for ?? [],\n path: `skills/${skillDir}/`,\n };\n\n skills.push(extracted);\n verbose(`Extracted skill: ${skillId}`);\n }\n\n return skills;\n}\n\nfunction buildReverseAliases(\n aliases: Record<string, string>,\n): Record<string, string> {\n const reverse: Record<string, string> = {};\n for (const [alias, fullId] of Object.entries(aliases)) {\n reverse[fullId] = alias;\n }\n return reverse;\n}\n\n/**\n * Build a map from short names to actual skill IDs.\n *\n * This handles multiple formats used in skill metadata:\n * - \"react (@vince)\" -> \"web/framework/react (@vince)\"\n * - \"react\" -> \"web/framework/react (@vince)\"\n * - Old alias targets that may still exist in metadata files\n */\nfunction buildAliasTargetToSkillIdMap(\n aliases: Record<string, string>,\n skills: ExtractedSkillMetadata[],\n): Record<string, string> {\n const map: Record<string, string> = {};\n\n for (const skill of skills) {\n // Extract the \"short author\" form: last path segment with author\n // e.g., \"web/framework/react (@vince)\" -> \"react (@vince)\"\n const parts = skill.id.split(\"/\");\n const shortForm = parts[parts.length - 1];\n\n if (shortForm && shortForm !== skill.id) {\n map[shortForm] = skill.id;\n }\n\n // Also map directory name without author if different\n // e.g., \"web/framework/react\" -> \"web/framework/react (@vince)\"\n if (skill.directoryPath && skill.directoryPath !== skill.id) {\n map[skill.directoryPath] = skill.id;\n }\n }\n\n // Also include any old-style alias targets that might still be referenced\n const aliasTargets = new Set(Object.values(aliases));\n for (const skill of skills) {\n for (const aliasTarget of aliasTargets) {\n if (\n aliasTarget !== skill.id &&\n (skill.id.endsWith(`/${aliasTarget}`) || skill.id === aliasTarget)\n ) {\n map[aliasTarget] = skill.id;\n }\n }\n }\n\n return map;\n}\n\nfunction buildDirectoryPathToIdMap(\n skills: ExtractedSkillMetadata[],\n): Record<string, string> {\n const map: Record<string, string> = {};\n for (const skill of skills) {\n if (skill.directoryPath && skill.directoryPath !== skill.id) {\n map[skill.directoryPath] = skill.id;\n }\n }\n return map;\n}\n\nfunction resolveToCanonicalId(\n aliasOrId: string,\n aliases: Record<string, string>,\n directoryPathToId: Record<string, string> = {},\n aliasTargetToSkillId: Record<string, string> = {},\n): string {\n if (aliases[aliasOrId]) {\n return aliases[aliasOrId];\n }\n if (directoryPathToId[aliasOrId]) {\n return directoryPathToId[aliasOrId];\n }\n // Handle \"short author\" format like \"react (@vince)\" that maps to full ID\n if (aliasTargetToSkillId[aliasOrId]) {\n return aliasTargetToSkillId[aliasOrId];\n }\n return aliasOrId;\n}\n\nexport async function mergeMatrixWithSkills(\n matrix: SkillsMatrixConfig,\n skills: ExtractedSkillMetadata[],\n): Promise<MergedSkillsMatrix> {\n const aliases = matrix.skill_aliases;\n const aliasesReverse = buildReverseAliases(aliases);\n const directoryPathToId = buildDirectoryPathToIdMap(skills);\n const aliasTargetToSkillId = buildAliasTargetToSkillIdMap(aliases, skills);\n const resolvedSkills: Record<string, ResolvedSkill> = {};\n\n for (const skill of skills) {\n const resolved = buildResolvedSkill(\n skill,\n matrix,\n aliases,\n aliasesReverse,\n directoryPathToId,\n aliasTargetToSkillId,\n );\n resolvedSkills[skill.id] = resolved;\n }\n\n computeInverseRelationships(resolvedSkills);\n const suggestedStacks = resolveSuggestedStacks(\n matrix,\n aliases,\n aliasTargetToSkillId,\n );\n\n const merged: MergedSkillsMatrix = {\n version: matrix.version,\n categories: matrix.categories,\n skills: resolvedSkills,\n suggestedStacks,\n aliases,\n aliasesReverse,\n generatedAt: new Date().toISOString(),\n };\n\n return merged;\n}\n\nfunction buildResolvedSkill(\n skill: ExtractedSkillMetadata,\n matrix: SkillsMatrixConfig,\n aliases: Record<string, string>,\n aliasesReverse: Record<string, string>,\n directoryPathToId: Record<string, string>,\n aliasTargetToSkillId: Record<string, string>,\n): ResolvedSkill {\n const conflictsWith: SkillRelation[] = [];\n const recommends: SkillRelation[] = [];\n const requires: SkillRequirement[] = [];\n const alternatives: SkillAlternative[] = [];\n const discourages: SkillRelation[] = [];\n\n // Helper to resolve with all maps\n const resolve = (id: string) =>\n resolveToCanonicalId(id, aliases, directoryPathToId, aliasTargetToSkillId);\n\n for (const conflictRef of skill.conflictsWith) {\n const canonicalId = resolve(conflictRef);\n conflictsWith.push({\n skillId: canonicalId,\n reason: \"Defined in skill metadata\",\n });\n }\n\n for (const conflictRule of matrix.relationships.conflicts) {\n const resolvedSkills = conflictRule.skills.map(resolve);\n if (resolvedSkills.includes(skill.id)) {\n for (const otherSkill of resolvedSkills) {\n if (otherSkill !== skill.id) {\n if (!conflictsWith.some((c) => c.skillId === otherSkill)) {\n conflictsWith.push({\n skillId: otherSkill,\n reason: conflictRule.reason,\n });\n }\n }\n }\n }\n }\n\n for (const compatRef of skill.compatibleWith) {\n const canonicalId = resolve(compatRef);\n recommends.push({\n skillId: canonicalId,\n reason: \"Compatible with this skill\",\n });\n }\n\n for (const recommendRule of matrix.relationships.recommends) {\n const whenCanonicalId = resolve(recommendRule.when);\n if (whenCanonicalId === skill.id) {\n for (const suggested of recommendRule.suggest) {\n const canonicalId = resolve(suggested);\n if (!recommends.some((r) => r.skillId === canonicalId)) {\n recommends.push({\n skillId: canonicalId,\n reason: recommendRule.reason,\n });\n }\n }\n }\n }\n\n if (skill.requires.length > 0) {\n requires.push({\n skillIds: skill.requires.map(resolve),\n needsAny: false,\n reason: \"Defined in skill metadata\",\n });\n }\n\n for (const requireRule of matrix.relationships.requires) {\n const skillCanonicalId = resolve(requireRule.skill);\n if (skillCanonicalId === skill.id) {\n requires.push({\n skillIds: requireRule.needs.map(resolve),\n needsAny: requireRule.needs_any ?? false,\n reason: requireRule.reason,\n });\n }\n }\n\n for (const altGroup of matrix.relationships.alternatives) {\n const resolvedAlts = altGroup.skills.map(resolve);\n if (resolvedAlts.includes(skill.id)) {\n for (const altSkill of resolvedAlts) {\n if (altSkill !== skill.id) {\n alternatives.push({\n skillId: altSkill,\n purpose: altGroup.purpose,\n });\n }\n }\n }\n }\n\n if (matrix.relationships.discourages) {\n for (const discourageRule of matrix.relationships.discourages) {\n const resolvedSkills = discourageRule.skills.map(resolve);\n if (resolvedSkills.includes(skill.id)) {\n for (const otherSkill of resolvedSkills) {\n if (otherSkill !== skill.id) {\n if (!discourages.some((d) => d.skillId === otherSkill)) {\n discourages.push({\n skillId: otherSkill,\n reason: discourageRule.reason,\n });\n }\n }\n }\n }\n }\n }\n\n // Preserve compatibleWith as resolved skill IDs for framework-first filtering\n const compatibleWith = skill.compatibleWith.map(resolve);\n\n return {\n id: skill.id,\n alias: aliasesReverse[skill.id],\n name: skill.name,\n description: skill.description,\n usageGuidance: skill.usageGuidance,\n category: skill.category,\n categoryExclusive: skill.categoryExclusive,\n tags: skill.tags,\n author: skill.author,\n conflictsWith,\n recommends,\n recommendedBy: [],\n requires,\n requiredBy: [],\n alternatives,\n discourages,\n compatibleWith,\n requiresSetup: skill.requiresSetup.map(resolve),\n providesSetupFor: skill.providesSetupFor.map(resolve),\n path: skill.path,\n };\n}\n\nfunction computeInverseRelationships(\n skills: Record<string, ResolvedSkill>,\n): void {\n for (const skill of Object.values(skills)) {\n for (const recommend of skill.recommends) {\n const targetSkill = skills[recommend.skillId];\n if (targetSkill) {\n targetSkill.recommendedBy.push({\n skillId: skill.id,\n reason: recommend.reason,\n });\n }\n }\n\n for (const requirement of skill.requires) {\n for (const requiredId of requirement.skillIds) {\n const targetSkill = skills[requiredId];\n if (targetSkill) {\n targetSkill.requiredBy.push({\n skillId: skill.id,\n reason: requirement.reason,\n });\n }\n }\n }\n }\n}\n\nfunction resolveSuggestedStacks(\n matrix: SkillsMatrixConfig,\n aliases: Record<string, string>,\n aliasTargetToSkillId: Record<string, string>,\n): ResolvedStack[] {\n // Phase 6: suggested_stacks is now optional (stacks moved to config/stacks.yaml)\n if (!matrix.suggested_stacks) {\n return [];\n }\n return matrix.suggested_stacks.map((stack) => {\n const resolvedSkillsMap: Record<string, Record<string, string>> = {};\n const allSkillIds: string[] = [];\n\n for (const [category, subcategories] of Object.entries(stack.skills)) {\n resolvedSkillsMap[category] = {};\n for (const [subcategory, alias] of Object.entries(subcategories)) {\n // First resolve the alias to its target (e.g., \"react\" -> \"react (@vince)\")\n const aliasTarget = resolveToCanonicalId(alias, aliases);\n // Then check if the alias target needs to be mapped to a full skill ID\n // (e.g., \"react (@vince)\" -> \"web/framework/react (@vince)\")\n const canonicalId = aliasTargetToSkillId[aliasTarget] || aliasTarget;\n resolvedSkillsMap[category][subcategory] = canonicalId;\n allSkillIds.push(canonicalId);\n }\n }\n\n return {\n id: stack.id,\n name: stack.name,\n description: stack.description,\n audience: stack.audience,\n skills: resolvedSkillsMap,\n allSkillIds,\n philosophy: stack.philosophy,\n };\n });\n}\n\nexport async function loadAndMergeSkillsMatrix(\n matrixPath: string,\n projectRoot: string,\n): Promise<MergedSkillsMatrix> {\n const matrix = await loadSkillsMatrix(matrixPath);\n const skillsDir = path.join(projectRoot, DIRS.skills);\n const skills = await extractAllSkills(skillsDir);\n return mergeMatrixWithSkills(matrix, skills);\n}\n"],"mappings":"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","names":["parseYaml","path","path","parseYaml","parseYaml","path","parseYaml","path"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
init_esm_shims
|
|
4
4
|
} from "./chunk-DHET7RCE.js";
|
|
5
5
|
|
|
6
|
-
// src/cli
|
|
6
|
+
// src/cli/components/wizard/section-progress.tsx
|
|
7
7
|
init_esm_shims();
|
|
8
8
|
import { Box, Text } from "ink";
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -51,4 +51,4 @@ var SectionProgress = ({
|
|
|
51
51
|
export {
|
|
52
52
|
SectionProgress
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-BDLUZVKU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/section-progress.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport interface SectionProgressProps {\n /** Section label (e.g., \"Domain\" or \"Skill\") */\n label: string;\n /** Current item name (e.g., \"Web\" or \"react\") */\n current: string;\n /** 1-based index */\n index: number;\n /** Total count */\n total: number;\n /** Next item name, or undefined if last */\n next?: string;\n}\n\nexport const SectionProgress: React.FC<SectionProgressProps> = ({\n label,\n current,\n index,\n total,\n next,\n}) => {\n const isLast = index === total;\n const rightText = isLast ? \"Last step\" : `Next: ${next}`;\n\n return (\n <Box\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n paddingX={2}\n marginBottom={1}\n >\n <Text>\n <Text bold>{label}:</Text> <Text color=\"cyan\">{current}</Text>\n </Text>\n <Text>\n <Text dimColor>\n [{index}/{total}]\n </Text>{\" \"}\n <Text dimColor={isLast}>{rightText}</Text>\n </Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;AAAA;AACA,SAAS,KAAK,YAAY;AAiClB,SAA2B,KAA3B;AAlBD,IAAM,kBAAkD,CAAC;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,SAAS,cAAc,SAAS,IAAI;AAEtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,UAAU;AAAA,MACV,cAAc;AAAA,MAEd;AAAA,6BAAC,QACC;AAAA,+BAAC,QAAK,MAAI,MAAE;AAAA;AAAA,YAAM;AAAA,aAAC;AAAA,UAAO;AAAA,UAAC,oBAAC,QAAK,OAAM,QAAQ,mBAAQ;AAAA,WACzD;AAAA,QACA,qBAAC,QACC;AAAA,+BAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,YACX;AAAA,YAAM;AAAA,YAAE;AAAA,YAAM;AAAA,aAClB;AAAA,UAAQ;AAAA,UACR,oBAAC,QAAK,UAAU,QAAS,qBAAU;AAAA,WACrC;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useWizardStore
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-D237EVNB.js";
|
|
5
5
|
import {
|
|
6
6
|
init_esm_shims
|
|
7
7
|
} from "./chunk-DHET7RCE.js";
|
|
8
8
|
|
|
9
|
-
// src/cli
|
|
9
|
+
// src/cli/components/wizard/step-stack.tsx
|
|
10
10
|
init_esm_shims();
|
|
11
11
|
import { Box, Text } from "ink";
|
|
12
12
|
import { Select } from "@inkjs/ui";
|
|
@@ -117,4 +117,4 @@ var StepStack = ({ matrix }) => {
|
|
|
117
117
|
export {
|
|
118
118
|
StepStack
|
|
119
119
|
};
|
|
120
|
-
//# sourceMappingURL=chunk-
|
|
120
|
+
//# sourceMappingURL=chunk-CDX4W4DM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/step-stack.tsx"],"sourcesContent":["/**\n * StepStack component - Dual-purpose step for stack selection or domain selection.\n *\n * Stack path (approach === \"stack\"):\n * - Shows list of pre-built stacks from matrix.suggestedStacks\n * - Selecting a stack populates domainSelections and goes to stack-options\n *\n * Scratch path (approach === \"scratch\"):\n * - Shows multi-select of domains (Web, API, CLI, Mobile)\n * - User selects which domains to configure\n * - Continue goes to build step for first selected domain\n */\nimport React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { Select } from \"@inkjs/ui\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { MergedSkillsMatrix } from \"../../types-matrix.js\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst BACK_VALUE = \"_back\";\nconst CONTINUE_VALUE = \"_continue\";\n\n/** Available domains for scratch path */\nconst AVAILABLE_DOMAINS = [\n { id: \"web\", label: \"Web\", description: \"Frontend web applications\" },\n { id: \"api\", label: \"API\", description: \"Backend APIs and services\" },\n { id: \"cli\", label: \"CLI\", description: \"Command-line tools\" },\n { id: \"mobile\", label: \"Mobile\", description: \"Mobile applications\" },\n];\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface StepStackProps {\n matrix: MergedSkillsMatrix;\n}\n\n// =============================================================================\n// Stack Selection Sub-component\n// =============================================================================\n\ninterface StackSelectionProps {\n matrix: MergedSkillsMatrix;\n}\n\nconst StackSelection: React.FC<StackSelectionProps> = ({ matrix }) => {\n const { selectStack, setStep, goBack } = useWizardStore();\n\n // Build options from matrix.suggestedStacks\n const options = [\n { value: BACK_VALUE, label: \"\\u2190 Back\" },\n ...matrix.suggestedStacks.map((stack) => ({\n value: stack.id,\n label: `${stack.name} - ${stack.description}`,\n })),\n ];\n\n const handleSelect = (value: string) => {\n if (value === BACK_VALUE) {\n goBack();\n return;\n }\n\n const stack = matrix.suggestedStacks.find((s) => s.id === value);\n if (stack) {\n selectStack(stack.id);\n setStep(\"stack-options\");\n }\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>Select a pre-built template:</Text>\n <Box marginTop={1}>\n <Select\n options={options}\n onChange={handleSelect}\n visibleOptionCount={options.length}\n />\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n {\"\\u2191\"}/{\"\\u2193\"} navigate ENTER select ESC back\n </Text>\n </Box>\n </Box>\n );\n};\n\n// =============================================================================\n// Domain Selection Sub-component\n// =============================================================================\n\nconst DomainSelection: React.FC = () => {\n const { selectedDomains, toggleDomain, setStep, goBack } = useWizardStore();\n\n // Build options with checkboxes showing selection state\n const domainOptions = AVAILABLE_DOMAINS.map((domain) => {\n const isSelected = selectedDomains.includes(domain.id);\n const checkbox = isSelected ? \"[\\u2713]\" : \"[ ]\";\n return {\n value: domain.id,\n label: `${checkbox} ${domain.label} - ${domain.description}`,\n };\n });\n\n const options = [\n { value: BACK_VALUE, label: \"\\u2190 Back\" },\n ...domainOptions,\n ...(selectedDomains.length > 0\n ? [\n {\n value: CONTINUE_VALUE,\n label: `\\u2192 Continue with ${selectedDomains.length} domain(s)`,\n },\n ]\n : []),\n ];\n\n const handleSelect = (value: string) => {\n if (value === BACK_VALUE) {\n goBack();\n return;\n }\n\n if (value === CONTINUE_VALUE) {\n if (selectedDomains.length > 0) {\n setStep(\"build\");\n }\n return;\n }\n\n // Toggle domain selection\n toggleDomain(value);\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>Select domains to configure:</Text>\n <Text dimColor>Select one or more domains, then continue</Text>\n <Box marginTop={1}>\n <Select options={options} onChange={handleSelect} />\n </Box>\n {selectedDomains.length > 0 ? (\n <Box marginTop={1}>\n <Text>\n Selected: <Text color=\"cyan\">{selectedDomains.join(\", \")}</Text>\n </Text>\n </Box>\n ) : (\n <Box marginTop={1}>\n <Text color=\"yellow\">Please select at least one domain</Text>\n </Box>\n )}\n <Box marginTop={1}>\n <Text dimColor>\n {\"\\u2191\"}/{\"\\u2193\"} navigate ENTER toggle/select ESC back\n </Text>\n </Box>\n </Box>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const StepStack: React.FC<StepStackProps> = ({ matrix }) => {\n const { approach } = useWizardStore();\n\n if (approach === \"stack\") {\n return <StackSelection matrix={matrix} />;\n }\n\n // approach === \"scratch\"\n return <DomainSelection />;\n};\n"],"mappings":";;;;;;;;;AAAA;AAaA,SAAS,KAAK,YAAsB;AACpC,SAAS,cAAc;AA8DjB,cASE,YATF;AAtDN,IAAM,aAAa;AACnB,IAAM,iBAAiB;AAGvB,IAAM,oBAAoB;AAAA,EACxB,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,4BAA4B;AAAA,EACpE,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,4BAA4B;AAAA,EACpE,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,qBAAqB;AAAA,EAC7D,EAAE,IAAI,UAAU,OAAO,UAAU,aAAa,sBAAsB;AACtE;AAkBA,IAAM,iBAAgD,CAAC,EAAE,OAAO,MAAM;AACpE,QAAM,EAAE,aAAa,SAAS,OAAO,IAAI,eAAe;AAGxD,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,YAAY,OAAO,cAAc;AAAA,IAC1C,GAAG,OAAO,gBAAgB,IAAI,CAAC,WAAW;AAAA,MACxC,OAAO,MAAM;AAAA,MACb,OAAO,GAAG,MAAM,IAAI,MAAM,MAAM,WAAW;AAAA,IAC7C,EAAE;AAAA,EACJ;AAEA,QAAM,eAAe,CAAC,UAAkB;AACtC,QAAI,UAAU,YAAY;AACxB,aAAO;AACP;AAAA,IACF;AAEA,UAAM,QAAQ,OAAO,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK;AAC/D,QAAI,OAAO;AACT,kBAAY,MAAM,EAAE;AACpB,cAAQ,eAAe;AAAA,IACzB;AAAA,EACF;AAEA,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,wBAAC,QAAK,MAAI,MAAC,0CAA4B;AAAA,IACvC,oBAAC,OAAI,WAAW,GACd;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU;AAAA,QACV,oBAAoB,QAAQ;AAAA;AAAA,IAC9B,GACF;AAAA,IACA,oBAAC,OAAI,WAAW,GACd,+BAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MAAS;AAAA,MAAE;AAAA,MAAS;AAAA,OACvB,GACF;AAAA,KACF;AAEJ;AAMA,IAAM,kBAA4B,MAAM;AACtC,QAAM,EAAE,iBAAiB,cAAc,SAAS,OAAO,IAAI,eAAe;AAG1E,QAAM,gBAAgB,kBAAkB,IAAI,CAAC,WAAW;AACtD,UAAM,aAAa,gBAAgB,SAAS,OAAO,EAAE;AACrD,UAAM,WAAW,aAAa,aAAa;AAC3C,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd,OAAO,GAAG,QAAQ,IAAI,OAAO,KAAK,MAAM,OAAO,WAAW;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,YAAY,OAAO,cAAc;AAAA,IAC1C,GAAG;AAAA,IACH,GAAI,gBAAgB,SAAS,IACzB;AAAA,MACE;AAAA,QACE,OAAO;AAAA,QACP,OAAO,wBAAwB,gBAAgB,MAAM;AAAA,MACvD;AAAA,IACF,IACA,CAAC;AAAA,EACP;AAEA,QAAM,eAAe,CAAC,UAAkB;AACtC,QAAI,UAAU,YAAY;AACxB,aAAO;AACP;AAAA,IACF;AAEA,QAAI,UAAU,gBAAgB;AAC5B,UAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAQ,OAAO;AAAA,MACjB;AACA;AAAA,IACF;AAGA,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,wBAAC,QAAK,MAAI,MAAC,0CAA4B;AAAA,IACvC,oBAAC,QAAK,UAAQ,MAAC,uDAAyC;AAAA,IACxD,oBAAC,OAAI,WAAW,GACd,8BAAC,UAAO,SAAkB,UAAU,cAAc,GACpD;AAAA,IACC,gBAAgB,SAAS,IACxB,oBAAC,OAAI,WAAW,GACd,+BAAC,QAAK;AAAA;AAAA,MACM,oBAAC,QAAK,OAAM,QAAQ,0BAAgB,KAAK,IAAI,GAAE;AAAA,OAC3D,GACF,IAEA,oBAAC,OAAI,WAAW,GACd,8BAAC,QAAK,OAAM,UAAS,+CAAiC,GACxD;AAAA,IAEF,oBAAC,OAAI,WAAW,GACd,+BAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MAAS;AAAA,MAAE;AAAA,MAAS;AAAA,OACvB,GACF;AAAA,KACF;AAEJ;AAMO,IAAM,YAAsC,CAAC,EAAE,OAAO,MAAM;AACjE,QAAM,EAAE,SAAS,IAAI,eAAe;AAEpC,MAAI,aAAa,SAAS;AACxB,WAAO,oBAAC,kBAAe,QAAgB;AAAA,EACzC;AAGA,SAAO,oBAAC,mBAAgB;AAC1B;","names":[]}
|
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
verbose
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3U3R4NCG.js";
|
|
5
5
|
import {
|
|
6
6
|
fileExists,
|
|
7
7
|
readFile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TKFPKEV3.js";
|
|
9
|
+
import {
|
|
10
|
+
CLAUDE_DIR,
|
|
11
|
+
CLAUDE_SRC_DIR
|
|
12
|
+
} from "./chunk-76DWXGQE.js";
|
|
9
13
|
import {
|
|
10
14
|
init_esm_shims
|
|
11
15
|
} from "./chunk-DHET7RCE.js";
|
|
12
16
|
|
|
13
|
-
// src/cli
|
|
17
|
+
// src/cli/lib/project-config.ts
|
|
14
18
|
init_esm_shims();
|
|
15
19
|
import path from "path";
|
|
16
20
|
import { parse as parseYaml } from "yaml";
|
|
17
|
-
var CONFIG_PATH =
|
|
21
|
+
var CONFIG_PATH = `${CLAUDE_SRC_DIR}/config.yaml`;
|
|
22
|
+
var LEGACY_CONFIG_PATH = `${CLAUDE_DIR}/config.yaml`;
|
|
18
23
|
async function loadProjectConfig(projectDir) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const srcConfigPath = path.join(projectDir, CONFIG_PATH);
|
|
25
|
+
const legacyConfigPath = path.join(projectDir, LEGACY_CONFIG_PATH);
|
|
26
|
+
let configPath = srcConfigPath;
|
|
27
|
+
if (!await fileExists(srcConfigPath)) {
|
|
28
|
+
if (await fileExists(legacyConfigPath)) {
|
|
29
|
+
configPath = legacyConfigPath;
|
|
30
|
+
verbose(`Using legacy config location: ${legacyConfigPath}`);
|
|
31
|
+
} else {
|
|
32
|
+
verbose(
|
|
33
|
+
`Project config not found at ${srcConfigPath} or ${legacyConfigPath}`
|
|
34
|
+
);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
23
37
|
}
|
|
24
38
|
try {
|
|
25
39
|
const content = await readFile(configPath);
|
|
@@ -353,4 +367,4 @@ export {
|
|
|
353
367
|
normalizeStackConfig,
|
|
354
368
|
validateProjectConfig
|
|
355
369
|
};
|
|
356
|
-
//# sourceMappingURL=chunk-
|
|
370
|
+
//# sourceMappingURL=chunk-CJEHB4TB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/project-config.ts"],"sourcesContent":["import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { CLAUDE_DIR, CLAUDE_SRC_DIR } from \"../consts\";\nimport type {\n ProjectConfig,\n StackConfig,\n SkillAssignment,\n SkillEntry,\n AgentSkillConfig,\n ValidationResult,\n CustomAgentConfig,\n} from \"../../types\";\n\nconst CONFIG_PATH = `${CLAUDE_SRC_DIR}/config.yaml`;\nconst LEGACY_CONFIG_PATH = `${CLAUDE_DIR}/config.yaml`;\n\nexport interface LoadedProjectConfig {\n config: ProjectConfig;\n configPath: string;\n /** true if was StackConfig format (legacy) */\n isLegacy: boolean;\n}\n\n/**\n * Load project config from .claude-src/config.yaml (with fallback to .claude/config.yaml)\n */\nexport async function loadProjectConfig(\n projectDir: string,\n): Promise<LoadedProjectConfig | null> {\n // Check .claude-src/config.yaml first (new location)\n const srcConfigPath = path.join(projectDir, CONFIG_PATH);\n // Fall back to .claude/config.yaml (legacy location)\n const legacyConfigPath = path.join(projectDir, LEGACY_CONFIG_PATH);\n\n let configPath = srcConfigPath;\n if (!(await fileExists(srcConfigPath))) {\n if (await fileExists(legacyConfigPath)) {\n configPath = legacyConfigPath;\n verbose(`Using legacy config location: ${legacyConfigPath}`);\n } else {\n verbose(\n `Project config not found at ${srcConfigPath} or ${legacyConfigPath}`,\n );\n return null;\n }\n }\n\n try {\n const content = await readFile(configPath);\n const parsed = parseYaml(content);\n\n if (!parsed || typeof parsed !== \"object\") {\n verbose(`Invalid project config structure at ${configPath}`);\n return null;\n }\n\n // Detect if this is legacy StackConfig format\n const isLegacy = isLegacyStackConfig(parsed);\n\n if (isLegacy) {\n verbose(`Detected legacy StackConfig format at ${configPath}`);\n const normalized = normalizeStackConfig(parsed as StackConfig);\n return {\n config: normalized,\n configPath,\n isLegacy: true,\n };\n }\n\n verbose(`Loaded project config from ${configPath}`);\n return {\n config: parsed as ProjectConfig,\n configPath,\n isLegacy: false,\n };\n } catch (error) {\n verbose(`Failed to parse project config: ${error}`);\n return null;\n }\n}\n\n/**\n * Check if a config object is in legacy StackConfig format.\n * Detection logic:\n * - If version is semver (e.g., \"1.0.0\") -> legacy\n * - If version is \"1\" -> new format\n * - If has 'id' field -> legacy\n * - If has 'created' or 'updated' fields -> legacy\n */\nexport function isLegacyStackConfig(config: unknown): boolean {\n if (!config || typeof config !== \"object\") return false;\n\n const obj = config as Record<string, unknown>;\n\n // If version looks like semver (x.y.z), it's legacy\n if (typeof obj.version === \"string\" && obj.version.includes(\".\")) {\n return true;\n }\n\n // If has legacy-only fields, it's legacy\n if (\n obj.id !== undefined ||\n obj.created !== undefined ||\n obj.updated !== undefined\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Normalize StackConfig to ProjectConfig (for backward compatibility)\n */\nexport function normalizeStackConfig(stackConfig: StackConfig): ProjectConfig {\n const config: ProjectConfig = {\n name: stackConfig.name,\n agents: stackConfig.agents,\n };\n\n // Copy optional fields\n if (stackConfig.description) {\n config.description = stackConfig.description;\n }\n\n if (stackConfig.skills && stackConfig.skills.length > 0) {\n config.skills = stackConfig.skills;\n }\n\n if (stackConfig.agent_skills) {\n // StackConfig agent_skills is always categorized format\n config.agent_skills = stackConfig.agent_skills;\n }\n\n if (stackConfig.hooks) {\n config.hooks = stackConfig.hooks;\n }\n\n if (stackConfig.author) {\n config.author = stackConfig.author;\n }\n\n if (stackConfig.framework) {\n config.framework = stackConfig.framework;\n }\n\n if (stackConfig.philosophy) {\n config.philosophy = stackConfig.philosophy;\n }\n\n if (stackConfig.principles && stackConfig.principles.length > 0) {\n config.principles = stackConfig.principles;\n }\n\n if (stackConfig.tags && stackConfig.tags.length > 0) {\n config.tags = stackConfig.tags;\n }\n\n return config;\n}\n\n/**\n * Validate project config structure.\n * Returns validation result with errors and warnings.\n */\nexport function validateProjectConfig(config: unknown): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!config || typeof config !== \"object\") {\n return { valid: false, errors: [\"Config must be an object\"], warnings: [] };\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: name\n if (!c.name || typeof c.name !== \"string\") {\n errors.push(\"name is required and must be a string\");\n }\n\n // Required: agents (for compilation)\n if (!c.agents || !Array.isArray(c.agents)) {\n errors.push(\"agents is required and must be an array\");\n } else {\n for (const agent of c.agents) {\n if (typeof agent !== \"string\") {\n errors.push(`agents must contain strings, found: ${typeof agent}`);\n }\n }\n }\n\n // Optional: version\n if (c.version !== undefined && c.version !== \"1\") {\n errors.push('version must be \"1\" (or omitted for default)');\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(\"skills must be an array\");\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(skillError);\n }\n }\n }\n }\n\n // Optional: agent_skills\n if (c.agent_skills !== undefined) {\n if (typeof c.agent_skills !== \"object\" || c.agent_skills === null) {\n errors.push(\"agent_skills must be an object\");\n } else {\n for (const [agentName, agentSkills] of Object.entries(c.agent_skills)) {\n const agentSkillsError = validateAgentSkillConfig(\n agentName,\n agentSkills,\n );\n if (agentSkillsError) {\n errors.push(agentSkillsError);\n }\n }\n }\n }\n\n // Optional: preload_patterns\n if (c.preload_patterns !== undefined) {\n if (typeof c.preload_patterns !== \"object\" || c.preload_patterns === null) {\n errors.push(\"preload_patterns must be an object\");\n } else {\n for (const [agentName, patterns] of Object.entries(c.preload_patterns)) {\n if (!Array.isArray(patterns)) {\n errors.push(\n `preload_patterns.${agentName} must be an array of strings`,\n );\n } else {\n for (const pattern of patterns) {\n if (typeof pattern !== \"string\") {\n errors.push(\n `preload_patterns.${agentName} must contain only strings`,\n );\n break;\n }\n }\n }\n }\n }\n }\n\n // Optional: custom_agents\n if (c.custom_agents !== undefined) {\n const customAgentsErrors = validateCustomAgents(c.custom_agents, c.agents);\n errors.push(...customAgentsErrors);\n }\n\n // Warnings for deprecated patterns\n if (c.id !== undefined) {\n warnings.push(\"id field is deprecated in project config\");\n }\n if (c.created !== undefined) {\n warnings.push(\"created field is deprecated in project config\");\n }\n if (c.updated !== undefined) {\n warnings.push(\"updated field is deprecated in project config\");\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/** Maximum number of custom agents allowed per project */\nconst MAX_CUSTOM_AGENTS = 20;\n\n/** Valid model values for custom agents */\nconst VALID_MODELS = [\"sonnet\", \"opus\", \"haiku\", \"inherit\"];\n\n/** Valid permission modes for custom agents */\nconst VALID_PERMISSION_MODES = [\n \"default\",\n \"acceptEdits\",\n \"dontAsk\",\n \"bypassPermissions\",\n \"plan\",\n \"delegate\",\n];\n\n/**\n * Validate custom_agents section of config\n */\nfunction validateCustomAgents(\n customAgents: unknown,\n agents: unknown,\n): string[] {\n const errors: string[] = [];\n\n if (typeof customAgents !== \"object\" || customAgents === null) {\n errors.push(\"custom_agents must be an object\");\n return errors;\n }\n\n const customAgentEntries = Object.entries(customAgents);\n\n // Check maximum limit\n if (customAgentEntries.length > MAX_CUSTOM_AGENTS) {\n errors.push(\n `custom_agents cannot exceed ${MAX_CUSTOM_AGENTS} agents (found ${customAgentEntries.length})`,\n );\n }\n\n // Collect custom agent names for circular reference check\n const customAgentNames = new Set(customAgentEntries.map(([name]) => name));\n\n for (const [agentName, agentConfig] of customAgentEntries) {\n const agentErrors = validateCustomAgentConfig(\n agentName,\n agentConfig,\n customAgentNames,\n );\n errors.push(...agentErrors);\n }\n\n return errors;\n}\n\n/**\n * Validate a single custom agent configuration\n */\nfunction validateCustomAgentConfig(\n agentName: string,\n config: unknown,\n customAgentNames: Set<string>,\n): string[] {\n const errors: string[] = [];\n const prefix = `custom_agents.${agentName}`;\n\n if (typeof config !== \"object\" || config === null) {\n errors.push(`${prefix} must be an object`);\n return errors;\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: title\n if (!c.title || typeof c.title !== \"string\") {\n errors.push(`${prefix}.title is required and must be a string`);\n }\n\n // Required: description\n if (!c.description || typeof c.description !== \"string\") {\n errors.push(`${prefix}.description is required and must be a string`);\n }\n\n // Optional: extends\n if (c.extends !== undefined) {\n if (typeof c.extends !== \"string\") {\n errors.push(`${prefix}.extends must be a string`);\n } else if (customAgentNames.has(c.extends)) {\n // Custom agents cannot extend other custom agents\n errors.push(\n `${prefix}.extends cannot reference another custom agent \"${c.extends}\"`,\n );\n }\n }\n\n // Optional: model\n if (c.model !== undefined) {\n if (typeof c.model !== \"string\" || !VALID_MODELS.includes(c.model)) {\n errors.push(`${prefix}.model must be one of: ${VALID_MODELS.join(\", \")}`);\n }\n }\n\n // Optional: tools\n if (c.tools !== undefined) {\n if (!Array.isArray(c.tools)) {\n errors.push(`${prefix}.tools must be an array`);\n } else {\n for (const tool of c.tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: disallowed_tools\n if (c.disallowed_tools !== undefined) {\n if (!Array.isArray(c.disallowed_tools)) {\n errors.push(`${prefix}.disallowed_tools must be an array`);\n } else {\n for (const tool of c.disallowed_tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.disallowed_tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: permission_mode\n if (c.permission_mode !== undefined) {\n if (\n typeof c.permission_mode !== \"string\" ||\n !VALID_PERMISSION_MODES.includes(c.permission_mode)\n ) {\n errors.push(\n `${prefix}.permission_mode must be one of: ${VALID_PERMISSION_MODES.join(\", \")}`,\n );\n }\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(`${prefix}.skills must be an array`);\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(`${prefix}.skills: ${skillError}`);\n }\n }\n }\n }\n\n // Optional: hooks\n if (c.hooks !== undefined) {\n if (typeof c.hooks !== \"object\" || c.hooks === null) {\n errors.push(`${prefix}.hooks must be an object`);\n }\n // Note: Detailed hook validation could be added later if needed\n }\n\n return errors;\n}\n\n/**\n * Validate a single skill entry\n */\nfunction validateSkillEntry(skill: unknown): string | null {\n if (typeof skill === \"string\") {\n return null; // String skill IDs are valid\n }\n\n if (typeof skill !== \"object\" || skill === null) {\n return \"skills must be strings or objects\";\n }\n\n const s = skill as Record<string, unknown>;\n\n if (!s.id || typeof s.id !== \"string\") {\n return \"skill object must have an id string\";\n }\n\n if (s.local === true && !s.path) {\n return `local skill \"${s.id}\" must have a path`;\n }\n\n if (s.preloaded !== undefined && typeof s.preloaded !== \"boolean\") {\n return `skill \"${s.id}\" preloaded must be a boolean`;\n }\n\n if (s.local !== undefined && typeof s.local !== \"boolean\") {\n return `skill \"${s.id}\" local must be a boolean`;\n }\n\n if (s.path !== undefined && typeof s.path !== \"string\") {\n return `skill \"${s.id}\" path must be a string`;\n }\n\n return null;\n}\n\n/**\n * Validate agent skill config (can be simple list or categorized)\n */\nfunction validateAgentSkillConfig(\n agentName: string,\n agentSkills: unknown,\n): string | null {\n // Check if it's a simple list (array)\n if (Array.isArray(agentSkills)) {\n for (const skill of agentSkills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}: ${skillError}`;\n }\n }\n return null;\n }\n\n // Check if it's categorized (object with array values)\n if (typeof agentSkills === \"object\" && agentSkills !== null) {\n for (const [category, skills] of Object.entries(agentSkills)) {\n if (!Array.isArray(skills)) {\n return `agent_skills.${agentName}.${category} must be an array`;\n }\n for (const skill of skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}.${category}: ${skillError}`;\n }\n }\n }\n return null;\n }\n\n return `agent_skills.${agentName} must be an array or object`;\n}\n\n/**\n * Check if agent_skills value is in simple list format (array)\n */\nexport function isSimpleAgentSkills(value: unknown): value is SkillEntry[] {\n return Array.isArray(value);\n}\n\n/**\n * Normalize a skill entry to SkillAssignment\n */\nexport function normalizeSkillEntry(entry: SkillEntry): SkillAssignment {\n if (typeof entry === \"string\") {\n return { id: entry };\n }\n return entry;\n}\n\n/**\n * Normalize agent_skills to always be categorized format for internal use.\n * Simple lists are placed under an \"uncategorized\" key.\n */\nexport function normalizeAgentSkills(\n agentSkills: Record<string, AgentSkillConfig>,\n): Record<string, Record<string, SkillAssignment[]>> {\n const result: Record<string, Record<string, SkillAssignment[]>> = {};\n\n for (const [agentName, skills] of Object.entries(agentSkills)) {\n if (isSimpleAgentSkills(skills)) {\n // Simple list -> put under \"uncategorized\"\n result[agentName] = {\n uncategorized: skills.map(normalizeSkillEntry),\n };\n } else {\n // Already categorized -> normalize entries\n result[agentName] = {};\n for (const [category, categorySkills] of Object.entries(skills)) {\n result[agentName][category] = categorySkills.map(normalizeSkillEntry);\n }\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -6,34 +6,34 @@ import {
|
|
|
6
6
|
mergeMatrixWithSkills,
|
|
7
7
|
parseFrontmatter,
|
|
8
8
|
resolveAgentConfigToSkills
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-B7CCVP6Q.js";
|
|
10
10
|
import {
|
|
11
11
|
fetchFromSource
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-IMDW5ZUP.js";
|
|
13
13
|
import {
|
|
14
14
|
isLocalSource,
|
|
15
15
|
resolveSource
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
LOCAL_SKILLS_PATH,
|
|
19
|
-
PROJECT_ROOT,
|
|
20
|
-
SKILLS_DIR_PATH,
|
|
21
|
-
SKILLS_MATRIX_PATH
|
|
22
|
-
} from "./chunk-A3J6IAXK.js";
|
|
16
|
+
} from "./chunk-V46GGCCI.js";
|
|
23
17
|
import {
|
|
24
18
|
verbose
|
|
25
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-3U3R4NCG.js";
|
|
26
20
|
import {
|
|
27
21
|
directoryExists,
|
|
28
22
|
fileExists,
|
|
29
23
|
listDirectories,
|
|
30
24
|
readFile
|
|
31
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-TKFPKEV3.js";
|
|
26
|
+
import {
|
|
27
|
+
LOCAL_SKILLS_PATH,
|
|
28
|
+
PROJECT_ROOT,
|
|
29
|
+
SKILLS_DIR_PATH,
|
|
30
|
+
SKILLS_MATRIX_PATH
|
|
31
|
+
} from "./chunk-76DWXGQE.js";
|
|
32
32
|
import {
|
|
33
33
|
init_esm_shims
|
|
34
34
|
} from "./chunk-DHET7RCE.js";
|
|
35
35
|
|
|
36
|
-
// src/cli
|
|
36
|
+
// src/cli/lib/local-skill-loader.ts
|
|
37
37
|
init_esm_shims();
|
|
38
38
|
import { parse as parseYaml } from "yaml";
|
|
39
39
|
import path from "path";
|
|
@@ -112,7 +112,7 @@ async function extractLocalSkill(localSkillsPath, skillDirName) {
|
|
|
112
112
|
return extracted;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// src/cli
|
|
115
|
+
// src/cli/lib/source-loader.ts
|
|
116
116
|
init_esm_shims();
|
|
117
117
|
import path2 from "path";
|
|
118
118
|
async function loadSkillsMatrixFromSource(options = {}) {
|
|
@@ -288,6 +288,7 @@ function mergeLocalSkillsIntoMatrix(matrix, localResult) {
|
|
|
288
288
|
requiredBy: [],
|
|
289
289
|
alternatives: [],
|
|
290
290
|
discourages: [],
|
|
291
|
+
compatibleWith: [],
|
|
291
292
|
requiresSetup: [],
|
|
292
293
|
providesSetupFor: [],
|
|
293
294
|
path: metadata.path,
|
|
@@ -304,4 +305,4 @@ export {
|
|
|
304
305
|
discoverLocalSkills,
|
|
305
306
|
loadSkillsMatrixFromSource
|
|
306
307
|
};
|
|
307
|
-
//# sourceMappingURL=chunk-
|
|
308
|
+
//# sourceMappingURL=chunk-CPZOTVCI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/local-skill-loader.ts","../src/cli/lib/source-loader.ts"],"sourcesContent":["import { parse as parseYaml } from \"yaml\";\nimport path from \"path\";\nimport {\n directoryExists,\n listDirectories,\n fileExists,\n readFile,\n} from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { LOCAL_SKILLS_PATH } from \"../consts\";\nimport { parseFrontmatter } from \"./loader\";\nimport type { ExtractedSkillMetadata } from \"../types-matrix\";\n\nconst LOCAL_CATEGORY = \"local\";\nconst LOCAL_AUTHOR = \"@local\";\n\ninterface LocalRawMetadata {\n cli_name: string;\n cli_description?: string;\n}\n\nexport interface LocalSkillDiscoveryResult {\n skills: ExtractedSkillMetadata[];\n localSkillsPath: string;\n}\n\nexport async function discoverLocalSkills(\n projectDir: string,\n): Promise<LocalSkillDiscoveryResult | null> {\n const localSkillsPath = path.join(projectDir, LOCAL_SKILLS_PATH);\n\n if (!(await directoryExists(localSkillsPath))) {\n verbose(`Local skills directory not found: ${localSkillsPath}`);\n return null;\n }\n\n const skills: ExtractedSkillMetadata[] = [];\n const skillDirs = await listDirectories(localSkillsPath);\n\n for (const skillDirName of skillDirs) {\n const skill = await extractLocalSkill(localSkillsPath, skillDirName);\n if (skill) {\n skills.push(skill);\n }\n }\n\n verbose(`Discovered ${skills.length} local skills from ${localSkillsPath}`);\n\n return {\n skills,\n localSkillsPath,\n };\n}\n\nasync function extractLocalSkill(\n localSkillsPath: string,\n skillDirName: string,\n): Promise<ExtractedSkillMetadata | null> {\n const skillDir = path.join(localSkillsPath, skillDirName);\n const metadataPath = path.join(skillDir, \"metadata.yaml\");\n const skillMdPath = path.join(skillDir, \"SKILL.md\");\n\n if (!(await fileExists(metadataPath))) {\n verbose(`Skipping local skill '${skillDirName}': No metadata.yaml found`);\n return null;\n }\n\n if (!(await fileExists(skillMdPath))) {\n verbose(`Skipping local skill '${skillDirName}': No SKILL.md found`);\n return null;\n }\n\n const metadataContent = await readFile(metadataPath);\n const metadata = parseYaml(metadataContent) as LocalRawMetadata;\n\n if (!metadata.cli_name) {\n verbose(\n `Skipping local skill '${skillDirName}': Missing required 'cli_name' in metadata.yaml`,\n );\n return null;\n }\n\n const skillMdContent = await readFile(skillMdPath);\n const frontmatter = parseFrontmatter(skillMdContent);\n\n if (!frontmatter) {\n verbose(\n `Skipping local skill '${skillDirName}': Invalid SKILL.md frontmatter`,\n );\n return null;\n }\n\n const relativePath = `${LOCAL_SKILLS_PATH}/${skillDirName}/`;\n const skillId = frontmatter.name;\n\n const extracted: ExtractedSkillMetadata = {\n id: skillId,\n directoryPath: skillDirName,\n name: `${metadata.cli_name} ${LOCAL_AUTHOR}`,\n description: metadata.cli_description || frontmatter.description,\n usageGuidance: undefined,\n category: LOCAL_CATEGORY,\n categoryExclusive: false,\n author: LOCAL_AUTHOR,\n tags: [],\n compatibleWith: [],\n conflictsWith: [],\n requires: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: relativePath,\n local: true,\n localPath: relativePath,\n };\n\n verbose(`Extracted local skill: ${skillId}`);\n return extracted;\n}\n","import path from \"path\";\nimport { PROJECT_ROOT, SKILLS_DIR_PATH, SKILLS_MATRIX_PATH } from \"../consts\";\nimport type {\n CategoryDefinition,\n MergedSkillsMatrix,\n ResolvedSkill,\n ResolvedStack,\n} from \"../types-matrix\";\nimport type { Stack } from \"../types-stacks\";\nimport { fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport { isLocalSource, resolveSource, type ResolvedConfig } from \"./config\";\nimport {\n discoverLocalSkills,\n type LocalSkillDiscoveryResult,\n} from \"./local-skill-loader\";\nimport {\n extractAllSkills,\n loadSkillsMatrix,\n mergeMatrixWithSkills,\n} from \"./matrix-loader\";\nimport { fetchFromSource } from \"./source-fetcher\";\nimport { loadStacks, resolveAgentConfigToSkills } from \"./stacks-loader\";\n\nexport interface SourceLoadOptions {\n sourceFlag?: string;\n projectDir?: string;\n forceRefresh?: boolean;\n devMode?: boolean;\n}\n\nexport interface SourceLoadResult {\n matrix: MergedSkillsMatrix;\n sourceConfig: ResolvedConfig;\n sourcePath: string;\n isLocal: boolean;\n marketplace?: string;\n}\n\nexport async function loadSkillsMatrixFromSource(\n options: SourceLoadOptions = {},\n): Promise<SourceLoadResult> {\n const {\n sourceFlag,\n projectDir,\n forceRefresh = false,\n devMode = false,\n } = options;\n\n const sourceConfig = await resolveSource(sourceFlag, projectDir);\n const { source } = sourceConfig;\n\n verbose(`Loading skills from source: ${source}`);\n\n const isLocal = isLocalSource(source) || devMode === true;\n\n let result: SourceLoadResult;\n if (isLocal) {\n result = await loadFromLocal(source, sourceConfig);\n } else {\n result = await loadFromRemote(source, sourceConfig, forceRefresh);\n }\n\n const resolvedProjectDir = projectDir || process.cwd();\n const localSkillsResult = await discoverLocalSkills(resolvedProjectDir);\n\n if (localSkillsResult && localSkillsResult.skills.length > 0) {\n verbose(\n `Found ${localSkillsResult.skills.length} local skill(s) in ${localSkillsResult.localSkillsPath}`,\n );\n result.matrix = mergeLocalSkillsIntoMatrix(\n result.matrix,\n localSkillsResult,\n );\n }\n\n return result;\n}\n\nasync function loadFromLocal(\n source: string,\n sourceConfig: ResolvedConfig,\n): Promise<SourceLoadResult> {\n let skillsPath: string;\n\n if (isLocalSource(source)) {\n skillsPath = path.isAbsolute(source)\n ? source\n : path.resolve(process.cwd(), source);\n } else {\n skillsPath = PROJECT_ROOT;\n }\n\n verbose(`Loading skills from local path: ${skillsPath}`);\n\n // Check if source has its own matrix, otherwise fallback to CLI matrix\n const sourceMatrixPath = path.join(skillsPath, SKILLS_MATRIX_PATH);\n const cliMatrixPath = path.join(PROJECT_ROOT, SKILLS_MATRIX_PATH);\n\n let matrixPath: string;\n if (await fileExists(sourceMatrixPath)) {\n matrixPath = sourceMatrixPath;\n verbose(`Matrix from source: ${matrixPath}`);\n } else {\n matrixPath = cliMatrixPath;\n verbose(`Matrix from CLI (source has no matrix): ${matrixPath}`);\n }\n\n const skillsDir = path.join(skillsPath, SKILLS_DIR_PATH);\n verbose(`Skills from source: ${skillsDir}`);\n\n const matrix = await loadSkillsMatrix(matrixPath);\n const skills = await extractAllSkills(skillsDir);\n const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);\n\n // Load stacks from CLI's config/stacks.yaml (Phase 6: agent-centric config)\n const stacks = await loadStacks(PROJECT_ROOT);\n if (stacks.length > 0) {\n // Phase 7: Skills are defined in stacks (per agent, per subcategory), not in agent YAMLs\n // Use skill_aliases from the matrix to resolve technology aliases to full skill IDs\n mergedMatrix.suggestedStacks = stacks.map((stack) =>\n stackToResolvedStack(stack, mergedMatrix.aliases),\n );\n verbose(`Loaded ${stacks.length} stacks from config/stacks.yaml`);\n }\n\n return {\n matrix: mergedMatrix,\n sourceConfig,\n sourcePath: skillsPath,\n isLocal: true,\n marketplace: sourceConfig.marketplace,\n };\n}\n\nasync function loadFromRemote(\n source: string,\n sourceConfig: ResolvedConfig,\n forceRefresh: boolean,\n): Promise<SourceLoadResult> {\n verbose(`Fetching skills from remote source: ${source}`);\n\n const fetchResult = await fetchFromSource(source, { forceRefresh });\n\n verbose(`Fetched to: ${fetchResult.path}`);\n\n // Check if source has its own matrix, otherwise fallback to CLI matrix\n const sourceMatrixPath = path.join(fetchResult.path, SKILLS_MATRIX_PATH);\n const cliMatrixPath = path.join(PROJECT_ROOT, SKILLS_MATRIX_PATH);\n\n let matrixPath: string;\n if (await fileExists(sourceMatrixPath)) {\n matrixPath = sourceMatrixPath;\n verbose(`Matrix from source: ${matrixPath}`);\n } else {\n matrixPath = cliMatrixPath;\n verbose(`Matrix from CLI (source has no matrix): ${matrixPath}`);\n }\n\n const skillsDir = path.join(fetchResult.path, SKILLS_DIR_PATH);\n verbose(`Skills from source: ${skillsDir}`);\n\n const matrix = await loadSkillsMatrix(matrixPath);\n const skills = await extractAllSkills(skillsDir);\n const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);\n\n // Load stacks from CLI's config/stacks.yaml (Phase 6: agent-centric config)\n const stacks = await loadStacks(PROJECT_ROOT);\n if (stacks.length > 0) {\n // Phase 7: Skills are defined in stacks (per agent, per subcategory), not in agent YAMLs\n // Use skill_aliases from the matrix to resolve technology aliases to full skill IDs\n mergedMatrix.suggestedStacks = stacks.map((stack) =>\n stackToResolvedStack(stack, mergedMatrix.aliases),\n );\n verbose(`Loaded ${stacks.length} stacks from config/stacks.yaml`);\n }\n\n return {\n matrix: mergedMatrix,\n sourceConfig,\n sourcePath: fetchResult.path,\n isLocal: false,\n marketplace: sourceConfig.marketplace,\n };\n}\n\n/**\n * Convert a Stack (from config/stacks.yaml) to ResolvedStack format\n * for compatibility with the wizard.\n *\n * Phase 7: Skills are defined in stacks per agent (subcategory -> technology alias).\n * Uses skill_aliases from the matrix to resolve aliases to full skill IDs.\n */\nfunction stackToResolvedStack(\n stack: Stack,\n skillAliases: Record<string, string>,\n): ResolvedStack {\n // Collect all unique skill IDs from agent configs in this stack\n const allSkillIds: string[] = [];\n const seenSkillIds = new Set<string>();\n\n // stack.agents is Record<string, StackAgentConfig> - iterate over agent IDs\n for (const agentId of Object.keys(stack.agents)) {\n const agentConfig = stack.agents[agentId];\n\n // Resolve this agent's technology selections to skill IDs\n const skillRefs = resolveAgentConfigToSkills(agentConfig, skillAliases);\n\n for (const ref of skillRefs) {\n if (!seenSkillIds.has(ref.id)) {\n seenSkillIds.add(ref.id);\n allSkillIds.push(ref.id);\n }\n }\n }\n\n const agentCount = Object.keys(stack.agents).length;\n verbose(\n `Stack '${stack.id}' has ${allSkillIds.length} skills from ${agentCount} agents`,\n );\n\n return {\n id: stack.id,\n name: stack.name,\n description: stack.description,\n audience: [], // Not used in new format\n skills: {}, // Skills come from stack agent configs, resolved at runtime\n allSkillIds,\n philosophy: stack.philosophy || \"\",\n };\n}\n\nconst LOCAL_CATEGORY_TOP: CategoryDefinition = {\n id: \"local\",\n name: \"Local Skills\",\n description: \"Project-specific skills from .claude/skills/\",\n exclusive: false,\n required: false,\n order: 0,\n};\n\nconst LOCAL_CATEGORY_CUSTOM: CategoryDefinition = {\n id: \"local/custom\",\n name: \"Custom\",\n description: \"Your project-specific skills\",\n exclusive: false,\n required: false,\n order: 0,\n parent: \"local\",\n};\n\nfunction mergeLocalSkillsIntoMatrix(\n matrix: MergedSkillsMatrix,\n localResult: LocalSkillDiscoveryResult,\n): MergedSkillsMatrix {\n if (!matrix.categories[\"local\"]) {\n matrix.categories[\"local\"] = LOCAL_CATEGORY_TOP;\n }\n if (!matrix.categories[\"local/custom\"]) {\n matrix.categories[\"local/custom\"] = LOCAL_CATEGORY_CUSTOM;\n }\n\n for (const metadata of localResult.skills) {\n const resolvedSkill: ResolvedSkill = {\n id: metadata.id,\n alias: undefined,\n name: metadata.name,\n description: metadata.description,\n usageGuidance: metadata.usageGuidance,\n\n category: \"local/custom\",\n categoryExclusive: false,\n tags: metadata.tags ?? [],\n\n author: \"@local\",\n\n conflictsWith: [],\n recommends: [],\n recommendedBy: [],\n requires: [],\n requiredBy: [],\n alternatives: [],\n discourages: [],\n compatibleWith: [],\n\n requiresSetup: [],\n providesSetupFor: [],\n\n path: metadata.path,\n\n local: true,\n localPath: metadata.localPath,\n };\n\n matrix.skills[metadata.id] = resolvedSkill;\n verbose(`Added local skill: ${metadata.id}`);\n }\n\n return matrix;\n}\n"],"mappings":"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","names":["path","path"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_PRESELECTED_SKILLS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-76DWXGQE.js";
|
|
5
5
|
import {
|
|
6
6
|
init_esm_shims
|
|
7
7
|
} from "./chunk-DHET7RCE.js";
|
|
8
8
|
|
|
9
|
-
// src/cli
|
|
9
|
+
// src/cli/stores/wizard-store.ts
|
|
10
10
|
init_esm_shims();
|
|
11
11
|
import { create } from "zustand";
|
|
12
12
|
var createInitialState = () => ({
|
|
@@ -39,6 +39,35 @@ var useWizardStore = create((set, get) => ({
|
|
|
39
39
|
setApproach: (approach) => set({ approach }),
|
|
40
40
|
selectStack: (stackId) => set({ selectedStackId: stackId }),
|
|
41
41
|
setStackAction: (action) => set({ stackAction: action }),
|
|
42
|
+
populateFromStack: (stack, categories) => set(() => {
|
|
43
|
+
const domainSelections = {};
|
|
44
|
+
const domains = /* @__PURE__ */ new Set();
|
|
45
|
+
for (const agentConfig of Object.values(stack.agents)) {
|
|
46
|
+
for (const [subcategoryId, technologyAlias] of Object.entries(
|
|
47
|
+
agentConfig
|
|
48
|
+
)) {
|
|
49
|
+
const category = categories[subcategoryId];
|
|
50
|
+
const domain = category?.domain;
|
|
51
|
+
if (!domain) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
domains.add(domain);
|
|
55
|
+
if (!domainSelections[domain]) {
|
|
56
|
+
domainSelections[domain] = {};
|
|
57
|
+
}
|
|
58
|
+
if (!domainSelections[domain][subcategoryId]) {
|
|
59
|
+
domainSelections[domain][subcategoryId] = [];
|
|
60
|
+
}
|
|
61
|
+
if (!domainSelections[domain][subcategoryId].includes(technologyAlias)) {
|
|
62
|
+
domainSelections[domain][subcategoryId].push(technologyAlias);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
domainSelections,
|
|
68
|
+
selectedDomains: Array.from(domains)
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
42
71
|
toggleDomain: (domain) => set((state) => {
|
|
43
72
|
const isSelected = state.selectedDomains.includes(domain);
|
|
44
73
|
return {
|
|
@@ -155,4 +184,4 @@ var useWizardStore = create((set, get) => ({
|
|
|
155
184
|
export {
|
|
156
185
|
useWizardStore
|
|
157
186
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
187
|
+
//# sourceMappingURL=chunk-D237EVNB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/stores/wizard-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { DEFAULT_PRESELECTED_SKILLS } from \"../consts\";\n\n// Step types for the wizard\nexport type WizardStep =\n | \"approach\" // Choose stack template or build from scratch\n | \"stack\" // Select pre-built stack (if approach=stack) or domains (if approach=scratch)\n | \"stack-options\" // After stack selection: continue defaults or customize\n | \"build\" // CategoryGrid for technology selection\n | \"refine\" // Skill source selection\n | \"confirm\"; // Final confirmation\n\nexport interface WizardState {\n // ─────────────────────────────────────────────────────────────────\n // Current step\n // ─────────────────────────────────────────────────────────────────\n step: WizardStep;\n\n // ─────────────────────────────────────────────────────────────────\n // Flow tracking\n // ─────────────────────────────────────────────────────────────────\n approach: \"stack\" | \"scratch\" | null;\n selectedStackId: string | null;\n stackAction: \"defaults\" | \"customize\" | null; // For stack flow after selection\n\n // ─────────────────────────────────────────────────────────────────\n // Domain selection (scratch flow or customize flow)\n // ─────────────────────────────────────────────────────────────────\n selectedDomains: string[]; // ['web', 'api', 'cli', 'mobile']\n\n // ─────────────────────────────────────────────────────────────────\n // Build step state\n // ─────────────────────────────────────────────────────────────────\n currentDomainIndex: number; // Which domain we're configuring (0-based)\n domainSelections: Record<string, Record<string, string[]>>;\n // e.g., { web: { framework: ['react'], styling: ['scss-modules'] } }\n // Note: array supports multi-select categories\n\n // ─────────────────────────────────────────────────────────────────\n // Grid navigation state\n // ─────────────────────────────────────────────────────────────────\n focusedRow: number;\n focusedCol: number;\n\n // ─────────────────────────────────────────────────────────────────\n // Refine step state\n // ─────────────────────────────────────────────────────────────────\n currentRefineIndex: number; // Which skill we're refining\n skillSources: Record<string, string>; // technology -> selected skill ID\n refineAction: \"all-recommended\" | \"customize\" | null;\n\n // ─────────────────────────────────────────────────────────────────\n // UI state\n // ─────────────────────────────────────────────────────────────────\n showDescriptions: boolean;\n expertMode: boolean;\n\n // ─────────────────────────────────────────────────────────────────\n // Modes\n // ─────────────────────────────────────────────────────────────────\n installMode: \"plugin\" | \"local\";\n\n // ─────────────────────────────────────────────────────────────────\n // Navigation history\n // ─────────────────────────────────────────────────────────────────\n history: WizardStep[];\n\n // ─────────────────────────────────────────────────────────────────\n // Actions\n // ─────────────────────────────────────────────────────────────────\n setStep: (step: WizardStep) => void;\n setApproach: (approach: \"stack\" | \"scratch\") => void;\n selectStack: (stackId: string | null) => void;\n setStackAction: (action: \"defaults\" | \"customize\") => void;\n /** Pre-populate domainSelections from a stack's technology mappings */\n populateFromStack: (\n stack: { agents: Record<string, Record<string, string>> },\n categories: Record<string, { domain?: string }>,\n ) => void;\n toggleDomain: (domain: string) => void;\n setDomainSelection: (\n domain: string,\n subcategory: string,\n technologies: string[],\n ) => void;\n toggleTechnology: (\n domain: string,\n subcategory: string,\n technology: string,\n exclusive: boolean,\n ) => void;\n setCurrentDomainIndex: (index: number) => void;\n nextDomain: () => boolean; // Returns true if moved to next domain, false if at end\n prevDomain: () => boolean; // Returns true if moved to prev domain, false if at start\n setFocus: (row: number, col: number) => void;\n setRefineAction: (action: \"all-recommended\" | \"customize\") => void;\n setSkillSource: (technology: string, skillId: string) => void;\n setCurrentRefineIndex: (index: number) => void;\n toggleShowDescriptions: () => void;\n toggleExpertMode: () => void;\n toggleInstallMode: () => void;\n goBack: () => void;\n reset: () => void;\n\n // ─────────────────────────────────────────────────────────────────\n // Computed getters (derive from state)\n // ─────────────────────────────────────────────────────────────────\n getAllSelectedTechnologies: () => string[];\n getCurrentDomain: () => string | null;\n getSelectedSkills: () => string[]; // All selected skills including preselected\n}\n\nconst createInitialState = () => ({\n step: \"approach\" as WizardStep,\n approach: null as \"stack\" | \"scratch\" | null,\n selectedStackId: null as string | null,\n stackAction: null as \"defaults\" | \"customize\" | null,\n selectedDomains: [] as string[],\n currentDomainIndex: 0,\n domainSelections: {} as Record<string, Record<string, string[]>>,\n focusedRow: 0,\n focusedCol: 0,\n currentRefineIndex: 0,\n skillSources: {} as Record<string, string>,\n refineAction: null as \"all-recommended\" | \"customize\" | null,\n showDescriptions: false,\n expertMode: false,\n installMode: \"local\" as \"plugin\" | \"local\",\n history: [] as WizardStep[],\n});\n\nexport const useWizardStore = create<WizardState>((set, get) => ({\n ...createInitialState(),\n\n setStep: (step) =>\n set((state) => ({\n step,\n history: [...state.history, state.step],\n // Reset focus when changing steps\n focusedRow: 0,\n focusedCol: 0,\n })),\n\n setApproach: (approach) => set({ approach }),\n\n selectStack: (stackId) => set({ selectedStackId: stackId }),\n\n setStackAction: (action) => set({ stackAction: action }),\n\n populateFromStack: (stack, categories) =>\n set(() => {\n const domainSelections: Record<string, Record<string, string[]>> = {};\n const domains = new Set<string>();\n\n // Iterate through all agents in the stack\n for (const agentConfig of Object.values(stack.agents)) {\n // Each agent has subcategory -> technology alias mappings\n for (const [subcategoryId, technologyAlias] of Object.entries(\n agentConfig,\n )) {\n const category = categories[subcategoryId];\n const domain = category?.domain;\n\n if (!domain) {\n // Skip if subcategory doesn't have a domain (top-level categories)\n continue;\n }\n\n domains.add(domain);\n\n // Initialize domain if needed\n if (!domainSelections[domain]) {\n domainSelections[domain] = {};\n }\n\n // Initialize subcategory array if needed\n if (!domainSelections[domain][subcategoryId]) {\n domainSelections[domain][subcategoryId] = [];\n }\n\n // Add technology if not already present\n if (\n !domainSelections[domain][subcategoryId].includes(technologyAlias)\n ) {\n domainSelections[domain][subcategoryId].push(technologyAlias);\n }\n }\n }\n\n return {\n domainSelections,\n selectedDomains: Array.from(domains),\n };\n }),\n\n toggleDomain: (domain) =>\n set((state) => {\n const isSelected = state.selectedDomains.includes(domain);\n return {\n selectedDomains: isSelected\n ? state.selectedDomains.filter((d) => d !== domain)\n : [...state.selectedDomains, domain],\n };\n }),\n\n setDomainSelection: (domain, subcategory, technologies) =>\n set((state) => ({\n domainSelections: {\n ...state.domainSelections,\n [domain]: {\n ...state.domainSelections[domain],\n [subcategory]: technologies,\n },\n },\n })),\n\n toggleTechnology: (domain, subcategory, technology, exclusive) =>\n set((state) => {\n const currentSelections =\n state.domainSelections[domain]?.[subcategory] || [];\n const isSelected = currentSelections.includes(technology);\n\n let newSelections: string[];\n if (exclusive) {\n // For exclusive categories, toggle off if already selected, otherwise select only this one\n newSelections = isSelected ? [] : [technology];\n } else {\n // For multi-select categories, toggle the selection\n newSelections = isSelected\n ? currentSelections.filter((t) => t !== technology)\n : [...currentSelections, technology];\n }\n\n return {\n domainSelections: {\n ...state.domainSelections,\n [domain]: {\n ...state.domainSelections[domain],\n [subcategory]: newSelections,\n },\n },\n };\n }),\n\n setCurrentDomainIndex: (index) =>\n set({ currentDomainIndex: index, focusedRow: 0, focusedCol: 0 }),\n\n nextDomain: () => {\n const state = get();\n if (state.currentDomainIndex < state.selectedDomains.length - 1) {\n set({\n currentDomainIndex: state.currentDomainIndex + 1,\n focusedRow: 0,\n focusedCol: 0,\n });\n return true;\n }\n return false;\n },\n\n prevDomain: () => {\n const state = get();\n if (state.currentDomainIndex > 0) {\n set({\n currentDomainIndex: state.currentDomainIndex - 1,\n focusedRow: 0,\n focusedCol: 0,\n });\n return true;\n }\n return false;\n },\n\n setFocus: (row, col) => set({ focusedRow: row, focusedCol: col }),\n\n setRefineAction: (action) => set({ refineAction: action }),\n\n setSkillSource: (technology, skillId) =>\n set((state) => ({\n skillSources: {\n ...state.skillSources,\n [technology]: skillId,\n },\n })),\n\n setCurrentRefineIndex: (index) => set({ currentRefineIndex: index }),\n\n toggleShowDescriptions: () =>\n set((state) => ({ showDescriptions: !state.showDescriptions })),\n\n toggleExpertMode: () => set((state) => ({ expertMode: !state.expertMode })),\n\n toggleInstallMode: () =>\n set((state) => ({\n installMode: state.installMode === \"plugin\" ? \"local\" : \"plugin\",\n })),\n\n goBack: () =>\n set((state) => {\n const history = [...state.history];\n const previousStep = history.pop();\n return {\n step: previousStep || \"approach\",\n history,\n focusedRow: 0,\n focusedCol: 0,\n };\n }),\n\n reset: () => set(createInitialState()),\n\n // ─────────────────────────────────────────────────────────────────\n // Computed getters\n // ─────────────────────────────────────────────────────────────────\n getAllSelectedTechnologies: () => {\n const state = get();\n const technologies: string[] = [];\n for (const domain of Object.keys(state.domainSelections)) {\n for (const subcategory of Object.keys(state.domainSelections[domain])) {\n technologies.push(...state.domainSelections[domain][subcategory]);\n }\n }\n return technologies;\n },\n\n getCurrentDomain: () => {\n const state = get();\n return state.selectedDomains[state.currentDomainIndex] || null;\n },\n\n getSelectedSkills: () => {\n const state = get();\n // Include preselected methodology skills plus resolved skill sources\n const skillIds: string[] = [...DEFAULT_PRESELECTED_SKILLS];\n for (const skillId of Object.values(state.skillSources)) {\n if (!skillIds.includes(skillId)) {\n skillIds.push(skillId);\n }\n }\n return skillIds;\n },\n}));\n"],"mappings":"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","names":[]}
|