@jakkrichm/create-nexus-devflow 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/branch-context.d.ts +1 -0
- package/dist/lib/branch-context.js +8 -3
- package/dist/lib/branch-context.js.map +1 -1
- package/dist/lib/dashboard-page.d.ts +5 -2
- package/dist/lib/dashboard-page.js +7 -388
- package/dist/lib/dashboard-page.js.map +1 -1
- package/dist/lib/dashboard-snapshot.d.ts +22 -3
- package/dist/lib/dashboard-snapshot.js +154 -3
- package/dist/lib/dashboard-snapshot.js.map +1 -1
- package/dist/lib/delivery-lifecycle-engine.d.ts +1 -1
- package/dist/lib/delivery-lifecycle-engine.js +1 -1
- package/dist/lib/delivery-lifecycle-engine.js.map +1 -1
- package/dist/lib/mcp-server-engine.d.ts +57 -0
- package/dist/lib/mcp-server-engine.js +678 -0
- package/dist/lib/mcp-server-engine.js.map +1 -0
- package/dist/lib/mcp.d.ts +15 -27
- package/dist/lib/mcp.js +20 -646
- package/dist/lib/mcp.js.map +1 -1
- package/dist/lib/project-status-engine.d.ts +110 -0
- package/dist/lib/project-status-engine.js +493 -0
- package/dist/lib/project-status-engine.js.map +1 -0
- package/dist/lib/skill-manager.d.ts +2 -82
- package/dist/lib/skill-manager.js +15 -1168
- package/dist/lib/skill-manager.js.map +1 -1
- package/dist/lib/skill-registry-engine.d.ts +121 -0
- package/dist/lib/skill-registry-engine.js +824 -0
- package/dist/lib/skill-registry-engine.js.map +1 -0
- package/dist/lib/status.d.ts +1 -90
- package/dist/lib/status.js +7 -467
- package/dist/lib/status.js.map +1 -1
- package/dist/lib/studio-view-renderer.d.ts +17 -1
- package/dist/lib/studio-view-renderer.js +855 -3
- package/dist/lib/studio-view-renderer.js.map +1 -1
- package/package.json +1 -1
- package/template/.agents/skills/complete/SKILL.md +14 -13
- package/template/.agents/skills/implement/SKILL.md +36 -2
- package/template/.claude/skills/complete/SKILL.md +14 -13
- package/template/.claude/skills/implement/SKILL.md +36 -2
- package/template/devflow/build-plan.md +5 -4
- package/template/devflow/context/ai-interaction.md +4 -2
|
@@ -1,1186 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { promisify } from "node:util";
|
|
7
|
-
import { loadCoreSkillInventory } from "./core-skill-inventory.js";
|
|
8
|
-
const execFileAsync = promisify(execFile);
|
|
9
|
-
export const DEV_ROLE_SKILLS = Object.freeze([
|
|
10
|
-
"adopt", "audit", "autopilot", "brainstorm", "brief", "browser-tests",
|
|
11
|
-
"bughunter", "check", "ci", "complete", "continuous", "convert-any-to-md",
|
|
12
|
-
"debug", "devflow", "discovery", "doctor", "feature", "fix", "grill",
|
|
13
|
-
"idea", "implement", "onboard", "overview", "prototype", "release",
|
|
14
|
-
"report-html", "rollback", "setup-tests", "status", "test", "try"
|
|
15
|
-
]);
|
|
16
|
-
export const SA_ROLE_SKILLS = Object.freeze([
|
|
17
|
-
"analyze", "audit", "brainstorm", "brief", "bughunter", "convert-any-to-md",
|
|
18
|
-
"devflow", "discovery", "doctor", "grill", "idea", "overview", "prototype",
|
|
19
|
-
"report-html", "status"
|
|
20
|
-
]);
|
|
21
|
-
export const FULL_ROLE_SKILLS = Object.freeze([
|
|
22
|
-
"adopt", "analyze", "audit", "autopilot", "brainstorm", "brief", "browser-tests",
|
|
23
|
-
"bughunter", "check", "ci", "complete", "continuous", "convert-any-to-md",
|
|
24
|
-
"debug", "devflow", "discovery", "doctor", "feature", "fix", "grill",
|
|
25
|
-
"idea", "implement", "onboard", "overview", "prototype", "release",
|
|
26
|
-
"report-html", "rollback", "setup-tests", "status", "test", "try"
|
|
27
|
-
]);
|
|
28
|
-
export function getSkillsForRole(role, allCoreSkills) {
|
|
29
|
-
switch (role) {
|
|
30
|
-
case "sa":
|
|
31
|
-
return SA_ROLE_SKILLS;
|
|
32
|
-
case "full":
|
|
33
|
-
return allCoreSkills || FULL_ROLE_SKILLS;
|
|
34
|
-
case "dev":
|
|
35
|
-
default:
|
|
36
|
-
return DEV_ROLE_SKILLS;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export const KNOWN_SKILL_ALIASES = {
|
|
40
|
-
archify: {
|
|
41
|
-
source: "https://github.com/tt-a1i/archify",
|
|
42
|
-
description: "Interactive technical system architecture, dataflow, and sequence trace diagrams"
|
|
43
|
-
},
|
|
44
|
-
"diagram-design": {
|
|
45
|
-
source: "https://github.com/cathrynlavery/diagram-design",
|
|
46
|
-
description: "39 editorial visual diagram templates (Business, Quadrants, Timelines, Mindmaps, Radar)"
|
|
47
|
-
},
|
|
48
|
-
"9arm-skills": {
|
|
49
|
-
source: "https://github.com/thananon/9arm-skills",
|
|
50
|
-
all: true,
|
|
51
|
-
description: "6 specialized skills (debug-mantra, post-mortem, qwen-agent, scrutinize, management-talk, qwenchance)"
|
|
52
|
-
},
|
|
53
|
-
"9arm": {
|
|
54
|
-
source: "https://github.com/thananon/9arm-skills",
|
|
55
|
-
all: true,
|
|
56
|
-
description: "4 specialized skills (debug-mantra, post-mortem, scrutinize, management-talk)"
|
|
57
|
-
},
|
|
58
|
-
bughunter: {
|
|
59
|
-
source: "https://github.com/elementalsouls/Claude-BugHunter",
|
|
60
|
-
type: "compound-knowledge",
|
|
61
|
-
referencePath: "devflow/.vendor/bughunter",
|
|
62
|
-
description: "[devflow] Offensive security orchestrator & bug hunting guide"
|
|
63
|
-
},
|
|
64
|
-
"matt-pocock": {
|
|
65
|
-
source: "https://github.com/mattpocock/skills",
|
|
66
|
-
name: "matt-pocock",
|
|
67
|
-
type: "compound-knowledge",
|
|
68
|
-
referencePath: "devflow/.vendor/matt-pocock",
|
|
69
|
-
description: "Master Matt Pocock's 6 AI-engineering flows (Getting Started, Main Flow, Shaping, Upkeep, Productivity, Reference)"
|
|
70
|
-
},
|
|
71
|
-
mattpocock: {
|
|
72
|
-
source: "https://github.com/mattpocock/skills",
|
|
73
|
-
name: "matt-pocock",
|
|
74
|
-
type: "compound-knowledge",
|
|
75
|
-
referencePath: "devflow/.vendor/matt-pocock",
|
|
76
|
-
description: "Master Matt Pocock's 6 AI-engineering flows (Getting Started, Main Flow, Shaping, Upkeep, Productivity, Reference)"
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
export const RECOMMENDED_THIRD_PARTY_SKILLS = Object.freeze([
|
|
80
|
-
{
|
|
81
|
-
source: "https://github.com/tt-a1i/archify",
|
|
82
|
-
description: "Interactive technical system architecture, dataflow, and sequence trace diagrams"
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
source: "https://github.com/cathrynlavery/diagram-design",
|
|
86
|
-
description: "39 editorial visual diagram templates (Business, Quadrants, Timelines, Mindmaps, Radar)"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
source: "https://github.com/thananon/9arm-skills",
|
|
90
|
-
all: true,
|
|
91
|
-
description: "4 specialized skills (debug-mantra, post-mortem, scrutinize, management-talk)"
|
|
92
|
-
}
|
|
93
|
-
]);
|
|
94
|
-
const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
95
|
-
const MANIFEST_PATH = path.join(".nexus", "nexus-devflow.json");
|
|
96
|
-
const AGENT_MANIFEST_PATH = "agent-bundle.manifest.json";
|
|
97
|
-
export function parseSkillFrontmatter(content) {
|
|
98
|
-
if (!content.startsWith("---"))
|
|
99
|
-
return {};
|
|
100
|
-
const parts = content.split("---");
|
|
101
|
-
if (parts.length < 3)
|
|
102
|
-
return {};
|
|
103
|
-
const lines = parts[1].split("\n");
|
|
104
|
-
let name;
|
|
105
|
-
let description;
|
|
106
|
-
let version;
|
|
107
|
-
for (const line of lines) {
|
|
108
|
-
const trimmed = line.trim();
|
|
109
|
-
if (trimmed.startsWith("name:")) {
|
|
110
|
-
name = trimmed.replace("name:", "").trim().replace(/^["']|["']$/g, "");
|
|
111
|
-
}
|
|
112
|
-
else if (trimmed.startsWith("description:")) {
|
|
113
|
-
description = trimmed.replace("description:", "").trim().replace(/^["']|["']$/g, "");
|
|
114
|
-
}
|
|
115
|
-
else if (trimmed.startsWith("version:")) {
|
|
116
|
-
version = trimmed.replace("version:", "").trim().replace(/^["']|["']$/g, "");
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// Also check metadata: version block
|
|
120
|
-
const metaMatch = parts[1].match(/version:\s*["']?([^"'\r\n]+)["']?/);
|
|
121
|
-
if (!version && metaMatch) {
|
|
122
|
-
version = metaMatch[1].trim();
|
|
123
|
-
}
|
|
124
|
-
return { name, description, version };
|
|
1
|
+
import { SkillRegistryEngine, DefaultGitRepositoryAdapter, DEV_ROLE_SKILLS, SA_ROLE_SKILLS, FULL_ROLE_SKILLS, getSkillsForRole, KNOWN_SKILL_ALIASES, RECOMMENDED_THIRD_PARTY_SKILLS, parseSkillFrontmatter, discoverSkillsInDirectory, findSkillSourceDirectory } from "./skill-registry-engine.js";
|
|
2
|
+
export { SkillRegistryEngine, DefaultGitRepositoryAdapter, DEV_ROLE_SKILLS, SA_ROLE_SKILLS, FULL_ROLE_SKILLS, getSkillsForRole, KNOWN_SKILL_ALIASES, RECOMMENDED_THIRD_PARTY_SKILLS, parseSkillFrontmatter, discoverSkillsInDirectory, findSkillSourceDirectory };
|
|
3
|
+
function getEngine(projectRoot) {
|
|
4
|
+
return new SkillRegistryEngine(projectRoot);
|
|
125
5
|
}
|
|
126
6
|
export async function readDevflowManifest(projectRoot) {
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const raw = await fs.readFile(fullPath, "utf8");
|
|
130
|
-
return JSON.parse(raw);
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
if (err.code === "ENOENT") {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
throw err;
|
|
137
|
-
}
|
|
7
|
+
return getEngine(projectRoot).readManifest();
|
|
138
8
|
}
|
|
139
9
|
export async function writeDevflowManifest(projectRoot, manifest) {
|
|
140
|
-
|
|
141
|
-
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
142
|
-
await fs.writeFile(fullPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
10
|
+
return getEngine(projectRoot).writeManifest(manifest);
|
|
143
11
|
}
|
|
144
12
|
export async function listInstalledSkills(projectRoot) {
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const inventory = await loadCoreSkillInventory(path.join(projectRoot, AGENT_MANIFEST_PATH));
|
|
148
|
-
coreNameSet = inventory.nameSet;
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// fallback if manifest not found
|
|
152
|
-
}
|
|
153
|
-
const manifest = await readDevflowManifest(projectRoot);
|
|
154
|
-
const recordedThirdParty = Array.isArray(manifest?.thirdPartySkills)
|
|
155
|
-
? manifest.thirdPartySkills
|
|
156
|
-
: [];
|
|
157
|
-
const thirdPartyMap = new Map();
|
|
158
|
-
for (const record of recordedThirdParty) {
|
|
159
|
-
if (record?.name) {
|
|
160
|
-
thirdPartyMap.set(record.name, record);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const agentsDir = path.join(projectRoot, ".agents", "skills");
|
|
164
|
-
const claudeDir = path.join(projectRoot, ".claude", "skills");
|
|
165
|
-
const agentsSkills = new Set();
|
|
166
|
-
const claudeSkills = new Set();
|
|
167
|
-
try {
|
|
168
|
-
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
169
|
-
for (const e of entries) {
|
|
170
|
-
if (e.isDirectory())
|
|
171
|
-
agentsSkills.add(e.name);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
// ignore missing dir
|
|
176
|
-
}
|
|
177
|
-
try {
|
|
178
|
-
const entries = await fs.readdir(claudeDir, { withFileTypes: true });
|
|
179
|
-
for (const e of entries) {
|
|
180
|
-
if (e.isDirectory())
|
|
181
|
-
claudeSkills.add(e.name);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
// ignore missing dir
|
|
186
|
-
}
|
|
187
|
-
const allNames = new Set([...agentsSkills, ...claudeSkills, ...thirdPartyMap.keys()]);
|
|
188
|
-
const coreSkills = [];
|
|
189
|
-
const thirdPartySkills = [];
|
|
190
|
-
for (const name of [...allNames].sort()) {
|
|
191
|
-
const inAgents = agentsSkills.has(name);
|
|
192
|
-
const inClaude = claudeSkills.has(name);
|
|
193
|
-
const adapters = [];
|
|
194
|
-
if (inAgents)
|
|
195
|
-
adapters.push(".agents");
|
|
196
|
-
if (inClaude)
|
|
197
|
-
adapters.push(".claude");
|
|
198
|
-
const skillFilePath = inAgents
|
|
199
|
-
? path.join(agentsDir, name, "SKILL.md")
|
|
200
|
-
: path.join(claudeDir, name, "SKILL.md");
|
|
201
|
-
let description = "";
|
|
202
|
-
let version = "";
|
|
203
|
-
try {
|
|
204
|
-
const content = await fs.readFile(skillFilePath, "utf8");
|
|
205
|
-
const meta = parseSkillFrontmatter(content);
|
|
206
|
-
description = meta.description || "";
|
|
207
|
-
version = meta.version || "";
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
// no readable SKILL.md
|
|
211
|
-
}
|
|
212
|
-
const recorded = thirdPartyMap.get(name);
|
|
213
|
-
const isCore = coreNameSet.has(name);
|
|
214
|
-
let category = "local-extension";
|
|
215
|
-
if (isCore) {
|
|
216
|
-
category = "core";
|
|
217
|
-
}
|
|
218
|
-
else if (recorded) {
|
|
219
|
-
category = "third-party";
|
|
220
|
-
}
|
|
221
|
-
const detail = {
|
|
222
|
-
name,
|
|
223
|
-
category,
|
|
224
|
-
description: description || recorded?.description || "",
|
|
225
|
-
version: version || recorded?.version,
|
|
226
|
-
source: recorded?.source,
|
|
227
|
-
adapters,
|
|
228
|
-
synced: inAgents && inClaude,
|
|
229
|
-
path: inAgents ? `.agents/skills/${name}` : `.claude/skills/${name}`
|
|
230
|
-
};
|
|
231
|
-
if (isCore) {
|
|
232
|
-
coreSkills.push(detail);
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
thirdPartySkills.push(detail);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return {
|
|
239
|
-
coreSkills,
|
|
240
|
-
thirdPartySkills,
|
|
241
|
-
totalCount: coreSkills.length + thirdPartySkills.length
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
export async function discoverSkillsInDirectory(rootDir, maxDepth = 5, currentDepth = 0) {
|
|
245
|
-
const results = [];
|
|
246
|
-
if (currentDepth > maxDepth)
|
|
247
|
-
return results;
|
|
248
|
-
const skillMdPath = path.join(rootDir, "SKILL.md");
|
|
249
|
-
if (fsSync.existsSync(skillMdPath)) {
|
|
250
|
-
try {
|
|
251
|
-
const content = await fs.readFile(skillMdPath, "utf8");
|
|
252
|
-
const meta = parseSkillFrontmatter(content);
|
|
253
|
-
const skillName = meta.name || path.basename(rootDir);
|
|
254
|
-
results.push({
|
|
255
|
-
sourceSkillPath: rootDir,
|
|
256
|
-
skillName,
|
|
257
|
-
meta
|
|
258
|
-
});
|
|
259
|
-
return results;
|
|
260
|
-
}
|
|
261
|
-
catch {
|
|
262
|
-
// ignore read error
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
let entries;
|
|
266
|
-
try {
|
|
267
|
-
entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
268
|
-
}
|
|
269
|
-
catch {
|
|
270
|
-
return results;
|
|
271
|
-
}
|
|
272
|
-
const IGNORED_DIRS = new Set([".git", "node_modules", "dist", ".nexus", "build", "coverage", ".turbo"]);
|
|
273
|
-
for (const entry of entries) {
|
|
274
|
-
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {
|
|
275
|
-
const subDir = path.join(rootDir, entry.name);
|
|
276
|
-
const subResults = await discoverSkillsInDirectory(subDir, maxDepth, currentDepth + 1);
|
|
277
|
-
results.push(...subResults);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return results;
|
|
281
|
-
}
|
|
282
|
-
export async function findSkillSourceDirectory(sourceDir, requestedName) {
|
|
283
|
-
const discovered = await discoverSkillsInDirectory(sourceDir);
|
|
284
|
-
if (discovered.length === 0) {
|
|
285
|
-
throw new Error(`Could not find any valid skill with SKILL.md in source: ${sourceDir}`);
|
|
286
|
-
}
|
|
287
|
-
if (requestedName) {
|
|
288
|
-
const match = discovered.find((s) => s.skillName === requestedName || path.basename(s.sourceSkillPath) === requestedName);
|
|
289
|
-
if (match) {
|
|
290
|
-
return {
|
|
291
|
-
sourceSkillPath: match.sourceSkillPath,
|
|
292
|
-
skillName: requestedName,
|
|
293
|
-
meta: match.meta
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
const available = discovered.map((s) => s.skillName).join(", ");
|
|
297
|
-
throw new Error(`Skill "${requestedName}" not found in source. Available skills (${discovered.length}): ${available}`);
|
|
298
|
-
}
|
|
299
|
-
if (discovered.length === 1) {
|
|
300
|
-
return {
|
|
301
|
-
sourceSkillPath: discovered[0].sourceSkillPath,
|
|
302
|
-
skillName: discovered[0].skillName,
|
|
303
|
-
meta: discovered[0].meta
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
// Check if one matches the sourceDir basename
|
|
307
|
-
const baseName = path.basename(sourceDir);
|
|
308
|
-
const baseMatch = discovered.find((s) => s.skillName === baseName || path.basename(s.sourceSkillPath) === baseName);
|
|
309
|
-
if (baseMatch) {
|
|
310
|
-
return {
|
|
311
|
-
sourceSkillPath: baseMatch.sourceSkillPath,
|
|
312
|
-
skillName: baseMatch.skillName,
|
|
313
|
-
meta: baseMatch.meta
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
const available = discovered.map((s) => s.skillName).join(", ");
|
|
317
|
-
throw new Error(`Multiple skills found in source (${discovered.length} skills: ${available}). Please specify --name <skill-name> or use --all to install all skills.`);
|
|
13
|
+
return getEngine(projectRoot).list();
|
|
318
14
|
}
|
|
319
15
|
export async function installThirdPartySkill(projectRoot, source, options) {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
let compoundName = "bughunter";
|
|
324
|
-
let compoundRefPath = "devflow/.vendor/bughunter";
|
|
325
|
-
if (alias) {
|
|
326
|
-
effectiveSource = alias.source;
|
|
327
|
-
if (alias.type === "compound-knowledge") {
|
|
328
|
-
isCompound = true;
|
|
329
|
-
compoundName = alias.name || source.toLowerCase();
|
|
330
|
-
compoundRefPath = alias.referencePath || path.join("devflow", ".vendor", compoundName);
|
|
331
|
-
}
|
|
332
|
-
if (alias.all && options?.all === undefined) {
|
|
333
|
-
options = { ...options, all: true };
|
|
334
|
-
}
|
|
335
|
-
if (alias.name && !options?.name) {
|
|
336
|
-
options = { ...options, name: alias.name };
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
else if (source.includes("Claude-BugHunter")) {
|
|
340
|
-
isCompound = true;
|
|
341
|
-
compoundName = "bughunter";
|
|
342
|
-
compoundRefPath = "devflow/.vendor/bughunter";
|
|
343
|
-
}
|
|
344
|
-
else if (source.includes("mattpocock/skills") || source.includes("matt-pocock")) {
|
|
345
|
-
isCompound = true;
|
|
346
|
-
compoundName = "matt-pocock";
|
|
347
|
-
compoundRefPath = "devflow/.vendor/matt-pocock";
|
|
348
|
-
}
|
|
349
|
-
const isGitUrl = /^https?:\/\/|^git@|^ssh:\/\/|\.git$/.test(effectiveSource);
|
|
350
|
-
let tempCloneDir = null;
|
|
351
|
-
let sourceDirectory = effectiveSource;
|
|
352
|
-
let coreNameSet = new Set();
|
|
353
|
-
try {
|
|
354
|
-
const inventory = await loadCoreSkillInventory(path.join(projectRoot, AGENT_MANIFEST_PATH));
|
|
355
|
-
coreNameSet = inventory.nameSet;
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
// ignore
|
|
359
|
-
}
|
|
360
|
-
try {
|
|
361
|
-
if (isGitUrl) {
|
|
362
|
-
tempCloneDir = await fs.mkdtemp(path.join(os.tmpdir(), "nexus-skill-"));
|
|
363
|
-
await execFileAsync("git", ["clone", "--depth", "1", effectiveSource, tempCloneDir]);
|
|
364
|
-
sourceDirectory = tempCloneDir;
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
sourceDirectory = path.isAbsolute(effectiveSource) ? effectiveSource : path.resolve(projectRoot, effectiveSource);
|
|
368
|
-
if (!fsSync.existsSync(sourceDirectory)) {
|
|
369
|
-
throw new Error(`Source directory does not exist: ${effectiveSource}`);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const recordedSource = options?.overrideSource || effectiveSource;
|
|
373
|
-
const recordedType = isCompound ? "compound-knowledge" : isGitUrl || options?.overrideSource ? "git" : "local";
|
|
374
|
-
if (isCompound) {
|
|
375
|
-
const targetRefDir = path.join(projectRoot, compoundRefPath);
|
|
376
|
-
await fs.mkdir(targetRefDir, { recursive: true });
|
|
377
|
-
const srcSkills = path.join(sourceDirectory, "skills");
|
|
378
|
-
if (fsSync.existsSync(srcSkills)) {
|
|
379
|
-
await fs.cp(srcSkills, path.join(targetRefDir, "skills"), { recursive: true });
|
|
380
|
-
}
|
|
381
|
-
const srcCommands = path.join(sourceDirectory, "commands");
|
|
382
|
-
if (fsSync.existsSync(srcCommands)) {
|
|
383
|
-
await fs.cp(srcCommands, path.join(targetRefDir, "commands"), { recursive: true });
|
|
384
|
-
}
|
|
385
|
-
const srcReports = path.join(sourceDirectory, "docs", "disclosed-reports");
|
|
386
|
-
if (fsSync.existsSync(srcReports)) {
|
|
387
|
-
await fs.cp(srcReports, path.join(targetRefDir, "disclosed-reports"), { recursive: true });
|
|
388
|
-
}
|
|
389
|
-
const srcDocs = path.join(sourceDirectory, "docs");
|
|
390
|
-
if (fsSync.existsSync(srcDocs)) {
|
|
391
|
-
await fs.cp(srcDocs, path.join(targetRefDir, "docs"), { recursive: true });
|
|
392
|
-
}
|
|
393
|
-
for (const guideFile of [
|
|
394
|
-
"ENGAGEMENTS.md",
|
|
395
|
-
"USAGE.md",
|
|
396
|
-
"README.md",
|
|
397
|
-
"INSTALL.md",
|
|
398
|
-
"CONTEXT.md",
|
|
399
|
-
"AGENTS.md",
|
|
400
|
-
"CLAUDE.md"
|
|
401
|
-
]) {
|
|
402
|
-
const srcFile = path.join(sourceDirectory, guideFile);
|
|
403
|
-
if (fsSync.existsSync(srcFile)) {
|
|
404
|
-
await fs.copyFile(srcFile, path.join(targetRefDir, guideFile));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
const isMatt = compoundName === "matt-pocock";
|
|
408
|
-
const compoundDesc = isMatt
|
|
409
|
-
? "Master Matt Pocock's 6 AI-engineering flows (Getting Started, Main Flow, Shaping, Upkeep, Productivity, Reference)"
|
|
410
|
-
: "Offensive security orchestrator & bug hunting guide";
|
|
411
|
-
const compoundVer = isMatt ? "1.0.0" : "2.0.0";
|
|
412
|
-
// Ensure Master Skill is written in .agents and .claude
|
|
413
|
-
const masterContent = isMatt
|
|
414
|
-
? `---
|
|
415
|
-
name: ${compoundName}
|
|
416
|
-
description: "[devflow] Master Matt Pocock's 6 AI-engineering flows (Getting Started, Main Flow, Shaping, Upkeep, Productivity, Reference) for developing software according to real-world situations, while interactively mentoring the developer. References JIT skills in ${compoundRefPath}/. Use when running /matt-pocock, learning AI workflows, or orchestrating spec-driven development."
|
|
417
|
-
argument-hint: "[{flow, topic, or question}]"
|
|
418
|
-
---
|
|
419
|
-
|
|
420
|
-
# 🧠 ${compoundName} — The 6 Canonical Matt Pocock Flows & Interactive Coaching
|
|
421
|
-
|
|
422
|
-
$ARGUMENTS
|
|
423
|
-
|
|
424
|
-
\`${compoundName}\` brings the full AI-engineering workflow pioneered by **Matt Pocock** (from [aihero.dev](https://www.aihero.dev)) into Nexus-DevFlow.
|
|
425
|
-
|
|
426
|
-
This skill serves two complementary purposes:
|
|
427
|
-
1. **Interactive Coach & Mentor**: Teaches the engineering discipline and mindset behind each flow based on real-world engineering situations.
|
|
428
|
-
2. **Execution & Routing Orchestrator**: Directs, prepares, and dispatches the actual skills located in \`${compoundRefPath}/\`.
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## ⚠️ Pre-Flight Check (Knowledge Base Availability)
|
|
433
|
-
|
|
434
|
-
Before executing any Matt Pocock flow, advice, or skill guidance:
|
|
435
|
-
1. **Check if \`${compoundRefPath}/\` exists in this project using your file inspection tool (\`view_file\` or \`list_dir\`)**.
|
|
436
|
-
2. **If \`${compoundRefPath}/\` is MISSING / NOT INSTALLED**:
|
|
437
|
-
- **DO NOT hallucinate skill instructions or invent fake flows**.
|
|
438
|
-
- Inform the user in their configured communication language (defaulting to Thai per \`devflow/config.json\` and \`AGENTS.md\`):
|
|
439
|
-
- State clearly that the Matt Pocock skill suite (\`${compoundRefPath}/\`) is not yet installed in this project.
|
|
440
|
-
- Provide the exact installation command:
|
|
441
|
-
\`\`\`bash
|
|
442
|
-
npx @jakkrichm/create-nexus-devflow skill add ${compoundName}
|
|
443
|
-
\`\`\`
|
|
444
|
-
- Offer to run the installation command on their behalf.
|
|
445
|
-
- Stop and wait for installation before proceeding.
|
|
446
|
-
3. **If \`${compoundRefPath}/\` is PRESENT**:
|
|
447
|
-
- Proceed with the 6 Canonical Flows, JIT skill references, and interactive coaching below.
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## 🧭 The Golden Rule: "Decisions are Yours, Facts are the Agent's"
|
|
452
|
-
|
|
453
|
-
Matt Pocock's workflow is built upon a strict division of responsibility:
|
|
454
|
-
- **Agent's Role**: Gather codebase facts, inspect primary sources, surface constraints, write deterministic tests, propose candidate options, and draft code.
|
|
455
|
-
- **Developer's Role**: Make architectural decisions, approve testing seams, define domain boundaries, and evaluate business trade-offs.
|
|
456
|
-
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
## 🗺️ The 6 Canonical Flows & Real-World Situations
|
|
460
|
-
|
|
461
|
-
\`\`\`text
|
|
462
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
463
|
-
│ 01. Getting Started : Setup repo once & Router (/setup-matt-pocock-skills, /ask-matt) │
|
|
464
|
-
└──────┬──────────────────────────────────────────────────────────────────────┘
|
|
465
|
-
│
|
|
466
|
-
├─────────────────────────────────────────────────────────────┐
|
|
467
|
-
▼ ▼
|
|
468
|
-
┌──────────────────────────────┐ ┌──────────────────────────────┐
|
|
469
|
-
│ 03. Shaping │ │ 04. Upkeep │
|
|
470
|
-
│ Open questions & fuzzy ideas │ │ Maintenance & bug diagnosis │
|
|
471
|
-
│ • /wayfinder (Decision map) │ │ • /diagnosing-bugs (Red loop)│
|
|
472
|
-
│ • /prototype (Throwaway code)│ │ • /improve-codebase-arch │
|
|
473
|
-
│ • /research (Primary docs) │ │ • /resolving-merge-conflicts │
|
|
474
|
-
└──────┬───────────────────────┘ │ • /triage /wizard │
|
|
475
|
-
│ (Clarity reached) └──────────────┬───────────────┘
|
|
476
|
-
│ │ (Split into issues)
|
|
477
|
-
▼ ▼
|
|
478
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
479
|
-
│ 02. The Main Flow (Idea ➔ Ship Spine) │
|
|
480
|
-
│ 1. /grill-with-docs ➔ 2. /to-spec ➔ 3. /to-tickets ➔ [CLEAR] ➔ 4. /implement ➔ 5. /code-review │
|
|
481
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
482
|
-
▲ ▲
|
|
483
|
-
│ │
|
|
484
|
-
┌──────┴───────────────────────┐ ┌──────────────────────┴───────┐
|
|
485
|
-
│ 05. Productivity (Human Collab) │ 06. Reference (Foundations) │
|
|
486
|
-
│ • /grill-me (Repo-less idea) │ │ • /codebase-design (Seams) │
|
|
487
|
-
│ • /handoff (Session transfer)│ │ • /domain-modeling (Glossary)│
|
|
488
|
-
│ • /to-questionnaire /teach │ │ • /grilling (Interview core) │
|
|
489
|
-
│ • /wait-what /writing-for-agents │ • /tdd (Red-Green-Refactor) │
|
|
490
|
-
└──────────────────────────────┘ └──────────────────────────────┘
|
|
491
|
-
\`\`\`
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
## 01. Getting Started Flow (Initial Configuration & Routing)
|
|
496
|
-
|
|
497
|
-
> **Real-World Situation:**
|
|
498
|
-
> - A freshly cloned or initialized repository where the AI does not yet know the issue tracker, triage labels, or domain context layout.
|
|
499
|
-
> - You have 37 skills available and need an immediate GPS to pick the right flow.
|
|
500
|
-
|
|
501
|
-
### Key Skills:
|
|
502
|
-
1. **\`/setup-matt-pocock-skills\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/setup-matt-pocock-skills/SKILL.md)
|
|
503
|
-
- **Action**: Run once per repo to explore the codebase and populate \`docs/agents/\` (configuring Issue Tracker location like \`.scratch/\`, triage labels, and \`CONTEXT.md\` layout).
|
|
504
|
-
- **Command**: \`/${compoundName} setup\` or \`/setup-matt-pocock-skills\`
|
|
505
|
-
2. **\`/ask-matt\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/ask-matt/SKILL.md)
|
|
506
|
-
- **Action**: Acts as an interactive router. Evaluates your current situation and directs you to the optimal flow.
|
|
507
|
-
- **Command**: \`/${compoundName} ask "<your current situation...>"\`
|
|
508
|
-
|
|
509
|
-
> 💡 **Matt's Tip:** Never begin coding until the agent understands the project's environment. Running setup once establishes unified project awareness for every tool in the repository.
|
|
510
|
-
|
|
511
|
-
---
|
|
512
|
-
|
|
513
|
-
## 02. The Main Flow (The Spine: Idea ➔ Ship)
|
|
514
|
-
|
|
515
|
-
> **Real-World Situation:**
|
|
516
|
-
> A feature or requirement is reasonably understood and needs to be taken from raw idea all the way to tested, verified, and shipped code.
|
|
517
|
-
|
|
518
|
-
### The 5 Progressive Steps:
|
|
519
|
-
1. **\`/grill-with-docs\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/grill-with-docs/SKILL.md)
|
|
520
|
-
- Conduct 2–3 rounds of Socratic grilling to unearth boundaries, assumptions, and edge cases. Retain domain terms in \`CONTEXT.md\` and record hard-to-reverse decisions in \`docs/adr/\`.
|
|
521
|
-
2. **\`/to-spec\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/to-spec/SKILL.md)
|
|
522
|
-
- Synthesize interview results into a Living Spec. Explicitly identify the **Testing Seam** (the highest level of integration to test against) and outline comprehensive User Stories.
|
|
523
|
-
3. **\`/to-tickets\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/to-tickets/SKILL.md)
|
|
524
|
-
- Decompose the spec into thin, end-to-end **Tracer-bullet tickets** with explicit \`Blocked by:\` dependencies (saved under \`.scratch/<feature>/issues/<NN>-<slug>.md\` or an issue tracker).
|
|
525
|
-
4. ⚠️ **Phase Boundary (Context Hygiene)**:
|
|
526
|
-
- **Clear Context (\`/clear\` or fresh session)** before coding! Because each ticket is self-contained with its own acceptance criteria, resetting the window brings the agent into the **Smart Zone (~150k tokens)** where code generation is sharpest.
|
|
527
|
-
5. **\`/implement\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/implement/SKILL.md)
|
|
528
|
-
- Execute tickets sequentially, driven by **\`/tdd\`** (Red ➔ Green ➔ Refactor).
|
|
529
|
-
6. **\`/code-review\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/code-review/SKILL.md)
|
|
530
|
-
- Perform independent two-axis review of the diff: **Spec Axis** (did we build what was asked?) and **Standards Axis** (clean code, deep modules, regression safety) prior to committing.
|
|
531
|
-
|
|
532
|
-
> 💡 **Matt's Tip:** Steps 1–3 must stay in a single unbroken context window so the grilling, spec, and tickets share continuous reasoning. But once tickets exist, *immediately clear context* before implementing.
|
|
533
|
-
|
|
534
|
-
---
|
|
535
|
-
|
|
536
|
-
## 03. Shaping Flow (Open Questions & Fog of War)
|
|
537
|
-
|
|
538
|
-
> **Real-World Situation:**
|
|
539
|
-
> - A massive greenfield initiative or high-uncertainty feature with heavy Fog of War.
|
|
540
|
-
> - UI/UX or state-machine dilemmas that cannot be resolved in conversation alone.
|
|
541
|
-
> - Deep research into official docs or third-party source code needed before committing to a plan.
|
|
542
|
-
|
|
543
|
-
### Key Skills:
|
|
544
|
-
1. **\`/wayfinder\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/wayfinder/SKILL.md)
|
|
545
|
-
- Constructs a **Decision Map** for sprawling projects, resolving fog node by node until an actionable path emerges to hand off to \`/to-spec\`.
|
|
546
|
-
2. **\`/prototype\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/prototype/SKILL.md)
|
|
547
|
-
- Creates throwaway, minimal prototype code (e.g. single HTML file or standalone script) to provide tactile validation of UI or complex state logic. Commit onto a separate prototype branch and discard.
|
|
548
|
-
3. **\`/research\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/research/SKILL.md)
|
|
549
|
-
- Dispatches a background subagent to inspect **Primary Sources** (official documentation, vendor source code) and generates cited summaries for \`/grill-with-docs\`.
|
|
550
|
-
|
|
551
|
-
> 💡 **Matt's Tip:** Prototypes are NOT production code! No unit tests, no real database. Their singular purpose is to answer nagging design questions as quickly as possible.
|
|
552
|
-
|
|
553
|
-
---
|
|
554
|
-
|
|
555
|
-
## 04. Upkeep Flow (Code Health & Maintenance)
|
|
556
|
-
|
|
557
|
-
> **Real-World Situation:**
|
|
558
|
-
> - Complex, intermittent, or hard-to-pinpoint bugs.
|
|
559
|
-
> - Modules that feel bloated, shallow, or tightly coupled.
|
|
560
|
-
> - Git merge conflicts during branch integration.
|
|
561
|
-
> - Incoming raw bug reports and feature requests needing categorization.
|
|
562
|
-
|
|
563
|
-
### Key Skills:
|
|
564
|
-
1. **\`/diagnosing-bugs\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/diagnosing-bugs/SKILL.md)
|
|
565
|
-
- Enforces a **Tight Red Loop**: refuse to propose fixes or theorize until an exact, failing reproduction command is captured. Fix the defect and keep the test as a regression lock.
|
|
566
|
-
2. **\`/improve-codebase-architecture\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/improve-codebase-architecture/SKILL.md)
|
|
567
|
-
- Analyzes codebase boundaries to identify shallow modules or tangled dependencies, outputting a prioritized refactoring plan.
|
|
568
|
-
3. **\`/resolving-merge-conflicts\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/resolving-merge-conflicts/SKILL.md)
|
|
569
|
-
- Resolves conflicts hunk-by-hunk by discovering the underlying **engineering intent** of both branches rather than picking arbitrary lines.
|
|
570
|
-
4. **\`/triage\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/triage/SKILL.md)
|
|
571
|
-
- Triages raw external requests and bugs, applying standard labels (\`needs-info\`, \`ready-for-agent\`, \`wontfix\`).
|
|
572
|
-
5. **\`/wizard\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/wizard/SKILL.md)
|
|
573
|
-
- Generates interactive CLI scripts guiding developers through manual out-of-band setups (cloud provisioning, OAuth tokens, secrets).
|
|
574
|
-
|
|
575
|
-
---
|
|
576
|
-
|
|
577
|
-
## 05. Productivity Skills (Human Collaboration & Communication)
|
|
578
|
-
|
|
579
|
-
> **Real-World Situation:**
|
|
580
|
-
> - Exploring an idea before any repository or directory exists.
|
|
581
|
-
> - Context window nearing saturation, requiring seamless handover to a fresh session.
|
|
582
|
-
> - Missing information is locked inside a teammate's or stakeholder's head.
|
|
583
|
-
> - Explaining complex technical concepts cleanly.
|
|
584
|
-
|
|
585
|
-
### Key Skills:
|
|
586
|
-
1. **\`/grill-me\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/grill-me/SKILL.md)
|
|
587
|
-
- Stateless ideation interview without file writes or repo modifications. Ideal for early brainstorming.
|
|
588
|
-
2. **\`/handoff\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/handoff/SKILL.md)
|
|
589
|
-
- Exports the current session state and open threads into a standalone Markdown file in the OS temporary directory for instant agent pickup.
|
|
590
|
-
3. **\`/to-questionnaire\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/to-questionnaire/SKILL.md)
|
|
591
|
-
- Formulates targeted questionnaires to send to external stakeholders when domain knowledge is missing from code.
|
|
592
|
-
4. **\`/wait-what\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/wait-what/SKILL.md)
|
|
593
|
-
- Instant interruption command when the agent uses jargon or confusing explanations; forces plain-language re-explanation using \`CONTEXT.md\`.
|
|
594
|
-
5. **\`/teach\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/teach/SKILL.md)
|
|
595
|
-
- Teaches programming or architecture concepts interactively using the workspace as a live chalkboard.
|
|
596
|
-
6. **\`/writing-for-agents\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/writing-for-agents/SKILL.md)
|
|
597
|
-
- Guidelines for authoring markdown documentation optimized for AI agent readability and retrieval.
|
|
598
|
-
|
|
599
|
-
---
|
|
600
|
-
|
|
601
|
-
## 06. Reference Skills (Architecture Foundations & Mental Models)
|
|
602
|
-
|
|
603
|
-
> **Real-World Situation:**
|
|
604
|
-
> Establishing foundational engineering standards, domain taxonomies, and testing discipline across the engineering team.
|
|
605
|
-
|
|
606
|
-
### Key Skills:
|
|
607
|
-
1. **\`/codebase-design\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/codebase-design/SKILL.md)
|
|
608
|
-
- Principles of **Deep Modules & Clean Seams**: encapsulating complexity behind concise, stable interfaces.
|
|
609
|
-
2. **\`/domain-modeling\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/domain-modeling/SKILL.md)
|
|
610
|
-
- Refining domain vocabulary, synchronizing \`CONTEXT.md\`, and documenting immutable decisions in Architecture Decision Records (ADRs).
|
|
611
|
-
3. **\`/grilling\`** — [SKILL.md](file:///${compoundRefPath}/skills/productivity/grilling/SKILL.md)
|
|
612
|
-
- Core interview methodology leveraged by \`grill-with-docs\`, \`triage\`, and \`wayfinder\`.
|
|
613
|
-
4. **\`/tdd\`** — [SKILL.md](file:///${compoundRefPath}/skills/engineering/tdd/SKILL.md)
|
|
614
|
-
- The ironclad Red ➔ Green ➔ Refactor discipline with deterministic assertions.
|
|
615
|
-
|
|
616
|
-
---
|
|
617
|
-
|
|
618
|
-
## 🗂️ Just-In-Time (JIT) Reference Knowledge Map
|
|
619
|
-
|
|
620
|
-
Before running any flow, inspect the exact primary skill document in \`${compoundRefPath}/\` using your file reading tool:
|
|
621
|
-
|
|
622
|
-
| Flow Category | Skill Name | Path in \`${compoundRefPath}/\` |
|
|
623
|
-
| :--- | :--- | :--- |
|
|
624
|
-
| **01. Getting Started** | \`setup-matt-pocock-skills\` | \`skills/engineering/setup-matt-pocock-skills/SKILL.md\` |
|
|
625
|
-
| | \`ask-matt\` | \`skills/engineering/ask-matt/SKILL.md\` |
|
|
626
|
-
| **02. The Main Flow** | \`grill-with-docs\` | \`skills/engineering/grill-with-docs/SKILL.md\` |
|
|
627
|
-
| | \`to-spec\` | \`skills/engineering/to-spec/SKILL.md\` |
|
|
628
|
-
| | \`to-tickets\` | \`skills/engineering/to-tickets/SKILL.md\` |
|
|
629
|
-
| | \`implement\` | \`skills/engineering/implement/SKILL.md\` |
|
|
630
|
-
| | \`code-review\` | \`skills/engineering/code-review/SKILL.md\` |
|
|
631
|
-
| **03. Shaping** | \`wayfinder\` | \`skills/engineering/wayfinder/SKILL.md\` |
|
|
632
|
-
| | \`prototype\` | \`skills/engineering/prototype/SKILL.md\` |
|
|
633
|
-
| | \`research\` | \`skills/engineering/research/SKILL.md\` |
|
|
634
|
-
| **04. Upkeep** | \`diagnosing-bugs\` | \`skills/engineering/diagnosing-bugs/SKILL.md\` |
|
|
635
|
-
| | \`improve-codebase-architecture\` | \`skills/engineering/improve-codebase-architecture/SKILL.md\` |
|
|
636
|
-
| | \`resolving-merge-conflicts\` | \`skills/engineering/resolving-merge-conflicts/SKILL.md\` |
|
|
637
|
-
| | \`triage\` | \`skills/engineering/triage/SKILL.md\` |
|
|
638
|
-
| | \`wizard\` | \`skills/engineering/wizard/SKILL.md\` |
|
|
639
|
-
| **05. Productivity** | \`grill-me\` | \`skills/productivity/grill-me/SKILL.md\` |
|
|
640
|
-
| | \`handoff\` | \`skills/productivity/handoff/SKILL.md\` |
|
|
641
|
-
| | \`to-questionnaire\` | \`skills/productivity/to-questionnaire/SKILL.md\` |
|
|
642
|
-
| | \`wait-what\` | \`skills/productivity/wait-what/SKILL.md\` |
|
|
643
|
-
| | \`teach\` | \`skills/productivity/teach/SKILL.md\` |
|
|
644
|
-
| | \`writing-for-agents\` | \`skills/productivity/writing-for-agents/SKILL.md\` |
|
|
645
|
-
| **06. Reference** | \`codebase-design\` | \`skills/engineering/codebase-design/SKILL.md\` |
|
|
646
|
-
| | \`domain-modeling\` | \`skills/engineering/domain-modeling/SKILL.md\` |
|
|
647
|
-
| | \`grilling\` | \`skills/productivity/grilling/SKILL.md\` |
|
|
648
|
-
| | \`tdd\` | \`skills/engineering/tdd/SKILL.md\` |
|
|
649
|
-
|
|
650
|
-
---
|
|
651
|
-
|
|
652
|
-
## 🕹️ CLI & Command Dispatcher
|
|
653
|
-
|
|
654
|
-
Invoke \`${compoundName}\` according to your specific task or question:
|
|
655
|
-
|
|
656
|
-
\`\`\`bash
|
|
657
|
-
# 01 Getting Started
|
|
658
|
-
/${compoundName} setup # Run one-time repo setup in docs/agents/
|
|
659
|
-
/${compoundName} ask "Encountering a bug..." # Consult Matt for flow selection
|
|
660
|
-
|
|
661
|
-
# 02 The Main Flow (Idea -> Ship)
|
|
662
|
-
/${compoundName} grill "Export PDF feature" # Socratic grilling of the requirement
|
|
663
|
-
/${compoundName} spec # Establish Seam and generate Living Spec
|
|
664
|
-
/${compoundName} tickets # Split spec into tracer-bullet tickets
|
|
665
|
-
/${compoundName} run-ticket 01 # TDD implementation of ticket 01
|
|
666
|
-
/${compoundName} review # Two-axis review before git commit
|
|
667
|
-
|
|
668
|
-
# 03 Shaping
|
|
669
|
-
/${compoundName} wayfinder "Massive project" # Resolve high-fog architecture
|
|
670
|
-
/${compoundName} prototype "Test UI state" # Fast throwaway tactile validation
|
|
671
|
-
/${compoundName} research "Library internals"# Deep-dive reading of official docs
|
|
672
|
-
|
|
673
|
-
# 04 Upkeep
|
|
674
|
-
/${compoundName} bug "check command failing" # Tight red loop root-cause diagnosis
|
|
675
|
-
/${compoundName} architecture # Identify shallow modules for refactoring
|
|
676
|
-
/${compoundName} conflict # Semantic merge conflict resolution
|
|
677
|
-
|
|
678
|
-
# 05 Productivity & Learning
|
|
679
|
-
/${compoundName} learn "Explain Smart Zone" # In-depth conceptual coaching
|
|
680
|
-
/${compoundName} handoff "Switching session" # Generate Markdown handoff note
|
|
681
|
-
\`\`\`
|
|
682
|
-
|
|
683
|
-
---
|
|
684
|
-
|
|
685
|
-
## 🌐 Artifact & Communication Language
|
|
686
|
-
|
|
687
|
-
All user-facing communication, guidance, coaching responses, and stage artifacts MUST default to **Thai (\`th\`)** (per \`devflow/config.json\` and \`AGENTS.md\` directive #5 / \`ai-interaction.md\`), while code snippets, CLI commands, file paths, and technical identifiers remain in English.
|
|
688
|
-
`
|
|
689
|
-
: `---
|
|
690
|
-
name: ${compoundName}
|
|
691
|
-
description: "[devflow] Offensive security orchestrator & bug hunting guide. Indexes 83 vulnerability classes, 5-phase methodology (Think, Hunt, Perimeter, Ship), 681 disclosed HackerOne patterns, and JIT reference guides in ${compoundRefPath}/. Use when running /bughunter, performing security reviews, verifying auth/injection risks in /check or /audit, or testing API endpoints for vulnerabilities."
|
|
692
|
-
argument-hint: "[{target, vuln-class, or topic}]"
|
|
693
|
-
---
|
|
694
|
-
|
|
695
|
-
# 🛡️ ${compoundName} - Offensive Security & Vulnerability Assessment Orchestrator
|
|
696
|
-
|
|
697
|
-
$ARGUMENTS
|
|
698
|
-
|
|
699
|
-
\`${compoundName}\` is the master security testing orchestrator in Nexus-DevFlow, bringing the complete power of **Claude-BugHunter** into your development lifecycle with **Zero Token Bloat**.
|
|
700
|
-
|
|
701
|
-
---
|
|
702
|
-
|
|
703
|
-
## ⚠️ Pre-Flight Check (Knowledge Base Availability)
|
|
704
|
-
|
|
705
|
-
Before executing any BugHunter analysis or security test:
|
|
706
|
-
1. **Check if \`${compoundRefPath}/\` exists in this project using your file inspection tool**.
|
|
707
|
-
2. **If \`${compoundRefPath}/\` is MISSING / NOT INSTALLED**:
|
|
708
|
-
- **DO NOT hallucinate payloads or fake security reports**.
|
|
709
|
-
- Inform the user in their configured communication language (per \`devflow/config.json\` and \`AGENTS.md\`):
|
|
710
|
-
- State clearly that the BugHunter knowledge base (83 Vulnerability Classes, Payloads & 681 Disclosed Reports) is not yet downloaded in this project.
|
|
711
|
-
- Provide the exact installation command:
|
|
712
|
-
\`\`\`bash
|
|
713
|
-
npx @jakkrichm/create-nexus-devflow skill add ${compoundName}
|
|
714
|
-
\`\`\`
|
|
715
|
-
- Offer to run the installation command on their behalf.
|
|
716
|
-
- Stop and wait for installation before proceeding.
|
|
717
|
-
3. **If \`${compoundRefPath}/\` is PRESENT**:
|
|
718
|
-
- Proceed with the JIT Knowledge Map and 5-phase testing methodology below.
|
|
719
|
-
|
|
720
|
-
---
|
|
721
|
-
|
|
722
|
-
### 📦 Full Upstream Arsenal in \`${compoundRefPath}/\`:
|
|
723
|
-
- **83 Full Skills** (\`${compoundRefPath}/skills/<skill-name>/SKILL.md\`): Detailed detection patterns, bypass tables, and payloads for all 83 classes.
|
|
724
|
-
- **15 Slash Commands** (\`${compoundRefPath}/commands/<command>.md\`): \`hunt\`, \`recon\`, \`triage\`, \`validate\`, \`chain\`, \`report\`, \`scope\`, \`token-scan\`, \`surface\`, \`autopilot\`, etc.
|
|
725
|
-
- **681 Disclosed HackerOne Reports** (\`${compoundRefPath}/disclosed-reports/<class>.md\`): Real-world vulnerability citations across 24 core classes.
|
|
726
|
-
- **Engagement Scaffolding** (\`${compoundRefPath}/ENGAGEMENTS.md\`): Comprehensive directory and methodology scaffolding for security assessments.
|
|
727
|
-
|
|
728
|
-
---
|
|
729
|
-
|
|
730
|
-
## 🧭 The 4-Layer Architecture & 5-Phase Methodology
|
|
731
|
-
|
|
732
|
-
\`\`\`text
|
|
733
|
-
┌────────────────────────────────────────────────────────────────────────┐
|
|
734
|
-
│ 1. THINK │ bb-methodology, redteam-mindset, 5-phase workflow │
|
|
735
|
-
├───────────────┼────────────────────────────────────────────────────────┤
|
|
736
|
-
│ 2. HUNT │ 58 web app vulnerability classes (IDOR, SSRF, SQLi) │
|
|
737
|
-
├───────────────┼────────────────────────────────────────────────────────┤
|
|
738
|
-
│ 3. PERIMETER │ M365/Entra, Okta, vCenter, Cloud IAM, SSL-VPN │
|
|
739
|
-
├───────────────┼────────────────────────────────────────────────────────┤
|
|
740
|
-
│ 4. SHIP │ 7-Question Gate, VRT-aware triage, H1/Bugcrowd reports │
|
|
741
|
-
└────────────────────────────────────────────────────────────────────────┘
|
|
742
|
-
\`\`\`
|
|
743
|
-
|
|
744
|
-
### The 5-Phase Workflow:
|
|
745
|
-
1. **Recon & Scope**: Fingerprint tech stack, enumerate subdomains/endpoints, define in-scope vs. out-of-scope boundaries (\`skills/web2-recon/\`, \`skills/recon-scope-triage/\`).
|
|
746
|
-
2. **Map & Surface Ranking**: Identify high-value targets (Auth, Payment, GraphQL mutations, File uploads, Webhook receivers) (\`commands/surface.md\`).
|
|
747
|
-
3. **Hunt & Test**: Apply vulnerability-specific bypass tables, edge-case payloads, and condition variations (\`skills/hunt-*/SKILL.md\`).
|
|
748
|
-
4. **Validate**: Apply the **7-Question Gate** (Impact, Pre-conditions, Repro steps, Root cause, Severity score) (\`skills/triage-validation/\`).
|
|
749
|
-
5. **Report / Remediate**: Produce actionable vulnerability proof, remediation advice, or bug bounty reports (\`commands/report.md\`, \`skills/report-writing/\`).
|
|
750
|
-
|
|
751
|
-
---
|
|
752
|
-
|
|
753
|
-
## 🗂️ Just-in-Time (JIT) Reference Knowledge Map
|
|
754
|
-
|
|
755
|
-
Before analyzing or probing any security concern, **ALWAYS read the relevant reference document in \`${compoundRefPath}/\` using your file reading tool (\`view_file\` / \`grep_search\`)**:
|
|
756
|
-
|
|
757
|
-
| Target Category | Specific Skill Path in \`${compoundRefPath}/\` | Disclosed Reports Path |
|
|
758
|
-
| :--- | :--- | :--- |
|
|
759
|
-
| **Index & Overview** | \`${compoundRefPath}/INDEX.md\` | - |
|
|
760
|
-
| **IDOR / BOLA** | \`skills/hunt-idor/SKILL.md\` | \`disclosed-reports/hunt-idor.md\` |
|
|
761
|
-
| **OAuth 2.0 / SSO** | \`skills/hunt-oauth/SKILL.md\` | \`disclosed-reports/hunt-oauth.md\` |
|
|
762
|
-
| **JWT Flaws & Crypto** | \`skills/hunt-jwt-crypto/SKILL.md\` | \`disclosed-reports/hunt-jwt-crypto.md\` |
|
|
763
|
-
| **SSRF (Cloud IMDS)** | \`skills/hunt-ssrf/SKILL.md\` | \`disclosed-reports/hunt-ssrf.md\` |
|
|
764
|
-
| **SQL & NoSQL Injection** | \`skills/hunt-sqli/SKILL.md\`, \`skills/hunt-nosqli/SKILL.md\` | \`disclosed-reports/hunt-sqli.md\` |
|
|
765
|
-
| **XSS & DOM Injection** | \`skills/hunt-xss/SKILL.md\`, \`skills/hunt-dom/SKILL.md\` | \`disclosed-reports/hunt-xss.md\` |
|
|
766
|
-
| **GraphQL & APIs** | \`skills/hunt-graphql/SKILL.md\`, \`skills/hunt-fintech-graphql/\` | \`disclosed-reports/hunt-graphql.md\` |
|
|
767
|
-
| **Next.js / Node.js** | \`skills/hunt-nextjs/SKILL.md\`, \`skills/hunt-nodejs/SKILL.md\` | - |
|
|
768
|
-
| **Cloud IAM & Perimeter** | \`skills/hunt-cloud-misconfig/\`, \`skills/m365-entra-attack/\` | \`disclosed-reports/hunt-cloud-misconfig.md\` |
|
|
769
|
-
| **CI/CD & Kubernetes** | \`skills/hunt-cicd/SKILL.md\`, \`skills/hunt-k8s/SKILL.md\` | - |
|
|
770
|
-
| **LLM & AI Security** | \`skills/hunt-llm-ai/SKILL.md\`, \`skills/hunt-rag-vector/SKILL.md\` | - |
|
|
771
|
-
|
|
772
|
-
---
|
|
773
|
-
|
|
774
|
-
## 🔗 Integration with Nexus-DevFlow Core Lifecycle
|
|
775
|
-
|
|
776
|
-
1. **During \`/check\` (Security & QA Verification)**:
|
|
777
|
-
- When verifying a feature dealing with Auth, Multi-tenancy, or Data Exports, run a BugHunter self-assessment:
|
|
778
|
-
*Read \`${compoundRefPath}/skills/hunt-idor/SKILL.md\` to test for IDOR and token tampering vulnerabilities on the affected endpoint.*
|
|
779
|
-
2. **During \`/audit\` (Code Audit)**:
|
|
780
|
-
- Review code against Fowler smells AND offensive attack vectors simultaneously, logging findings into \`devflow/context/{xxx-slug}/findings.md\`.
|
|
781
|
-
3. **During \`/debug\` (Security Incident Investigation)**:
|
|
782
|
-
- Trace vulnerability root causes using the 681 disclosed report patterns in \`disclosed-reports/\`.
|
|
783
|
-
|
|
784
|
-
---
|
|
785
|
-
|
|
786
|
-
## 🌐 Artifact & Communication Language
|
|
787
|
-
|
|
788
|
-
All generated reports, findings recorded in \`devflow/context/{xxx-slug}/findings.md\`, and user communication MUST follow the project's central configuration in \`devflow/config.json\` (and \`AGENTS.md\` directive #5 / \`ai-interaction.md\`, defaulting to Thai \`th\`), while code snippets, CVE IDs, HTTP payloads, and technical identifiers remain in English.
|
|
789
|
-
|
|
790
|
-
---
|
|
791
|
-
|
|
792
|
-
## 🔄 Updating Knowledge Base
|
|
793
|
-
|
|
794
|
-
To update all 83 skills, 15 commands, and report patterns to the latest upstream version:
|
|
795
|
-
\`\`\`bash
|
|
796
|
-
npx @jakkrichm/create-nexus-devflow skill update ${compoundName}
|
|
797
|
-
\`\`\`
|
|
798
|
-
`;
|
|
799
|
-
const agentTarget = path.join(projectRoot, ".agents", "skills", compoundName, "SKILL.md");
|
|
800
|
-
const claudeTarget = path.join(projectRoot, ".claude", "skills", compoundName, "SKILL.md");
|
|
801
|
-
await fs.mkdir(path.dirname(agentTarget), { recursive: true });
|
|
802
|
-
await fs.mkdir(path.dirname(claudeTarget), { recursive: true });
|
|
803
|
-
if (!fsSync.existsSync(agentTarget) || options?.force) {
|
|
804
|
-
await fs.writeFile(agentTarget, masterContent, "utf8");
|
|
805
|
-
}
|
|
806
|
-
if (!fsSync.existsSync(claudeTarget) || options?.force) {
|
|
807
|
-
await fs.writeFile(claudeTarget, masterContent, "utf8");
|
|
808
|
-
}
|
|
809
|
-
// Update manifest
|
|
810
|
-
const manifest = (await readDevflowManifest(projectRoot)) || {
|
|
811
|
-
schemaVersion: 1,
|
|
812
|
-
name: "nexus-devflow",
|
|
813
|
-
package: "@jakkrichm/create-nexus-devflow",
|
|
814
|
-
version: "2.9.5"
|
|
815
|
-
};
|
|
816
|
-
const existingThirdParty = Array.isArray(manifest.thirdPartySkills)
|
|
817
|
-
? manifest.thirdPartySkills
|
|
818
|
-
: [];
|
|
819
|
-
const filtered = existingThirdParty.filter((s) => s.name !== compoundName);
|
|
820
|
-
filtered.push({
|
|
821
|
-
name: compoundName,
|
|
822
|
-
source: recordedSource,
|
|
823
|
-
version: compoundVer,
|
|
824
|
-
description: compoundDesc,
|
|
825
|
-
installedAt: new Date().toISOString(),
|
|
826
|
-
type: "compound-knowledge",
|
|
827
|
-
referencePath: compoundRefPath
|
|
828
|
-
});
|
|
829
|
-
manifest.thirdPartySkills = filtered;
|
|
830
|
-
await writeDevflowManifest(projectRoot, manifest);
|
|
831
|
-
return {
|
|
832
|
-
name: compoundName,
|
|
833
|
-
category: "third-party",
|
|
834
|
-
description: compoundDesc,
|
|
835
|
-
version: compoundVer,
|
|
836
|
-
source: recordedSource,
|
|
837
|
-
adapters: [".agents", ".claude"],
|
|
838
|
-
synced: true,
|
|
839
|
-
path: `.agents/skills/${compoundName}`
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
if (options?.all) {
|
|
843
|
-
const discovered = await discoverSkillsInDirectory(sourceDirectory);
|
|
844
|
-
if (discovered.length === 0) {
|
|
845
|
-
throw new Error(`Could not find any valid skill with SKILL.md in source: ${source}`);
|
|
846
|
-
}
|
|
847
|
-
const installedList = [];
|
|
848
|
-
const manifest = (await readDevflowManifest(projectRoot)) || {
|
|
849
|
-
schemaVersion: 1,
|
|
850
|
-
name: "nexus-devflow",
|
|
851
|
-
package: "@jakkrichm/create-nexus-devflow",
|
|
852
|
-
version: "2.9.3"
|
|
853
|
-
};
|
|
854
|
-
const existingThirdParty = Array.isArray(manifest.thirdPartySkills)
|
|
855
|
-
? manifest.thirdPartySkills
|
|
856
|
-
: [];
|
|
857
|
-
let updatedThirdParty = [...existingThirdParty];
|
|
858
|
-
for (const skill of discovered) {
|
|
859
|
-
const skillName = skill.skillName;
|
|
860
|
-
if (!SKILL_NAME_PATTERN.test(skillName)) {
|
|
861
|
-
continue; // skip invalid names in batch mode
|
|
862
|
-
}
|
|
863
|
-
if (coreNameSet.has(skillName) && !options?.force) {
|
|
864
|
-
continue; // skip core collisions in batch mode
|
|
865
|
-
}
|
|
866
|
-
const targetAgentsSkillDir = path.join(projectRoot, ".agents", "skills", skillName);
|
|
867
|
-
const targetClaudeSkillDir = path.join(projectRoot, ".claude", "skills", skillName);
|
|
868
|
-
await fs.rm(targetAgentsSkillDir, { recursive: true, force: true });
|
|
869
|
-
await fs.rm(targetClaudeSkillDir, { recursive: true, force: true });
|
|
870
|
-
await fs.mkdir(path.dirname(targetAgentsSkillDir), { recursive: true });
|
|
871
|
-
await fs.mkdir(path.dirname(targetClaudeSkillDir), { recursive: true });
|
|
872
|
-
await fs.cp(skill.sourceSkillPath, targetAgentsSkillDir, { recursive: true });
|
|
873
|
-
await fs.cp(skill.sourceSkillPath, targetClaudeSkillDir, { recursive: true });
|
|
874
|
-
updatedThirdParty = updatedThirdParty.filter((s) => s.name !== skillName);
|
|
875
|
-
updatedThirdParty.push({
|
|
876
|
-
name: skillName,
|
|
877
|
-
source: recordedSource,
|
|
878
|
-
version: skill.meta.version || "1.0.0",
|
|
879
|
-
description: skill.meta.description || "",
|
|
880
|
-
installedAt: new Date().toISOString(),
|
|
881
|
-
type: recordedType
|
|
882
|
-
});
|
|
883
|
-
installedList.push({
|
|
884
|
-
name: skillName,
|
|
885
|
-
category: "third-party",
|
|
886
|
-
description: skill.meta.description || "",
|
|
887
|
-
version: skill.meta.version || "1.0.0",
|
|
888
|
-
source: recordedSource,
|
|
889
|
-
adapters: [".agents", ".claude"],
|
|
890
|
-
synced: true,
|
|
891
|
-
path: `.agents/skills/${skillName}`
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
manifest.thirdPartySkills = updatedThirdParty;
|
|
895
|
-
await writeDevflowManifest(projectRoot, manifest);
|
|
896
|
-
return installedList;
|
|
897
|
-
}
|
|
898
|
-
const { sourceSkillPath, skillName, meta } = await findSkillSourceDirectory(sourceDirectory, options?.name);
|
|
899
|
-
if (!SKILL_NAME_PATTERN.test(skillName)) {
|
|
900
|
-
throw new Error(`Invalid skill name: "${skillName}". Must use kebab-case (e.g. "diagram-design").`);
|
|
901
|
-
}
|
|
902
|
-
if (coreNameSet.has(skillName) && !options?.force) {
|
|
903
|
-
throw new Error(`Cannot install skill with name "${skillName}" as it conflicts with a Core DevFlow Skill.`);
|
|
904
|
-
}
|
|
905
|
-
const targetAgentsSkillDir = path.join(projectRoot, ".agents", "skills", skillName);
|
|
906
|
-
const targetClaudeSkillDir = path.join(projectRoot, ".claude", "skills", skillName);
|
|
907
|
-
await fs.rm(targetAgentsSkillDir, { recursive: true, force: true });
|
|
908
|
-
await fs.rm(targetClaudeSkillDir, { recursive: true, force: true });
|
|
909
|
-
await fs.mkdir(path.dirname(targetAgentsSkillDir), { recursive: true });
|
|
910
|
-
await fs.mkdir(path.dirname(targetClaudeSkillDir), { recursive: true });
|
|
911
|
-
// Copy verbatim to .agents/skills/<name>
|
|
912
|
-
await fs.cp(sourceSkillPath, targetAgentsSkillDir, { recursive: true });
|
|
913
|
-
// Copy verbatim to .claude/skills/<name>
|
|
914
|
-
await fs.cp(sourceSkillPath, targetClaudeSkillDir, { recursive: true });
|
|
915
|
-
// Update .nexus/nexus-devflow.json
|
|
916
|
-
const manifest = (await readDevflowManifest(projectRoot)) || {
|
|
917
|
-
schemaVersion: 1,
|
|
918
|
-
name: "nexus-devflow",
|
|
919
|
-
package: "@jakkrichm/create-nexus-devflow",
|
|
920
|
-
version: "2.9.3"
|
|
921
|
-
};
|
|
922
|
-
const existingThirdParty = Array.isArray(manifest.thirdPartySkills)
|
|
923
|
-
? manifest.thirdPartySkills
|
|
924
|
-
: [];
|
|
925
|
-
const existingRecord = existingThirdParty.find((s) => s.name === skillName);
|
|
926
|
-
const filtered = existingThirdParty.filter((s) => s.name !== skillName);
|
|
927
|
-
filtered.push({
|
|
928
|
-
name: skillName,
|
|
929
|
-
source: recordedSource,
|
|
930
|
-
version: meta.version || existingRecord?.version || "1.0.0",
|
|
931
|
-
description: meta.description || existingRecord?.description || "",
|
|
932
|
-
installedAt: new Date().toISOString(),
|
|
933
|
-
type: existingRecord?.type || recordedType,
|
|
934
|
-
referencePath: existingRecord?.referencePath
|
|
935
|
-
});
|
|
936
|
-
manifest.thirdPartySkills = filtered;
|
|
937
|
-
await writeDevflowManifest(projectRoot, manifest);
|
|
938
|
-
return {
|
|
939
|
-
name: skillName,
|
|
940
|
-
category: "third-party",
|
|
941
|
-
description: meta.description || existingRecord?.description || "",
|
|
942
|
-
version: meta.version || existingRecord?.version || "1.0.0",
|
|
943
|
-
source: recordedSource,
|
|
944
|
-
adapters: [".agents", ".claude"],
|
|
945
|
-
synced: true,
|
|
946
|
-
path: `.agents/skills/${skillName}`
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
finally {
|
|
950
|
-
if (tempCloneDir) {
|
|
951
|
-
try {
|
|
952
|
-
await fs.rm(tempCloneDir, { recursive: true, force: true });
|
|
953
|
-
}
|
|
954
|
-
catch {
|
|
955
|
-
// ignore cleanup error
|
|
956
|
-
}
|
|
957
|
-
}
|
|
16
|
+
const result = await getEngine(projectRoot).install(source, options);
|
|
17
|
+
if (result.details.length === 1 && !options?.all) {
|
|
18
|
+
return result.details[0];
|
|
958
19
|
}
|
|
20
|
+
return result.details;
|
|
959
21
|
}
|
|
960
22
|
export async function removeThirdPartySkill(projectRoot, name) {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
const inventory = await loadCoreSkillInventory(path.join(projectRoot, AGENT_MANIFEST_PATH));
|
|
964
|
-
coreNameSet = inventory.nameSet;
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
// ignore
|
|
968
|
-
}
|
|
969
|
-
if (coreNameSet.has(name)) {
|
|
970
|
-
throw new Error(`Cannot remove Core Skill: "${name}". Core skills are managed by Nexus-DevFlow.`);
|
|
971
|
-
}
|
|
972
|
-
const targetAgentsSkillDir = path.join(projectRoot, ".agents", "skills", name);
|
|
973
|
-
const targetClaudeSkillDir = path.join(projectRoot, ".claude", "skills", name);
|
|
974
|
-
let removedAny = false;
|
|
975
|
-
if (fsSync.existsSync(targetAgentsSkillDir)) {
|
|
976
|
-
await fs.rm(targetAgentsSkillDir, { recursive: true, force: true });
|
|
977
|
-
removedAny = true;
|
|
978
|
-
}
|
|
979
|
-
if (fsSync.existsSync(targetClaudeSkillDir)) {
|
|
980
|
-
await fs.rm(targetClaudeSkillDir, { recursive: true, force: true });
|
|
981
|
-
removedAny = true;
|
|
982
|
-
}
|
|
983
|
-
const manifest = await readDevflowManifest(projectRoot);
|
|
984
|
-
if (manifest && Array.isArray(manifest.thirdPartySkills)) {
|
|
985
|
-
const prevList = manifest.thirdPartySkills;
|
|
986
|
-
const nextList = prevList.filter((s) => s.name !== name);
|
|
987
|
-
if (nextList.length !== prevList.length) {
|
|
988
|
-
manifest.thirdPartySkills = nextList;
|
|
989
|
-
await writeDevflowManifest(projectRoot, manifest);
|
|
990
|
-
removedAny = true;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
return removedAny;
|
|
23
|
+
const res = await getEngine(projectRoot).remove(name);
|
|
24
|
+
return res.removed;
|
|
994
25
|
}
|
|
995
26
|
export async function syncSkills(projectRoot) {
|
|
996
|
-
|
|
997
|
-
const claudeDir = path.join(projectRoot, ".claude", "skills");
|
|
998
|
-
if (!fsSync.existsSync(agentsDir)) {
|
|
999
|
-
return { syncedCount: 0, skills: [] };
|
|
1000
|
-
}
|
|
1001
|
-
await fs.rm(claudeDir, { recursive: true, force: true });
|
|
1002
|
-
await fs.mkdir(claudeDir, { recursive: true });
|
|
1003
|
-
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
1004
|
-
const syncedSkills = [];
|
|
1005
|
-
for (const entry of entries) {
|
|
1006
|
-
if (entry.isDirectory()) {
|
|
1007
|
-
const src = path.join(agentsDir, entry.name);
|
|
1008
|
-
const dst = path.join(claudeDir, entry.name);
|
|
1009
|
-
await fs.cp(src, dst, { recursive: true });
|
|
1010
|
-
syncedSkills.push(entry.name);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
return {
|
|
1014
|
-
syncedCount: syncedSkills.length,
|
|
1015
|
-
skills: syncedSkills
|
|
1016
|
-
};
|
|
27
|
+
return getEngine(projectRoot).sync();
|
|
1017
28
|
}
|
|
1018
29
|
export async function updateThirdPartySkills(projectRoot, targetSkillName, options) {
|
|
1019
|
-
|
|
1020
|
-
const existingThirdParty = Array.isArray(manifest?.thirdPartySkills)
|
|
1021
|
-
? manifest.thirdPartySkills
|
|
1022
|
-
: [];
|
|
1023
|
-
if (existingThirdParty.length === 0) {
|
|
1024
|
-
return { updatedSkills: [], failedSkills: [], totalUpdated: 0 };
|
|
1025
|
-
}
|
|
1026
|
-
let skillsToUpdate = existingThirdParty;
|
|
1027
|
-
if (targetSkillName && targetSkillName !== "--all") {
|
|
1028
|
-
skillsToUpdate = existingThirdParty.filter((s) => s.name === targetSkillName);
|
|
1029
|
-
if (skillsToUpdate.length === 0) {
|
|
1030
|
-
throw new Error(`Skill "${targetSkillName}" is not installed as a third-party skill.`);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
// Group by source to avoid duplicate cloning
|
|
1034
|
-
const sourceToSkillsMap = new Map();
|
|
1035
|
-
for (const skill of skillsToUpdate) {
|
|
1036
|
-
const list = sourceToSkillsMap.get(skill.source) || [];
|
|
1037
|
-
list.push(skill);
|
|
1038
|
-
sourceToSkillsMap.set(skill.source, list);
|
|
1039
|
-
}
|
|
1040
|
-
const updatedSkills = [];
|
|
1041
|
-
const failedSkills = [];
|
|
1042
|
-
for (const [source, skills] of sourceToSkillsMap) {
|
|
1043
|
-
const isGit = /^https?:\/\/|^git@|^ssh:\/\/|\.git$/.test(source);
|
|
1044
|
-
let tempCloneDir = null;
|
|
1045
|
-
try {
|
|
1046
|
-
let sourceDir = source;
|
|
1047
|
-
if (isGit) {
|
|
1048
|
-
tempCloneDir = await fs.mkdtemp(path.join(os.tmpdir(), "nexus-skill-update-"));
|
|
1049
|
-
await execFileAsync("git", ["clone", "--depth", "1", source, tempCloneDir]);
|
|
1050
|
-
sourceDir = tempCloneDir;
|
|
1051
|
-
}
|
|
1052
|
-
for (const skill of skills) {
|
|
1053
|
-
try {
|
|
1054
|
-
if (skill.type === "compound-knowledge") {
|
|
1055
|
-
let directSkillFound = false;
|
|
1056
|
-
try {
|
|
1057
|
-
const detail = (await installThirdPartySkill(projectRoot, sourceDir, {
|
|
1058
|
-
name: skill.name,
|
|
1059
|
-
force: true,
|
|
1060
|
-
overrideSource: isGit ? source : undefined
|
|
1061
|
-
}));
|
|
1062
|
-
updatedSkills.push(detail);
|
|
1063
|
-
directSkillFound = true;
|
|
1064
|
-
}
|
|
1065
|
-
catch {
|
|
1066
|
-
directSkillFound = false;
|
|
1067
|
-
}
|
|
1068
|
-
if (!directSkillFound) {
|
|
1069
|
-
const refRelPath = skill.referencePath || path.join("devflow", ".vendor", skill.name);
|
|
1070
|
-
const targetRefDir = path.join(projectRoot, refRelPath);
|
|
1071
|
-
await fs.mkdir(targetRefDir, { recursive: true });
|
|
1072
|
-
// 1. Sync source skills directory to reference/skills
|
|
1073
|
-
const srcSkills = path.join(sourceDir, "skills");
|
|
1074
|
-
if (fsSync.existsSync(srcSkills)) {
|
|
1075
|
-
await fs.cp(srcSkills, path.join(targetRefDir, "skills"), { recursive: true });
|
|
1076
|
-
}
|
|
1077
|
-
// 2. Sync source commands directory to reference/commands
|
|
1078
|
-
const srcCommands = path.join(sourceDir, "commands");
|
|
1079
|
-
if (fsSync.existsSync(srcCommands)) {
|
|
1080
|
-
await fs.cp(srcCommands, path.join(targetRefDir, "commands"), { recursive: true });
|
|
1081
|
-
}
|
|
1082
|
-
// 3. Sync source disclosed-reports directory
|
|
1083
|
-
const srcReports = path.join(sourceDir, "docs", "disclosed-reports");
|
|
1084
|
-
if (fsSync.existsSync(srcReports)) {
|
|
1085
|
-
await fs.cp(srcReports, path.join(targetRefDir, "disclosed-reports"), { recursive: true });
|
|
1086
|
-
}
|
|
1087
|
-
// 4. Sync source docs directory
|
|
1088
|
-
const srcDocs = path.join(sourceDir, "docs");
|
|
1089
|
-
if (fsSync.existsSync(srcDocs)) {
|
|
1090
|
-
await fs.cp(srcDocs, path.join(targetRefDir, "docs"), { recursive: true });
|
|
1091
|
-
}
|
|
1092
|
-
// 5. Copy notable guide files
|
|
1093
|
-
for (const guideFile of ["ENGAGEMENTS.md", "USAGE.md", "README.md", "INSTALL.md"]) {
|
|
1094
|
-
const srcFile = path.join(sourceDir, guideFile);
|
|
1095
|
-
if (fsSync.existsSync(srcFile)) {
|
|
1096
|
-
await fs.copyFile(srcFile, path.join(targetRefDir, guideFile));
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
let newVersion = skill.version || "1.0.0";
|
|
1100
|
-
try {
|
|
1101
|
-
const pkgPath = path.join(sourceDir, "package.json");
|
|
1102
|
-
if (fsSync.existsSync(pkgPath)) {
|
|
1103
|
-
const pkgRaw = await fs.readFile(pkgPath, "utf8");
|
|
1104
|
-
const pkg = JSON.parse(pkgRaw);
|
|
1105
|
-
if (pkg.version)
|
|
1106
|
-
newVersion = pkg.version;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
catch {
|
|
1110
|
-
// ignore
|
|
1111
|
-
}
|
|
1112
|
-
// Update manifest
|
|
1113
|
-
const currentManifest = (await readDevflowManifest(projectRoot)) || {};
|
|
1114
|
-
const existingList = Array.isArray(currentManifest.thirdPartySkills)
|
|
1115
|
-
? currentManifest.thirdPartySkills
|
|
1116
|
-
: [];
|
|
1117
|
-
const updatedList = existingList.map((s) => {
|
|
1118
|
-
if (s.name === skill.name) {
|
|
1119
|
-
return {
|
|
1120
|
-
...s,
|
|
1121
|
-
version: newVersion,
|
|
1122
|
-
installedAt: new Date().toISOString(),
|
|
1123
|
-
type: "compound-knowledge",
|
|
1124
|
-
referencePath: refRelPath
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
return s;
|
|
1128
|
-
});
|
|
1129
|
-
currentManifest.thirdPartySkills = updatedList;
|
|
1130
|
-
await writeDevflowManifest(projectRoot, currentManifest);
|
|
1131
|
-
updatedSkills.push({
|
|
1132
|
-
name: skill.name,
|
|
1133
|
-
category: "third-party",
|
|
1134
|
-
description: skill.description || "Compound Knowledge Skill",
|
|
1135
|
-
version: newVersion,
|
|
1136
|
-
source: skill.source,
|
|
1137
|
-
adapters: [".agents", ".claude"],
|
|
1138
|
-
synced: true,
|
|
1139
|
-
path: `.agents/skills/${skill.name}`
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else {
|
|
1144
|
-
const detail = (await installThirdPartySkill(projectRoot, sourceDir, {
|
|
1145
|
-
name: skill.name,
|
|
1146
|
-
force: true,
|
|
1147
|
-
overrideSource: isGit ? source : undefined
|
|
1148
|
-
}));
|
|
1149
|
-
updatedSkills.push(detail);
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
catch (err) {
|
|
1153
|
-
failedSkills.push({
|
|
1154
|
-
name: skill.name,
|
|
1155
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
catch (err) {
|
|
1161
|
-
for (const skill of skills) {
|
|
1162
|
-
failedSkills.push({
|
|
1163
|
-
name: skill.name,
|
|
1164
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
finally {
|
|
1169
|
-
if (tempCloneDir) {
|
|
1170
|
-
try {
|
|
1171
|
-
await fs.rm(tempCloneDir, { recursive: true, force: true });
|
|
1172
|
-
}
|
|
1173
|
-
catch {
|
|
1174
|
-
// ignore
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
return {
|
|
1180
|
-
updatedSkills,
|
|
1181
|
-
failedSkills,
|
|
1182
|
-
totalUpdated: updatedSkills.length
|
|
1183
|
-
};
|
|
30
|
+
return getEngine(projectRoot).update(targetSkillName, options);
|
|
1184
31
|
}
|
|
1185
32
|
export async function installRecommendedSkills(projectRoot, options) {
|
|
1186
33
|
const presets = options?.presets || RECOMMENDED_THIRD_PARTY_SKILLS;
|