@jakkrichm/create-nexus-devflow 2.9.0 → 2.9.3
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/bin/create-nexus-devflow.d.ts +6 -2
- package/dist/bin/create-nexus-devflow.js +120 -17
- package/dist/bin/create-nexus-devflow.js.map +1 -1
- package/dist/lib/dashboard-page.d.ts +1 -1
- package/dist/lib/dashboard-page.js +76 -0
- package/dist/lib/dashboard-page.js.map +1 -1
- package/dist/lib/project-config.d.ts +1 -1
- package/dist/lib/project-config.js +4 -1
- package/dist/lib/project-config.js.map +1 -1
- package/dist/lib/run-state.d.ts +39 -0
- package/dist/lib/run-state.js +203 -0
- package/dist/lib/run-state.js.map +1 -0
- package/dist/lib/skill-manager.d.ts +24 -1
- package/dist/lib/skill-manager.js +206 -68
- package/dist/lib/skill-manager.js.map +1 -1
- package/dist/lib/status.d.ts +4 -1
- package/dist/lib/status.js +49 -3
- package/dist/lib/status.js.map +1 -1
- package/dist/lib/webview-studio.js +169 -47
- package/dist/lib/webview-studio.js.map +1 -1
- package/package.json +1 -1
- package/template/.agents/skills/adopt/SKILL.md +4 -0
- package/template/.agents/skills/audit/SKILL.md +4 -0
- package/template/.agents/skills/autopilot/SKILL.md +4 -0
- package/template/.agents/skills/check/SKILL.md +4 -0
- package/template/.agents/skills/ci/SKILL.md +4 -0
- package/template/.agents/skills/complete/SKILL.md +7 -1
- package/template/.agents/skills/continuous/SKILL.md +4 -0
- package/template/.agents/skills/debug/SKILL.md +4 -0
- package/template/.agents/skills/discovery/SKILL.md +8 -4
- package/template/.agents/skills/feature/SKILL.md +4 -0
- package/template/.agents/skills/fix/SKILL.md +4 -0
- package/template/.agents/skills/implement/SKILL.md +4 -0
- package/template/.agents/skills/onboard/SKILL.md +4 -0
- package/template/.agents/skills/overview/SKILL.md +4 -0
- package/template/.agents/skills/prototype/SKILL.md +4 -0
- package/template/.agents/skills/release/SKILL.md +4 -0
- package/template/.agents/skills/rollback/SKILL.md +18 -5
- package/template/.agents/skills/status/SKILL.md +7 -0
- package/template/.agents/skills/tests/SKILL.md +4 -0
- package/template/.claude/skills/adopt/SKILL.md +4 -0
- package/template/.claude/skills/audit/SKILL.md +4 -0
- package/template/.claude/skills/autopilot/SKILL.md +4 -0
- package/template/.claude/skills/check/SKILL.md +4 -0
- package/template/.claude/skills/ci/SKILL.md +4 -0
- package/template/.claude/skills/complete/SKILL.md +7 -1
- package/template/.claude/skills/continuous/SKILL.md +4 -0
- package/template/.claude/skills/debug/SKILL.md +4 -0
- package/template/.claude/skills/discovery/SKILL.md +8 -4
- package/template/.claude/skills/feature/SKILL.md +4 -0
- package/template/.claude/skills/fix/SKILL.md +4 -0
- package/template/.claude/skills/implement/SKILL.md +4 -0
- package/template/.claude/skills/onboard/SKILL.md +4 -0
- package/template/.claude/skills/overview/SKILL.md +4 -0
- package/template/.claude/skills/prototype/SKILL.md +4 -0
- package/template/.claude/skills/release/SKILL.md +4 -0
- package/template/.claude/skills/rollback/SKILL.md +18 -5
- package/template/.claude/skills/status/SKILL.md +7 -0
- package/template/.claude/skills/tests/SKILL.md +4 -0
- package/template/AGENTS.md +24 -5
- package/template/devflow/build-plan.md +16 -0
- package/template/devflow/context/ai-interaction.md +4 -4
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const RUN_STATE_PATH = path.join("devflow", ".state", "run.json");
|
|
4
|
+
const COMMAND_PATTERN = /^[a-z][a-z-]{0,31}$/;
|
|
5
|
+
const RUN_STATUSES = new Set([
|
|
6
|
+
"running",
|
|
7
|
+
"blocked",
|
|
8
|
+
"ready",
|
|
9
|
+
"completed"
|
|
10
|
+
]);
|
|
11
|
+
const RUN_BOUNDARIES = new Set([
|
|
12
|
+
"read-only",
|
|
13
|
+
"reviewed",
|
|
14
|
+
"local-only"
|
|
15
|
+
]);
|
|
16
|
+
const RUN_STALE_AFTER_MS = 60 * 60 * 1000;
|
|
17
|
+
async function readRunState(projectRoot, now = new Date()) {
|
|
18
|
+
const runStatePath = path.join(projectRoot, RUN_STATE_PATH);
|
|
19
|
+
try {
|
|
20
|
+
const stats = await fs.lstat(runStatePath);
|
|
21
|
+
if (stats.isSymbolicLink()) {
|
|
22
|
+
return malformedSummary({
|
|
23
|
+
code: "unsafe_run_state_path",
|
|
24
|
+
message: "Dashboard run state is a symbolic link and was not read."
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (!stats.isFile()) {
|
|
28
|
+
return malformedSummary({
|
|
29
|
+
code: "invalid_run_state_path",
|
|
30
|
+
message: "Dashboard run state path is not a regular file."
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return parseRunState(await fs.readFile(runStatePath, "utf8"), now);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (getErrorCode(error) === "ENOENT") {
|
|
37
|
+
return idleSummary();
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function parseRunState(value, now = new Date()) {
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(value);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return malformedSummary({
|
|
49
|
+
code: "malformed_run_state",
|
|
50
|
+
message: "Dashboard run state is not valid JSON."
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!isRecord(parsed)) {
|
|
54
|
+
return malformedRunState();
|
|
55
|
+
}
|
|
56
|
+
const command = readString(parsed.command);
|
|
57
|
+
const status = readString(parsed.status);
|
|
58
|
+
const summary = readString(parsed.summary);
|
|
59
|
+
const detail = readOptionalString(parsed.detail);
|
|
60
|
+
const boundary = readOptionalString(parsed.boundary);
|
|
61
|
+
const startedAt = readString(parsed.startedAt);
|
|
62
|
+
const updatedAt = readString(parsed.updatedAt);
|
|
63
|
+
const resumeCommand = readOptionalString(parsed.resumeCommand);
|
|
64
|
+
const progress = parseProgress(parsed.progress);
|
|
65
|
+
const feature = parseFeature(parsed.feature);
|
|
66
|
+
if (parsed.schemaVersion !== 1 ||
|
|
67
|
+
!command ||
|
|
68
|
+
!COMMAND_PATTERN.test(command) ||
|
|
69
|
+
!status ||
|
|
70
|
+
!RUN_STATUSES.has(status) ||
|
|
71
|
+
!summary ||
|
|
72
|
+
!startedAt ||
|
|
73
|
+
!updatedAt ||
|
|
74
|
+
!isTimestamp(startedAt) ||
|
|
75
|
+
!isTimestamp(updatedAt) ||
|
|
76
|
+
Date.parse(startedAt) > Date.parse(updatedAt) ||
|
|
77
|
+
(parsed.detail !== undefined && detail === null) ||
|
|
78
|
+
(parsed.boundary !== undefined && (!boundary || !RUN_BOUNDARIES.has(boundary))) ||
|
|
79
|
+
(parsed.resumeCommand !== undefined && resumeCommand === null) ||
|
|
80
|
+
(parsed.progress !== undefined && progress === null) ||
|
|
81
|
+
(parsed.feature !== undefined && feature === null)) {
|
|
82
|
+
return malformedRunState();
|
|
83
|
+
}
|
|
84
|
+
const freshness = status === "running" &&
|
|
85
|
+
now.getTime() - Date.parse(updatedAt) > RUN_STALE_AFTER_MS
|
|
86
|
+
? "stale"
|
|
87
|
+
: "current";
|
|
88
|
+
const warnings = freshness === "stale"
|
|
89
|
+
? [
|
|
90
|
+
{
|
|
91
|
+
code: "stale_run_state",
|
|
92
|
+
message: `Recorded /${command} activity has not updated for over one hour and may have been interrupted.`
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
: [];
|
|
96
|
+
return {
|
|
97
|
+
state: "recorded",
|
|
98
|
+
mode: command === "autopilot"
|
|
99
|
+
? "autopilot"
|
|
100
|
+
: command === "continuous"
|
|
101
|
+
? "continuous"
|
|
102
|
+
: "manual",
|
|
103
|
+
command,
|
|
104
|
+
status,
|
|
105
|
+
freshness,
|
|
106
|
+
summary,
|
|
107
|
+
detail,
|
|
108
|
+
boundary,
|
|
109
|
+
startedAt,
|
|
110
|
+
updatedAt,
|
|
111
|
+
resumeCommand,
|
|
112
|
+
progress,
|
|
113
|
+
feature,
|
|
114
|
+
warnings
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function parseProgress(value) {
|
|
118
|
+
if (value === undefined) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (!isRecord(value)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const label = readString(value.label);
|
|
125
|
+
const current = value.current;
|
|
126
|
+
const total = value.total;
|
|
127
|
+
if (!label ||
|
|
128
|
+
!Number.isInteger(current) ||
|
|
129
|
+
!Number.isInteger(total) ||
|
|
130
|
+
Number(current) < 0 ||
|
|
131
|
+
Number(total) < 1 ||
|
|
132
|
+
Number(current) > Number(total)) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return { label, current: Number(current), total: Number(total) };
|
|
136
|
+
}
|
|
137
|
+
function parseFeature(value) {
|
|
138
|
+
if (value === undefined) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
if (!isRecord(value)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const id = readOptionalString(value.id);
|
|
145
|
+
const title = readString(value.title);
|
|
146
|
+
if (!title || (value.id !== undefined && id === null)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return { id, title };
|
|
150
|
+
}
|
|
151
|
+
function malformedRunState() {
|
|
152
|
+
return malformedSummary({
|
|
153
|
+
code: "malformed_run_state",
|
|
154
|
+
message: "Dashboard run state does not match schema version 1."
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function idleSummary() {
|
|
158
|
+
return {
|
|
159
|
+
state: "idle",
|
|
160
|
+
mode: "manual",
|
|
161
|
+
command: null,
|
|
162
|
+
status: null,
|
|
163
|
+
freshness: null,
|
|
164
|
+
summary: null,
|
|
165
|
+
detail: null,
|
|
166
|
+
boundary: null,
|
|
167
|
+
startedAt: null,
|
|
168
|
+
updatedAt: null,
|
|
169
|
+
resumeCommand: null,
|
|
170
|
+
progress: null,
|
|
171
|
+
feature: null,
|
|
172
|
+
warnings: []
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function malformedSummary(warning) {
|
|
176
|
+
return {
|
|
177
|
+
...idleSummary(),
|
|
178
|
+
state: "malformed",
|
|
179
|
+
warnings: [warning]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function readString(value) {
|
|
183
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
184
|
+
}
|
|
185
|
+
function readOptionalString(value) {
|
|
186
|
+
return value === undefined ? null : readString(value);
|
|
187
|
+
}
|
|
188
|
+
function isTimestamp(value) {
|
|
189
|
+
return !Number.isNaN(Date.parse(value));
|
|
190
|
+
}
|
|
191
|
+
function isRecord(value) {
|
|
192
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
193
|
+
}
|
|
194
|
+
function getErrorCode(error) {
|
|
195
|
+
return typeof error === "object" &&
|
|
196
|
+
error !== null &&
|
|
197
|
+
"code" in error &&
|
|
198
|
+
typeof error.code === "string"
|
|
199
|
+
? error.code
|
|
200
|
+
: undefined;
|
|
201
|
+
}
|
|
202
|
+
export { RUN_STATE_PATH, parseRunState, readRunState };
|
|
203
|
+
//# sourceMappingURL=run-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-state.js","sourceRoot":"","sources":["../../lib/run-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,9 @@ export interface SkillListResult {
|
|
|
23
23
|
}
|
|
24
24
|
export interface InstallSkillOptions {
|
|
25
25
|
name?: string;
|
|
26
|
+
all?: boolean;
|
|
26
27
|
force?: boolean;
|
|
28
|
+
overrideSource?: string;
|
|
27
29
|
}
|
|
28
30
|
export declare function parseSkillFrontmatter(content: string): {
|
|
29
31
|
name?: string;
|
|
@@ -33,6 +35,16 @@ export declare function parseSkillFrontmatter(content: string): {
|
|
|
33
35
|
export declare function readDevflowManifest(projectRoot: string): Promise<Record<string, unknown> | null>;
|
|
34
36
|
export declare function writeDevflowManifest(projectRoot: string, manifest: Record<string, unknown>): Promise<void>;
|
|
35
37
|
export declare function listInstalledSkills(projectRoot: string): Promise<SkillListResult>;
|
|
38
|
+
export interface DiscoveredSkill {
|
|
39
|
+
sourceSkillPath: string;
|
|
40
|
+
skillName: string;
|
|
41
|
+
meta: {
|
|
42
|
+
name?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export declare function discoverSkillsInDirectory(rootDir: string, maxDepth?: number, currentDepth?: number): Promise<DiscoveredSkill[]>;
|
|
36
48
|
export declare function findSkillSourceDirectory(sourceDir: string, requestedName?: string): Promise<{
|
|
37
49
|
sourceSkillPath: string;
|
|
38
50
|
skillName: string;
|
|
@@ -42,9 +54,20 @@ export declare function findSkillSourceDirectory(sourceDir: string, requestedNam
|
|
|
42
54
|
version?: string;
|
|
43
55
|
};
|
|
44
56
|
}>;
|
|
45
|
-
export declare function installThirdPartySkill(projectRoot: string, source: string, options?: InstallSkillOptions): Promise<SkillDetail>;
|
|
57
|
+
export declare function installThirdPartySkill(projectRoot: string, source: string, options?: InstallSkillOptions): Promise<SkillDetail | SkillDetail[]>;
|
|
46
58
|
export declare function removeThirdPartySkill(projectRoot: string, name: string): Promise<boolean>;
|
|
47
59
|
export declare function syncSkills(projectRoot: string): Promise<{
|
|
48
60
|
syncedCount: number;
|
|
49
61
|
skills: string[];
|
|
50
62
|
}>;
|
|
63
|
+
export interface SkillUpdateResult {
|
|
64
|
+
updatedSkills: SkillDetail[];
|
|
65
|
+
failedSkills: Array<{
|
|
66
|
+
name: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
}>;
|
|
69
|
+
totalUpdated: number;
|
|
70
|
+
}
|
|
71
|
+
export declare function updateThirdPartySkills(projectRoot: string, targetSkillName?: string, options?: {
|
|
72
|
+
force?: boolean;
|
|
73
|
+
}): Promise<SkillUpdateResult>;
|
|
@@ -156,76 +156,80 @@ export async function listInstalledSkills(projectRoot) {
|
|
|
156
156
|
totalCount: coreSkills.length + thirdPartySkills.length
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
|
-
export async function
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (fsSync.existsSync(path.join(targetDir, "SKILL.md"))) {
|
|
176
|
-
const content = await fs.readFile(path.join(targetDir, "SKILL.md"), "utf8");
|
|
177
|
-
const meta = parseSkillFrontmatter(content);
|
|
178
|
-
return { sourceSkillPath: targetDir, skillName: requestedName, meta };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (skillDirs.length === 1) {
|
|
182
|
-
const onlyDir = skillDirs[0].name;
|
|
183
|
-
const targetDir = path.join(skillsSubDir, onlyDir);
|
|
184
|
-
if (fsSync.existsSync(path.join(targetDir, "SKILL.md"))) {
|
|
185
|
-
const content = await fs.readFile(path.join(targetDir, "SKILL.md"), "utf8");
|
|
186
|
-
const meta = parseSkillFrontmatter(content);
|
|
187
|
-
return { sourceSkillPath: targetDir, skillName: requestedName || meta.name || onlyDir, meta };
|
|
188
|
-
}
|
|
159
|
+
export async function discoverSkillsInDirectory(rootDir, maxDepth = 5, currentDepth = 0) {
|
|
160
|
+
const results = [];
|
|
161
|
+
if (currentDepth > maxDepth)
|
|
162
|
+
return results;
|
|
163
|
+
const skillMdPath = path.join(rootDir, "SKILL.md");
|
|
164
|
+
if (fsSync.existsSync(skillMdPath)) {
|
|
165
|
+
try {
|
|
166
|
+
const content = await fs.readFile(skillMdPath, "utf8");
|
|
167
|
+
const meta = parseSkillFrontmatter(content);
|
|
168
|
+
const skillName = meta.name || path.basename(rootDir);
|
|
169
|
+
results.push({
|
|
170
|
+
sourceSkillPath: rootDir,
|
|
171
|
+
skillName,
|
|
172
|
+
meta
|
|
173
|
+
});
|
|
174
|
+
return results;
|
|
189
175
|
}
|
|
190
|
-
|
|
191
|
-
//
|
|
192
|
-
const baseName = path.basename(sourceDir);
|
|
193
|
-
for (const dir of skillDirs) {
|
|
194
|
-
if (dir.name === requestedName || dir.name === baseName) {
|
|
195
|
-
const targetDir = path.join(skillsSubDir, dir.name);
|
|
196
|
-
if (fsSync.existsSync(path.join(targetDir, "SKILL.md"))) {
|
|
197
|
-
const content = await fs.readFile(path.join(targetDir, "SKILL.md"), "utf8");
|
|
198
|
-
const meta = parseSkillFrontmatter(content);
|
|
199
|
-
return { sourceSkillPath: targetDir, skillName: dir.name, meta };
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
176
|
+
catch {
|
|
177
|
+
// ignore read error
|
|
203
178
|
}
|
|
204
179
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
180
|
+
let entries;
|
|
181
|
+
try {
|
|
182
|
+
entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return results;
|
|
186
|
+
}
|
|
187
|
+
const IGNORED_DIRS = new Set([".git", "node_modules", "dist", ".nexus", "build", "coverage", ".turbo"]);
|
|
188
|
+
for (const entry of entries) {
|
|
189
|
+
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {
|
|
190
|
+
const subDir = path.join(rootDir, entry.name);
|
|
191
|
+
const subResults = await discoverSkillsInDirectory(subDir, maxDepth, currentDepth + 1);
|
|
192
|
+
results.push(...subResults);
|
|
217
193
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
194
|
+
}
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
197
|
+
export async function findSkillSourceDirectory(sourceDir, requestedName) {
|
|
198
|
+
const discovered = await discoverSkillsInDirectory(sourceDir);
|
|
199
|
+
if (discovered.length === 0) {
|
|
200
|
+
throw new Error(`Could not find any valid skill with SKILL.md in source: ${sourceDir}`);
|
|
201
|
+
}
|
|
202
|
+
if (requestedName) {
|
|
203
|
+
const match = discovered.find((s) => s.skillName === requestedName || path.basename(s.sourceSkillPath) === requestedName);
|
|
204
|
+
if (match) {
|
|
205
|
+
return {
|
|
206
|
+
sourceSkillPath: match.sourceSkillPath,
|
|
207
|
+
skillName: requestedName,
|
|
208
|
+
meta: match.meta
|
|
209
|
+
};
|
|
226
210
|
}
|
|
211
|
+
const available = discovered.map((s) => s.skillName).join(", ");
|
|
212
|
+
throw new Error(`Skill "${requestedName}" not found in source. Available skills (${discovered.length}): ${available}`);
|
|
213
|
+
}
|
|
214
|
+
if (discovered.length === 1) {
|
|
215
|
+
return {
|
|
216
|
+
sourceSkillPath: discovered[0].sourceSkillPath,
|
|
217
|
+
skillName: discovered[0].skillName,
|
|
218
|
+
meta: discovered[0].meta
|
|
219
|
+
};
|
|
227
220
|
}
|
|
228
|
-
|
|
221
|
+
// Check if one matches the sourceDir basename
|
|
222
|
+
const baseName = path.basename(sourceDir);
|
|
223
|
+
const baseMatch = discovered.find((s) => s.skillName === baseName || path.basename(s.sourceSkillPath) === baseName);
|
|
224
|
+
if (baseMatch) {
|
|
225
|
+
return {
|
|
226
|
+
sourceSkillPath: baseMatch.sourceSkillPath,
|
|
227
|
+
skillName: baseMatch.skillName,
|
|
228
|
+
meta: baseMatch.meta
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const available = discovered.map((s) => s.skillName).join(", ");
|
|
232
|
+
throw new Error(`Multiple skills found in source (${discovered.length} skills: ${available}). Please specify --name <skill-name> or use --all to install all skills.`);
|
|
229
233
|
}
|
|
230
234
|
export async function installThirdPartySkill(projectRoot, source, options) {
|
|
231
235
|
const isGitUrl = /^https?:\/\/|^git@|^ssh:\/\/|\.git$/.test(source);
|
|
@@ -251,6 +255,64 @@ export async function installThirdPartySkill(projectRoot, source, options) {
|
|
|
251
255
|
throw new Error(`Source directory does not exist: ${source}`);
|
|
252
256
|
}
|
|
253
257
|
}
|
|
258
|
+
const recordedSource = options?.overrideSource || source;
|
|
259
|
+
const recordedType = isGitUrl || options?.overrideSource ? "git" : "local";
|
|
260
|
+
if (options?.all) {
|
|
261
|
+
const discovered = await discoverSkillsInDirectory(sourceDirectory);
|
|
262
|
+
if (discovered.length === 0) {
|
|
263
|
+
throw new Error(`Could not find any valid skill with SKILL.md in source: ${source}`);
|
|
264
|
+
}
|
|
265
|
+
const installedList = [];
|
|
266
|
+
const manifest = (await readDevflowManifest(projectRoot)) || {
|
|
267
|
+
schemaVersion: 1,
|
|
268
|
+
name: "nexus-devflow",
|
|
269
|
+
package: "@jakkrichm/create-nexus-devflow",
|
|
270
|
+
version: "2.9.3"
|
|
271
|
+
};
|
|
272
|
+
const existingThirdParty = Array.isArray(manifest.thirdPartySkills)
|
|
273
|
+
? manifest.thirdPartySkills
|
|
274
|
+
: [];
|
|
275
|
+
let updatedThirdParty = [...existingThirdParty];
|
|
276
|
+
for (const skill of discovered) {
|
|
277
|
+
const skillName = skill.skillName;
|
|
278
|
+
if (!SKILL_NAME_PATTERN.test(skillName)) {
|
|
279
|
+
continue; // skip invalid names in batch mode
|
|
280
|
+
}
|
|
281
|
+
if (coreNameSet.has(skillName) && !options?.force) {
|
|
282
|
+
continue; // skip core collisions in batch mode
|
|
283
|
+
}
|
|
284
|
+
const targetAgentsSkillDir = path.join(projectRoot, ".agents", "skills", skillName);
|
|
285
|
+
const targetClaudeSkillDir = path.join(projectRoot, ".claude", "skills", skillName);
|
|
286
|
+
await fs.rm(targetAgentsSkillDir, { recursive: true, force: true });
|
|
287
|
+
await fs.rm(targetClaudeSkillDir, { recursive: true, force: true });
|
|
288
|
+
await fs.mkdir(path.dirname(targetAgentsSkillDir), { recursive: true });
|
|
289
|
+
await fs.mkdir(path.dirname(targetClaudeSkillDir), { recursive: true });
|
|
290
|
+
await fs.cp(skill.sourceSkillPath, targetAgentsSkillDir, { recursive: true });
|
|
291
|
+
await fs.cp(skill.sourceSkillPath, targetClaudeSkillDir, { recursive: true });
|
|
292
|
+
updatedThirdParty = updatedThirdParty.filter((s) => s.name !== skillName);
|
|
293
|
+
updatedThirdParty.push({
|
|
294
|
+
name: skillName,
|
|
295
|
+
source: recordedSource,
|
|
296
|
+
version: skill.meta.version || "1.0.0",
|
|
297
|
+
description: skill.meta.description || "",
|
|
298
|
+
installedAt: new Date().toISOString(),
|
|
299
|
+
type: recordedType
|
|
300
|
+
});
|
|
301
|
+
installedList.push({
|
|
302
|
+
name: skillName,
|
|
303
|
+
category: "third-party",
|
|
304
|
+
description: skill.meta.description || "",
|
|
305
|
+
version: skill.meta.version || "1.0.0",
|
|
306
|
+
source: recordedSource,
|
|
307
|
+
adapters: [".agents", ".claude"],
|
|
308
|
+
synced: true,
|
|
309
|
+
path: `.agents/skills/${skillName}`
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
manifest.thirdPartySkills = updatedThirdParty;
|
|
313
|
+
await writeDevflowManifest(projectRoot, manifest);
|
|
314
|
+
return installedList;
|
|
315
|
+
}
|
|
254
316
|
const { sourceSkillPath, skillName, meta } = await findSkillSourceDirectory(sourceDirectory, options?.name);
|
|
255
317
|
if (!SKILL_NAME_PATTERN.test(skillName)) {
|
|
256
318
|
throw new Error(`Invalid skill name: "${skillName}". Must use kebab-case (e.g. "diagram-design").`);
|
|
@@ -273,7 +335,7 @@ export async function installThirdPartySkill(projectRoot, source, options) {
|
|
|
273
335
|
schemaVersion: 1,
|
|
274
336
|
name: "nexus-devflow",
|
|
275
337
|
package: "@jakkrichm/create-nexus-devflow",
|
|
276
|
-
version: "2.9.
|
|
338
|
+
version: "2.9.3"
|
|
277
339
|
};
|
|
278
340
|
const existingThirdParty = Array.isArray(manifest.thirdPartySkills)
|
|
279
341
|
? manifest.thirdPartySkills
|
|
@@ -281,11 +343,11 @@ export async function installThirdPartySkill(projectRoot, source, options) {
|
|
|
281
343
|
const filtered = existingThirdParty.filter((s) => s.name !== skillName);
|
|
282
344
|
filtered.push({
|
|
283
345
|
name: skillName,
|
|
284
|
-
source,
|
|
346
|
+
source: recordedSource,
|
|
285
347
|
version: meta.version || "1.0.0",
|
|
286
348
|
description: meta.description || "",
|
|
287
349
|
installedAt: new Date().toISOString(),
|
|
288
|
-
type:
|
|
350
|
+
type: recordedType
|
|
289
351
|
});
|
|
290
352
|
manifest.thirdPartySkills = filtered;
|
|
291
353
|
await writeDevflowManifest(projectRoot, manifest);
|
|
@@ -294,7 +356,7 @@ export async function installThirdPartySkill(projectRoot, source, options) {
|
|
|
294
356
|
category: "third-party",
|
|
295
357
|
description: meta.description || "",
|
|
296
358
|
version: meta.version || "1.0.0",
|
|
297
|
-
source,
|
|
359
|
+
source: recordedSource,
|
|
298
360
|
adapters: [".agents", ".claude"],
|
|
299
361
|
synced: true,
|
|
300
362
|
path: `.agents/skills/${skillName}`
|
|
@@ -369,4 +431,80 @@ export async function syncSkills(projectRoot) {
|
|
|
369
431
|
skills: syncedSkills
|
|
370
432
|
};
|
|
371
433
|
}
|
|
434
|
+
export async function updateThirdPartySkills(projectRoot, targetSkillName, options) {
|
|
435
|
+
const manifest = await readDevflowManifest(projectRoot);
|
|
436
|
+
const existingThirdParty = Array.isArray(manifest?.thirdPartySkills)
|
|
437
|
+
? manifest.thirdPartySkills
|
|
438
|
+
: [];
|
|
439
|
+
if (existingThirdParty.length === 0) {
|
|
440
|
+
return { updatedSkills: [], failedSkills: [], totalUpdated: 0 };
|
|
441
|
+
}
|
|
442
|
+
let skillsToUpdate = existingThirdParty;
|
|
443
|
+
if (targetSkillName && targetSkillName !== "--all") {
|
|
444
|
+
skillsToUpdate = existingThirdParty.filter((s) => s.name === targetSkillName);
|
|
445
|
+
if (skillsToUpdate.length === 0) {
|
|
446
|
+
throw new Error(`Skill "${targetSkillName}" is not installed as a third-party skill.`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Group by source to avoid duplicate cloning
|
|
450
|
+
const sourceToSkillsMap = new Map();
|
|
451
|
+
for (const skill of skillsToUpdate) {
|
|
452
|
+
const list = sourceToSkillsMap.get(skill.source) || [];
|
|
453
|
+
list.push(skill);
|
|
454
|
+
sourceToSkillsMap.set(skill.source, list);
|
|
455
|
+
}
|
|
456
|
+
const updatedSkills = [];
|
|
457
|
+
const failedSkills = [];
|
|
458
|
+
for (const [source, skills] of sourceToSkillsMap) {
|
|
459
|
+
const isGit = /^https?:\/\/|^git@|^ssh:\/\/|\.git$/.test(source);
|
|
460
|
+
let tempCloneDir = null;
|
|
461
|
+
try {
|
|
462
|
+
let sourceDir = source;
|
|
463
|
+
if (isGit) {
|
|
464
|
+
tempCloneDir = await fs.mkdtemp(path.join(os.tmpdir(), "nexus-skill-update-"));
|
|
465
|
+
await execFileAsync("git", ["clone", "--depth", "1", source, tempCloneDir]);
|
|
466
|
+
sourceDir = tempCloneDir;
|
|
467
|
+
}
|
|
468
|
+
for (const skill of skills) {
|
|
469
|
+
try {
|
|
470
|
+
const detail = (await installThirdPartySkill(projectRoot, sourceDir, {
|
|
471
|
+
name: skill.name,
|
|
472
|
+
force: true,
|
|
473
|
+
overrideSource: isGit ? source : undefined
|
|
474
|
+
}));
|
|
475
|
+
updatedSkills.push(detail);
|
|
476
|
+
}
|
|
477
|
+
catch (err) {
|
|
478
|
+
failedSkills.push({
|
|
479
|
+
name: skill.name,
|
|
480
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
for (const skill of skills) {
|
|
487
|
+
failedSkills.push({
|
|
488
|
+
name: skill.name,
|
|
489
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
if (tempCloneDir) {
|
|
495
|
+
try {
|
|
496
|
+
await fs.rm(tempCloneDir, { recursive: true, force: true });
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// ignore
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
updatedSkills,
|
|
506
|
+
failedSkills,
|
|
507
|
+
totalUpdated: updatedSkills.length
|
|
508
|
+
};
|
|
509
|
+
}
|
|
372
510
|
//# sourceMappingURL=skill-manager.js.map
|