apero-kit-cli 1.7.0 → 2.0.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/bin/ak.js +1 -85
- package/dist/index.js +2437 -0
- package/dist/index.js.map +1 -0
- package/package.json +20 -10
- package/src/commands/add.js +0 -126
- package/src/commands/doctor.js +0 -129
- package/src/commands/help.js +0 -1412
- package/src/commands/init.js +0 -263
- package/src/commands/list.js +0 -190
- package/src/commands/status.js +0 -113
- package/src/commands/update.js +0 -183
- package/src/index.js +0 -8
- package/src/kits/index.js +0 -122
- package/src/utils/copy.js +0 -214
- package/src/utils/hash.js +0 -74
- package/src/utils/paths.js +0 -255
- package/src/utils/prompts.js +0 -254
- package/src/utils/state.js +0 -136
package/dist/index.js
ADDED
|
@@ -0,0 +1,2437 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/kits/index.ts
|
|
14
|
+
var KITS, getKit, getKitList;
|
|
15
|
+
var init_kits = __esm({
|
|
16
|
+
"src/kits/index.ts"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
KITS = {
|
|
19
|
+
engineer: {
|
|
20
|
+
name: "engineer",
|
|
21
|
+
description: "Full-stack development kit for building applications",
|
|
22
|
+
emoji: "\u{1F6E0}\uFE0F",
|
|
23
|
+
color: "blue",
|
|
24
|
+
agents: [
|
|
25
|
+
"planner",
|
|
26
|
+
"debugger",
|
|
27
|
+
"fullstack-developer",
|
|
28
|
+
"tester",
|
|
29
|
+
"code-reviewer",
|
|
30
|
+
"git-manager",
|
|
31
|
+
"database-admin"
|
|
32
|
+
],
|
|
33
|
+
commands: [
|
|
34
|
+
"plan",
|
|
35
|
+
"plan/parallel",
|
|
36
|
+
"plan/fast",
|
|
37
|
+
"plan/hard",
|
|
38
|
+
"code",
|
|
39
|
+
"code/auto",
|
|
40
|
+
"code/parallel",
|
|
41
|
+
"fix",
|
|
42
|
+
"fix/test",
|
|
43
|
+
"fix/types",
|
|
44
|
+
"fix/fast",
|
|
45
|
+
"fix/ci",
|
|
46
|
+
"test",
|
|
47
|
+
"test/ui",
|
|
48
|
+
"review",
|
|
49
|
+
"review/codebase",
|
|
50
|
+
"scout",
|
|
51
|
+
"build",
|
|
52
|
+
"lint"
|
|
53
|
+
],
|
|
54
|
+
skills: [
|
|
55
|
+
"frontend-development",
|
|
56
|
+
"backend-development",
|
|
57
|
+
"databases",
|
|
58
|
+
"debugging",
|
|
59
|
+
"code-review",
|
|
60
|
+
"planning",
|
|
61
|
+
"problem-solving"
|
|
62
|
+
],
|
|
63
|
+
workflows: ["feature-development", "bug-fixing"],
|
|
64
|
+
includeRouter: true,
|
|
65
|
+
includeHooks: true
|
|
66
|
+
},
|
|
67
|
+
researcher: {
|
|
68
|
+
name: "researcher",
|
|
69
|
+
description: "Research and analysis kit for exploring codebases",
|
|
70
|
+
emoji: "\u{1F52C}",
|
|
71
|
+
color: "green",
|
|
72
|
+
agents: [
|
|
73
|
+
"researcher",
|
|
74
|
+
"scout",
|
|
75
|
+
"scout-external",
|
|
76
|
+
"brainstormer",
|
|
77
|
+
"docs-manager",
|
|
78
|
+
"planner"
|
|
79
|
+
],
|
|
80
|
+
commands: [
|
|
81
|
+
"scout",
|
|
82
|
+
"scout/ext",
|
|
83
|
+
"investigate",
|
|
84
|
+
"brainstorm",
|
|
85
|
+
"docs",
|
|
86
|
+
"docs/init",
|
|
87
|
+
"docs/update",
|
|
88
|
+
"docs/summarize",
|
|
89
|
+
"plan",
|
|
90
|
+
"ask",
|
|
91
|
+
"context"
|
|
92
|
+
],
|
|
93
|
+
skills: [
|
|
94
|
+
"research",
|
|
95
|
+
"planning-with-files",
|
|
96
|
+
"documentation",
|
|
97
|
+
"project-index"
|
|
98
|
+
],
|
|
99
|
+
workflows: [],
|
|
100
|
+
includeRouter: true,
|
|
101
|
+
includeHooks: false
|
|
102
|
+
},
|
|
103
|
+
designer: {
|
|
104
|
+
name: "designer",
|
|
105
|
+
description: "UI/UX design and frontend development kit",
|
|
106
|
+
emoji: "\u{1F3A8}",
|
|
107
|
+
color: "magenta",
|
|
108
|
+
agents: [
|
|
109
|
+
"ui-ux-designer",
|
|
110
|
+
"fullstack-developer",
|
|
111
|
+
"code-reviewer"
|
|
112
|
+
],
|
|
113
|
+
commands: [
|
|
114
|
+
"code",
|
|
115
|
+
"fix",
|
|
116
|
+
"fix/ui",
|
|
117
|
+
"test/ui",
|
|
118
|
+
"review"
|
|
119
|
+
],
|
|
120
|
+
skills: [
|
|
121
|
+
"ui-ux-pro-max",
|
|
122
|
+
"frontend-development",
|
|
123
|
+
"frontend-design"
|
|
124
|
+
],
|
|
125
|
+
workflows: [],
|
|
126
|
+
includeRouter: false,
|
|
127
|
+
includeHooks: true
|
|
128
|
+
},
|
|
129
|
+
minimal: {
|
|
130
|
+
name: "minimal",
|
|
131
|
+
description: "Lightweight kit with essential agents only",
|
|
132
|
+
emoji: "\u{1F4E6}",
|
|
133
|
+
color: "yellow",
|
|
134
|
+
agents: ["planner", "debugger"],
|
|
135
|
+
commands: ["plan", "fix", "code"],
|
|
136
|
+
skills: ["planning", "debugging"],
|
|
137
|
+
workflows: [],
|
|
138
|
+
includeRouter: false,
|
|
139
|
+
includeHooks: false
|
|
140
|
+
},
|
|
141
|
+
full: {
|
|
142
|
+
name: "full",
|
|
143
|
+
description: "Complete kit with ALL agents, commands, and skills",
|
|
144
|
+
emoji: "\u{1F680}",
|
|
145
|
+
color: "cyan",
|
|
146
|
+
agents: "all",
|
|
147
|
+
commands: "all",
|
|
148
|
+
skills: "all",
|
|
149
|
+
workflows: "all",
|
|
150
|
+
includeRouter: true,
|
|
151
|
+
includeHooks: true
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
getKit = (name) => KITS[name] || null;
|
|
155
|
+
getKitList = () => Object.values(KITS);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// src/utils/paths.ts
|
|
160
|
+
import { fileURLToPath } from "url";
|
|
161
|
+
import { dirname, join, resolve } from "path";
|
|
162
|
+
import { existsSync } from "fs";
|
|
163
|
+
import { homedir } from "os";
|
|
164
|
+
function getEmbeddedTemplates() {
|
|
165
|
+
if (existsSync(TEMPLATES_DIR)) {
|
|
166
|
+
const agentsMd = join(TEMPLATES_DIR, "AGENTS.md");
|
|
167
|
+
return {
|
|
168
|
+
path: TEMPLATES_DIR,
|
|
169
|
+
type: "embedded",
|
|
170
|
+
claudeDir: TEMPLATES_DIR,
|
|
171
|
+
agentsMd: existsSync(agentsMd) ? agentsMd : null
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function getGlobalInstallPath() {
|
|
177
|
+
if (process.platform === "win32") {
|
|
178
|
+
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "claude");
|
|
179
|
+
}
|
|
180
|
+
return join(homedir(), ".claude");
|
|
181
|
+
}
|
|
182
|
+
function isAkProject(dir = process.cwd()) {
|
|
183
|
+
const akConfig = join(dir, ".ak", "state.json");
|
|
184
|
+
const claudeDir = join(dir, ".claude");
|
|
185
|
+
const opencodeDir = join(dir, ".opencode");
|
|
186
|
+
const agentDir = join(dir, ".agent");
|
|
187
|
+
return existsSync(akConfig) || existsSync(claudeDir) || existsSync(opencodeDir) || existsSync(agentDir);
|
|
188
|
+
}
|
|
189
|
+
function getTargetDir(projectDir, target = "claude") {
|
|
190
|
+
const folder = TARGETS[target] || TARGETS.claude;
|
|
191
|
+
return join(projectDir, folder);
|
|
192
|
+
}
|
|
193
|
+
function resolveSource(sourceFlag) {
|
|
194
|
+
if (sourceFlag) {
|
|
195
|
+
const resolved = resolve(sourceFlag);
|
|
196
|
+
if (!existsSync(resolved)) {
|
|
197
|
+
return { error: `Source path not found: ${sourceFlag}` };
|
|
198
|
+
}
|
|
199
|
+
const claudeDir = join(resolved, ".claude");
|
|
200
|
+
const opencodeDir = join(resolved, ".opencode");
|
|
201
|
+
if (existsSync(claudeDir)) {
|
|
202
|
+
return {
|
|
203
|
+
path: resolved,
|
|
204
|
+
type: "custom",
|
|
205
|
+
claudeDir,
|
|
206
|
+
agentsMd: existsSync(join(resolved, "AGENTS.md")) ? join(resolved, "AGENTS.md") : null
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (existsSync(opencodeDir)) {
|
|
210
|
+
return {
|
|
211
|
+
path: resolved,
|
|
212
|
+
type: "custom",
|
|
213
|
+
claudeDir: opencodeDir,
|
|
214
|
+
agentsMd: existsSync(join(resolved, "AGENTS.md")) ? join(resolved, "AGENTS.md") : null
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (existsSync(join(resolved, "agents")) || existsSync(join(resolved, "commands"))) {
|
|
218
|
+
return {
|
|
219
|
+
path: resolved,
|
|
220
|
+
type: "custom",
|
|
221
|
+
claudeDir: resolved,
|
|
222
|
+
agentsMd: existsSync(join(resolved, "AGENTS.md")) ? join(resolved, "AGENTS.md") : null
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return { error: `No templates found in: ${sourceFlag}` };
|
|
226
|
+
}
|
|
227
|
+
const embedded = getEmbeddedTemplates();
|
|
228
|
+
if (embedded) {
|
|
229
|
+
return embedded;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
error: "No templates found. Reinstall: npm install -g apero-kit-cli"
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
var __filename, __dirname, CLI_ROOT, TEMPLATES_DIR, TARGETS;
|
|
236
|
+
var init_paths = __esm({
|
|
237
|
+
"src/utils/paths.ts"() {
|
|
238
|
+
"use strict";
|
|
239
|
+
__filename = fileURLToPath(import.meta.url);
|
|
240
|
+
__dirname = dirname(__filename);
|
|
241
|
+
CLI_ROOT = __dirname.endsWith("dist") ? resolve(__dirname, "..") : resolve(__dirname, "../..");
|
|
242
|
+
TEMPLATES_DIR = join(CLI_ROOT, "templates");
|
|
243
|
+
TARGETS = {
|
|
244
|
+
claude: ".claude",
|
|
245
|
+
opencode: ".opencode",
|
|
246
|
+
generic: ".agent"
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// src/utils/copy.ts
|
|
252
|
+
import fs from "fs-extra";
|
|
253
|
+
import { join as join2 } from "path";
|
|
254
|
+
async function copyItems(items, type, sourceDir, destDir, mergeMode = false) {
|
|
255
|
+
const typeDir = join2(sourceDir, type);
|
|
256
|
+
const destTypeDir = join2(destDir, type);
|
|
257
|
+
if (!fs.existsSync(typeDir)) {
|
|
258
|
+
return { copied: [], skipped: items, errors: [] };
|
|
259
|
+
}
|
|
260
|
+
await fs.ensureDir(destTypeDir);
|
|
261
|
+
const copied = [];
|
|
262
|
+
const skipped = [];
|
|
263
|
+
const errors = [];
|
|
264
|
+
for (const item of items) {
|
|
265
|
+
try {
|
|
266
|
+
const itemPath = join2(typeDir, item);
|
|
267
|
+
const itemPathMd = itemPath + ".md";
|
|
268
|
+
let srcPath;
|
|
269
|
+
if (fs.existsSync(itemPath)) {
|
|
270
|
+
srcPath = itemPath;
|
|
271
|
+
} else if (fs.existsSync(itemPathMd)) {
|
|
272
|
+
srcPath = itemPathMd;
|
|
273
|
+
} else {
|
|
274
|
+
skipped.push(item);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const stat = fs.statSync(srcPath);
|
|
278
|
+
if (stat.isDirectory()) {
|
|
279
|
+
await fs.copy(srcPath, join2(destTypeDir, item), { overwrite: !mergeMode });
|
|
280
|
+
} else {
|
|
281
|
+
const destPath = srcPath.endsWith(".md") ? join2(destTypeDir, item + ".md") : join2(destTypeDir, item);
|
|
282
|
+
await fs.ensureDir(join2(destTypeDir, item.split("/").slice(0, -1).join("/")));
|
|
283
|
+
await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
|
|
284
|
+
}
|
|
285
|
+
copied.push(item);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
errors.push({ item, error: err.message });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { copied, skipped, errors };
|
|
291
|
+
}
|
|
292
|
+
async function copyAllOfType(type, sourceDir, destDir, mergeMode = false) {
|
|
293
|
+
const typeDir = join2(sourceDir, type);
|
|
294
|
+
const destTypeDir = join2(destDir, type);
|
|
295
|
+
if (!fs.existsSync(typeDir)) {
|
|
296
|
+
return { success: false, error: `${type} directory not found` };
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
await fs.copy(typeDir, destTypeDir, { overwrite: !mergeMode });
|
|
300
|
+
return { success: true };
|
|
301
|
+
} catch (err) {
|
|
302
|
+
return { success: false, error: err.message };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function copyRouter(sourceDir, destDir, mergeMode = false) {
|
|
306
|
+
const routerDir = join2(sourceDir, "router");
|
|
307
|
+
if (!fs.existsSync(routerDir)) {
|
|
308
|
+
return { success: false, error: "Router directory not found" };
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
await fs.copy(routerDir, join2(destDir, "router"), { overwrite: !mergeMode });
|
|
312
|
+
return { success: true };
|
|
313
|
+
} catch (err) {
|
|
314
|
+
return { success: false, error: err.message };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function copyHooks(sourceDir, destDir, mergeMode = false) {
|
|
318
|
+
const hooksDir = join2(sourceDir, "hooks");
|
|
319
|
+
if (!fs.existsSync(hooksDir)) {
|
|
320
|
+
return { success: false, error: "Hooks directory not found" };
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
await fs.copy(hooksDir, join2(destDir, "hooks"), { overwrite: !mergeMode });
|
|
324
|
+
return { success: true };
|
|
325
|
+
} catch (err) {
|
|
326
|
+
return { success: false, error: err.message };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function copyBaseFiles(sourceDir, destDir, mergeMode = false) {
|
|
330
|
+
const baseFiles = ["README.md", "settings.json", ".env.example"];
|
|
331
|
+
const copied = [];
|
|
332
|
+
for (const file of baseFiles) {
|
|
333
|
+
const srcPath = join2(sourceDir, file);
|
|
334
|
+
const destPath = join2(destDir, file);
|
|
335
|
+
if (mergeMode && fs.existsSync(destPath)) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (fs.existsSync(srcPath)) {
|
|
339
|
+
await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
|
|
340
|
+
copied.push(file);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return copied;
|
|
344
|
+
}
|
|
345
|
+
async function copyAgentsMd(agentsMdPath, projectDir, mergeMode = false) {
|
|
346
|
+
if (!agentsMdPath || !fs.existsSync(agentsMdPath)) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
const destPath = join2(projectDir, "AGENTS.md");
|
|
350
|
+
if (mergeMode && fs.existsSync(destPath)) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
await fs.copy(agentsMdPath, destPath, { overwrite: !mergeMode });
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
function listAvailable(type, sourceDir) {
|
|
357
|
+
const typeDir = join2(sourceDir, type);
|
|
358
|
+
if (!fs.existsSync(typeDir)) {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
const items = fs.readdirSync(typeDir);
|
|
362
|
+
return items.map((item) => {
|
|
363
|
+
const itemPath = join2(typeDir, item);
|
|
364
|
+
const isDir = fs.statSync(itemPath).isDirectory();
|
|
365
|
+
const name = item.replace(/\.md$/, "");
|
|
366
|
+
return { name, isDir, path: itemPath };
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
var init_copy = __esm({
|
|
370
|
+
"src/utils/copy.ts"() {
|
|
371
|
+
"use strict";
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// src/utils/hash.ts
|
|
376
|
+
import { createHash } from "crypto";
|
|
377
|
+
import fs2 from "fs-extra";
|
|
378
|
+
import { join as join3, relative } from "path";
|
|
379
|
+
function hashFile(filePath) {
|
|
380
|
+
if (!fs2.existsSync(filePath)) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const content = fs2.readFileSync(filePath);
|
|
384
|
+
return createHash("md5").update(content).digest("hex");
|
|
385
|
+
}
|
|
386
|
+
async function hashDirectory(dirPath, baseDir = dirPath) {
|
|
387
|
+
const hashes = {};
|
|
388
|
+
if (!fs2.existsSync(dirPath)) {
|
|
389
|
+
return hashes;
|
|
390
|
+
}
|
|
391
|
+
const items = await fs2.readdir(dirPath, { withFileTypes: true });
|
|
392
|
+
for (const item of items) {
|
|
393
|
+
const itemPath = join3(dirPath, item.name);
|
|
394
|
+
const relativePath = relative(baseDir, itemPath);
|
|
395
|
+
if (item.isDirectory()) {
|
|
396
|
+
const subHashes = await hashDirectory(itemPath, baseDir);
|
|
397
|
+
Object.assign(hashes, subHashes);
|
|
398
|
+
} else if (item.isFile()) {
|
|
399
|
+
const hash = hashFile(itemPath);
|
|
400
|
+
if (hash) {
|
|
401
|
+
hashes[relativePath] = hash;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return hashes;
|
|
406
|
+
}
|
|
407
|
+
var init_hash = __esm({
|
|
408
|
+
"src/utils/hash.ts"() {
|
|
409
|
+
"use strict";
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// src/utils/state.ts
|
|
414
|
+
import fs3 from "fs-extra";
|
|
415
|
+
import { join as join4 } from "path";
|
|
416
|
+
function getStatePath(projectDir) {
|
|
417
|
+
return join4(projectDir, STATE_DIR, STATE_FILE);
|
|
418
|
+
}
|
|
419
|
+
async function loadState(projectDir) {
|
|
420
|
+
const statePath = getStatePath(projectDir);
|
|
421
|
+
if (!fs3.existsSync(statePath)) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
return await fs3.readJson(statePath);
|
|
426
|
+
} catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function saveState(projectDir, state) {
|
|
431
|
+
const stateDir = join4(projectDir, STATE_DIR);
|
|
432
|
+
const statePath = join4(stateDir, STATE_FILE);
|
|
433
|
+
await fs3.ensureDir(stateDir);
|
|
434
|
+
await fs3.writeJson(statePath, state, { spaces: 2 });
|
|
435
|
+
}
|
|
436
|
+
async function createInitialState(projectDir, options) {
|
|
437
|
+
const { kit, source, target, installed } = options;
|
|
438
|
+
const targetDir = join4(projectDir, target);
|
|
439
|
+
const hashes = await hashDirectory(targetDir);
|
|
440
|
+
const state = {
|
|
441
|
+
version: "1.0.0",
|
|
442
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
443
|
+
lastUpdate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
444
|
+
kit,
|
|
445
|
+
source,
|
|
446
|
+
target,
|
|
447
|
+
installed,
|
|
448
|
+
originalHashes: hashes
|
|
449
|
+
};
|
|
450
|
+
await saveState(projectDir, state);
|
|
451
|
+
return state;
|
|
452
|
+
}
|
|
453
|
+
async function updateState(projectDir, updates) {
|
|
454
|
+
const state = await loadState(projectDir);
|
|
455
|
+
if (!state) {
|
|
456
|
+
throw new Error("No state found. Is this an ak project?");
|
|
457
|
+
}
|
|
458
|
+
const targetDir = join4(projectDir, state.target || ".claude");
|
|
459
|
+
const newHashes = await hashDirectory(targetDir);
|
|
460
|
+
const updatedState = {
|
|
461
|
+
...state,
|
|
462
|
+
...updates,
|
|
463
|
+
lastUpdate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
464
|
+
originalHashes: newHashes
|
|
465
|
+
};
|
|
466
|
+
await saveState(projectDir, updatedState);
|
|
467
|
+
return updatedState;
|
|
468
|
+
}
|
|
469
|
+
async function getFileStatuses(projectDir) {
|
|
470
|
+
const state = await loadState(projectDir);
|
|
471
|
+
if (!state) {
|
|
472
|
+
return { error: "No state found" };
|
|
473
|
+
}
|
|
474
|
+
const targetDir = join4(projectDir, state.target || ".claude");
|
|
475
|
+
const currentHashes = await hashDirectory(targetDir);
|
|
476
|
+
const originalHashes = state.originalHashes || {};
|
|
477
|
+
const statuses = {
|
|
478
|
+
unchanged: [],
|
|
479
|
+
modified: [],
|
|
480
|
+
added: [],
|
|
481
|
+
deleted: []
|
|
482
|
+
};
|
|
483
|
+
for (const [path, hash] of Object.entries(originalHashes)) {
|
|
484
|
+
if (currentHashes[path] === void 0) {
|
|
485
|
+
statuses.deleted.push(path);
|
|
486
|
+
} else if (currentHashes[path] !== hash) {
|
|
487
|
+
statuses.modified.push(path);
|
|
488
|
+
} else {
|
|
489
|
+
statuses.unchanged.push(path);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
for (const path of Object.keys(currentHashes)) {
|
|
493
|
+
if (originalHashes[path] === void 0) {
|
|
494
|
+
statuses.added.push(path);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
state,
|
|
499
|
+
statuses,
|
|
500
|
+
targetDir
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
var STATE_DIR, STATE_FILE;
|
|
504
|
+
var init_state = __esm({
|
|
505
|
+
"src/utils/state.ts"() {
|
|
506
|
+
"use strict";
|
|
507
|
+
init_hash();
|
|
508
|
+
STATE_DIR = ".ak";
|
|
509
|
+
STATE_FILE = "state.json";
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// src/utils/prompts.ts
|
|
514
|
+
import * as p from "@clack/prompts";
|
|
515
|
+
import pc from "picocolors";
|
|
516
|
+
async function promptKit() {
|
|
517
|
+
const kits = getKitList();
|
|
518
|
+
const options = [
|
|
519
|
+
...kits.map((kit2) => ({
|
|
520
|
+
value: kit2.name,
|
|
521
|
+
label: `${kit2.emoji} ${kit2.name}`,
|
|
522
|
+
hint: kit2.description
|
|
523
|
+
})),
|
|
524
|
+
{ value: "custom", label: "\u{1F527} custom", hint: "Pick your own agents, skills, and commands" }
|
|
525
|
+
];
|
|
526
|
+
const kit = await p.select({ message: "Select a kit:", options });
|
|
527
|
+
if (p.isCancel(kit)) process.exit(0);
|
|
528
|
+
return kit;
|
|
529
|
+
}
|
|
530
|
+
async function promptAgents(sourceDir) {
|
|
531
|
+
const available = listAvailable("agents", sourceDir);
|
|
532
|
+
if (available.length === 0) return [];
|
|
533
|
+
const selected = await p.multiselect({
|
|
534
|
+
message: "Select agents:",
|
|
535
|
+
options: available.map((item) => ({
|
|
536
|
+
value: item.name,
|
|
537
|
+
label: item.name,
|
|
538
|
+
hint: ["planner", "debugger"].includes(item.name) ? "(recommended)" : void 0
|
|
539
|
+
})),
|
|
540
|
+
initialValues: ["planner", "debugger"].filter((n) => available.some((a) => a.name === n))
|
|
541
|
+
});
|
|
542
|
+
if (p.isCancel(selected)) process.exit(0);
|
|
543
|
+
return selected;
|
|
544
|
+
}
|
|
545
|
+
async function promptSkills(sourceDir) {
|
|
546
|
+
const available = listAvailable("skills", sourceDir).filter((s) => s.isDir);
|
|
547
|
+
if (available.length === 0) return [];
|
|
548
|
+
const selected = await p.multiselect({
|
|
549
|
+
message: "Select skills:",
|
|
550
|
+
options: available.map((item) => ({
|
|
551
|
+
value: item.name,
|
|
552
|
+
label: item.name,
|
|
553
|
+
hint: ["planning", "debugging"].includes(item.name) ? "(recommended)" : void 0
|
|
554
|
+
})),
|
|
555
|
+
initialValues: ["planning", "debugging"].filter((n) => available.some((a) => a.name === n))
|
|
556
|
+
});
|
|
557
|
+
if (p.isCancel(selected)) process.exit(0);
|
|
558
|
+
return selected;
|
|
559
|
+
}
|
|
560
|
+
async function promptCommands(sourceDir) {
|
|
561
|
+
const available = listAvailable("commands", sourceDir);
|
|
562
|
+
if (available.length === 0) return [];
|
|
563
|
+
const selected = await p.multiselect({
|
|
564
|
+
message: "Select commands:",
|
|
565
|
+
options: available.map((item) => ({
|
|
566
|
+
value: item.name,
|
|
567
|
+
label: item.name,
|
|
568
|
+
hint: ["plan", "fix", "code"].includes(item.name) ? "(recommended)" : void 0
|
|
569
|
+
})),
|
|
570
|
+
initialValues: ["plan", "fix", "code"].filter((n) => available.some((a) => a.name === n))
|
|
571
|
+
});
|
|
572
|
+
if (p.isCancel(selected)) process.exit(0);
|
|
573
|
+
return selected;
|
|
574
|
+
}
|
|
575
|
+
async function promptIncludeRouter() {
|
|
576
|
+
const result = await p.confirm({ message: "Include router?", initialValue: true });
|
|
577
|
+
if (p.isCancel(result)) process.exit(0);
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
async function promptIncludeHooks() {
|
|
581
|
+
const result = await p.confirm({ message: "Include hooks?", initialValue: false });
|
|
582
|
+
if (p.isCancel(result)) process.exit(0);
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
async function promptConfirm(message, defaultValue = true) {
|
|
586
|
+
const result = await p.confirm({ message, initialValue: defaultValue });
|
|
587
|
+
if (p.isCancel(result)) process.exit(0);
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
async function promptExistingTarget(targetPath) {
|
|
591
|
+
const action = await p.select({
|
|
592
|
+
message: `${targetPath} already exists. What do you want to do?`,
|
|
593
|
+
options: [
|
|
594
|
+
{ value: "override", label: "Override", hint: "Replace all files" },
|
|
595
|
+
{ value: "merge", label: "Merge", hint: "Only add missing files" },
|
|
596
|
+
{ value: "skip", label: "Skip", hint: "Do nothing" }
|
|
597
|
+
]
|
|
598
|
+
});
|
|
599
|
+
if (p.isCancel(action)) process.exit(0);
|
|
600
|
+
return action;
|
|
601
|
+
}
|
|
602
|
+
async function promptUpdateConfirm(updates) {
|
|
603
|
+
console.log(pc.cyan("\nChanges to apply:"));
|
|
604
|
+
if (updates.toUpdate.length > 0) {
|
|
605
|
+
console.log(pc.green(" Will update:"));
|
|
606
|
+
updates.toUpdate.slice(0, 10).forEach((f) => console.log(pc.green(` \u2713 ${f}`)));
|
|
607
|
+
if (updates.toUpdate.length > 10) console.log(pc.gray(` ... and ${updates.toUpdate.length - 10} more`));
|
|
608
|
+
}
|
|
609
|
+
if (updates.skipped.length > 0) {
|
|
610
|
+
console.log(pc.yellow(" Will skip (modified locally):"));
|
|
611
|
+
updates.skipped.slice(0, 5).forEach((f) => console.log(pc.yellow(` ~ ${f}`)));
|
|
612
|
+
if (updates.skipped.length > 5) console.log(pc.gray(` ... and ${updates.skipped.length - 5} more`));
|
|
613
|
+
}
|
|
614
|
+
console.log("");
|
|
615
|
+
return promptConfirm("Apply these updates?", true);
|
|
616
|
+
}
|
|
617
|
+
var init_prompts = __esm({
|
|
618
|
+
"src/utils/prompts.ts"() {
|
|
619
|
+
"use strict";
|
|
620
|
+
init_kits();
|
|
621
|
+
init_copy();
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// src/commands/init.ts
|
|
626
|
+
var init_exports = {};
|
|
627
|
+
__export(init_exports, {
|
|
628
|
+
initCommand: () => initCommand
|
|
629
|
+
});
|
|
630
|
+
import fs4 from "fs-extra";
|
|
631
|
+
import { join as join5, resolve as resolve2 } from "path";
|
|
632
|
+
import pc2 from "picocolors";
|
|
633
|
+
import ora from "ora";
|
|
634
|
+
function filterComponents(list, exclude, only) {
|
|
635
|
+
if (!list || list === "all") return list;
|
|
636
|
+
let filtered = [...list];
|
|
637
|
+
if (only) {
|
|
638
|
+
const patterns = only.split(",").map((s) => s.trim());
|
|
639
|
+
filtered = filtered.filter((item) => patterns.some((p4) => item.includes(p4)));
|
|
640
|
+
}
|
|
641
|
+
if (exclude) {
|
|
642
|
+
const patterns = exclude.split(",").map((s) => s.trim());
|
|
643
|
+
filtered = filtered.filter((item) => !patterns.some((p4) => item.includes(p4)));
|
|
644
|
+
}
|
|
645
|
+
return filtered;
|
|
646
|
+
}
|
|
647
|
+
async function initCommand(projectName, options) {
|
|
648
|
+
console.log("");
|
|
649
|
+
let projectDir;
|
|
650
|
+
let isCurrentDir = false;
|
|
651
|
+
if (options.global) {
|
|
652
|
+
projectDir = getGlobalInstallPath();
|
|
653
|
+
isCurrentDir = true;
|
|
654
|
+
console.log(pc2.cyan(`Installing globally to ${projectDir}`));
|
|
655
|
+
} else if (!projectName || projectName === ".") {
|
|
656
|
+
projectDir = process.cwd();
|
|
657
|
+
projectName = ".";
|
|
658
|
+
isCurrentDir = true;
|
|
659
|
+
console.log(pc2.gray(`Initializing in current directory: ${projectDir}`));
|
|
660
|
+
} else {
|
|
661
|
+
projectDir = resolve2(process.cwd(), projectName);
|
|
662
|
+
}
|
|
663
|
+
let target = options.target || "claude";
|
|
664
|
+
if (!TARGETS[target]) {
|
|
665
|
+
console.log(pc2.yellow(`Unknown target "${target}", using "claude"`));
|
|
666
|
+
target = "claude";
|
|
667
|
+
}
|
|
668
|
+
const targetDir = getTargetDir(projectDir, target);
|
|
669
|
+
let existingAction = null;
|
|
670
|
+
if (options.fresh && fs4.existsSync(targetDir)) {
|
|
671
|
+
await fs4.remove(targetDir);
|
|
672
|
+
const akDir = join5(projectDir, ".ak");
|
|
673
|
+
if (fs4.existsSync(akDir)) {
|
|
674
|
+
await fs4.remove(akDir);
|
|
675
|
+
}
|
|
676
|
+
console.log(pc2.cyan("Fresh install: removed existing files"));
|
|
677
|
+
existingAction = null;
|
|
678
|
+
} else if (fs4.existsSync(targetDir) && !options.force) {
|
|
679
|
+
if (!process.stdin.isTTY || options.yes) {
|
|
680
|
+
if (options.yes) {
|
|
681
|
+
existingAction = "override";
|
|
682
|
+
} else {
|
|
683
|
+
console.log(pc2.yellow(`${TARGETS[target]} already exists. Use --force to override.`));
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
existingAction = await promptExistingTarget(TARGETS[target]);
|
|
688
|
+
if (existingAction === "skip") {
|
|
689
|
+
console.log(pc2.yellow("Skipped. No changes made."));
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (!isCurrentDir && fs4.existsSync(projectDir) && !options.force) {
|
|
695
|
+
const files = fs4.readdirSync(projectDir);
|
|
696
|
+
if (files.length > 0 && !existingAction) {
|
|
697
|
+
console.log(pc2.red(`Directory "${projectName}" already exists and is not empty.`));
|
|
698
|
+
console.log(pc2.gray("Use --force to overwrite."));
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const source = resolveSource(options.source);
|
|
703
|
+
if ("error" in source) {
|
|
704
|
+
console.log(pc2.red(`Error: ${source.error}`));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
console.log(pc2.gray(`Source: ${source.path}`));
|
|
708
|
+
let kitName = options.kit;
|
|
709
|
+
if (!kitName && !options.force && !options.yes) {
|
|
710
|
+
kitName = await promptKit();
|
|
711
|
+
} else if (!kitName) {
|
|
712
|
+
kitName = "engineer";
|
|
713
|
+
}
|
|
714
|
+
const mergeMode = existingAction === "merge";
|
|
715
|
+
let toInstall = {
|
|
716
|
+
agents: [],
|
|
717
|
+
commands: [],
|
|
718
|
+
skills: [],
|
|
719
|
+
workflows: [],
|
|
720
|
+
includeRouter: false,
|
|
721
|
+
includeHooks: false
|
|
722
|
+
};
|
|
723
|
+
if (kitName === "custom" && !options.yes) {
|
|
724
|
+
console.log(pc2.cyan("\nCustom kit configuration:"));
|
|
725
|
+
toInstall.agents = await promptAgents(source.claudeDir);
|
|
726
|
+
toInstall.skills = await promptSkills(source.claudeDir);
|
|
727
|
+
toInstall.commands = await promptCommands(source.claudeDir);
|
|
728
|
+
toInstall.includeRouter = await promptIncludeRouter();
|
|
729
|
+
toInstall.includeHooks = await promptIncludeHooks();
|
|
730
|
+
} else {
|
|
731
|
+
const kit = getKit(kitName);
|
|
732
|
+
if (!kit) {
|
|
733
|
+
console.log(pc2.red(`Unknown kit: ${kitName}`));
|
|
734
|
+
console.log(pc2.gray(`Available kits: ${Object.keys(KITS).join(", ")}`));
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
toInstall = {
|
|
738
|
+
agents: kit.agents,
|
|
739
|
+
commands: kit.commands,
|
|
740
|
+
skills: kit.skills,
|
|
741
|
+
workflows: kit.workflows || [],
|
|
742
|
+
includeRouter: kit.includeRouter,
|
|
743
|
+
includeHooks: kit.includeHooks
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
if (options.exclude || options.only) {
|
|
747
|
+
toInstall.agents = filterComponents(toInstall.agents, options.exclude, options.only);
|
|
748
|
+
toInstall.skills = filterComponents(toInstall.skills, options.exclude, options.only);
|
|
749
|
+
toInstall.commands = filterComponents(toInstall.commands, options.exclude, options.only);
|
|
750
|
+
toInstall.workflows = filterComponents(toInstall.workflows, options.exclude, options.only);
|
|
751
|
+
}
|
|
752
|
+
console.log(pc2.cyan("\nWill create:"));
|
|
753
|
+
console.log(pc2.white(` Project: ${projectName}/`));
|
|
754
|
+
console.log(pc2.white(` Target: ${TARGETS[target]}/`));
|
|
755
|
+
console.log(pc2.white(` Kit: ${kitName}`));
|
|
756
|
+
if (Array.isArray(toInstall.agents)) {
|
|
757
|
+
console.log(pc2.gray(` Agents: ${toInstall.agents.length}`));
|
|
758
|
+
}
|
|
759
|
+
if (Array.isArray(toInstall.skills)) {
|
|
760
|
+
console.log(pc2.gray(` Skills: ${toInstall.skills.length}`));
|
|
761
|
+
}
|
|
762
|
+
if (Array.isArray(toInstall.commands)) {
|
|
763
|
+
console.log(pc2.gray(` Commands: ${toInstall.commands.length}`));
|
|
764
|
+
}
|
|
765
|
+
console.log("");
|
|
766
|
+
if (!options.force && !options.yes) {
|
|
767
|
+
if (!await promptConfirm("Proceed?")) {
|
|
768
|
+
console.log(pc2.yellow("Cancelled."));
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const spinner = ora("Creating project...").start();
|
|
773
|
+
try {
|
|
774
|
+
await fs4.ensureDir(projectDir);
|
|
775
|
+
const targetDir2 = getTargetDir(projectDir, target);
|
|
776
|
+
await fs4.ensureDir(targetDir2);
|
|
777
|
+
spinner.text = mergeMode ? "Merging agents..." : "Copying agents...";
|
|
778
|
+
if (toInstall.agents === "all") {
|
|
779
|
+
await copyAllOfType("agents", source.claudeDir, targetDir2, mergeMode);
|
|
780
|
+
} else if (toInstall.agents.length > 0) {
|
|
781
|
+
await copyItems(toInstall.agents, "agents", source.claudeDir, targetDir2, mergeMode);
|
|
782
|
+
}
|
|
783
|
+
spinner.text = mergeMode ? "Merging skills..." : "Copying skills...";
|
|
784
|
+
if (toInstall.skills === "all") {
|
|
785
|
+
await copyAllOfType("skills", source.claudeDir, targetDir2, mergeMode);
|
|
786
|
+
} else if (toInstall.skills.length > 0) {
|
|
787
|
+
await copyItems(toInstall.skills, "skills", source.claudeDir, targetDir2, mergeMode);
|
|
788
|
+
}
|
|
789
|
+
spinner.text = mergeMode ? "Merging commands..." : "Copying commands...";
|
|
790
|
+
if (toInstall.commands === "all") {
|
|
791
|
+
await copyAllOfType("commands", source.claudeDir, targetDir2, mergeMode);
|
|
792
|
+
} else if (toInstall.commands.length > 0) {
|
|
793
|
+
await copyItems(toInstall.commands, "commands", source.claudeDir, targetDir2, mergeMode);
|
|
794
|
+
}
|
|
795
|
+
spinner.text = mergeMode ? "Merging workflows..." : "Copying workflows...";
|
|
796
|
+
if (toInstall.workflows === "all") {
|
|
797
|
+
await copyAllOfType("workflows", source.claudeDir, targetDir2, mergeMode);
|
|
798
|
+
} else if (toInstall.workflows && toInstall.workflows.length > 0) {
|
|
799
|
+
await copyItems(toInstall.workflows, "workflows", source.claudeDir, targetDir2, mergeMode);
|
|
800
|
+
}
|
|
801
|
+
if (toInstall.includeRouter) {
|
|
802
|
+
spinner.text = mergeMode ? "Merging router..." : "Copying router...";
|
|
803
|
+
await copyRouter(source.claudeDir, targetDir2, mergeMode);
|
|
804
|
+
}
|
|
805
|
+
if (toInstall.includeHooks) {
|
|
806
|
+
spinner.text = mergeMode ? "Merging hooks..." : "Copying hooks...";
|
|
807
|
+
await copyHooks(source.claudeDir, targetDir2, mergeMode);
|
|
808
|
+
}
|
|
809
|
+
spinner.text = mergeMode ? "Merging base files..." : "Copying base files...";
|
|
810
|
+
await copyBaseFiles(source.claudeDir, targetDir2, mergeMode);
|
|
811
|
+
if (source.agentsMd) {
|
|
812
|
+
await copyAgentsMd(source.agentsMd, projectDir, mergeMode);
|
|
813
|
+
}
|
|
814
|
+
spinner.text = "Saving state...";
|
|
815
|
+
await createInitialState(projectDir, {
|
|
816
|
+
kit: kitName,
|
|
817
|
+
source: source.path,
|
|
818
|
+
target: TARGETS[target],
|
|
819
|
+
installed: {
|
|
820
|
+
agents: toInstall.agents === "all" ? ["all"] : toInstall.agents,
|
|
821
|
+
skills: toInstall.skills === "all" ? ["all"] : toInstall.skills,
|
|
822
|
+
commands: toInstall.commands === "all" ? ["all"] : toInstall.commands,
|
|
823
|
+
workflows: toInstall.workflows === "all" ? ["all"] : toInstall.workflows || [],
|
|
824
|
+
router: toInstall.includeRouter,
|
|
825
|
+
hooks: toInstall.includeHooks
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
const actionWord = mergeMode ? "merged" : existingAction === "override" ? "overridden" : "created";
|
|
829
|
+
spinner.succeed(pc2.green(`Project ${actionWord} successfully!`));
|
|
830
|
+
console.log("");
|
|
831
|
+
if (!isCurrentDir) {
|
|
832
|
+
console.log(pc2.cyan("Next steps:"));
|
|
833
|
+
console.log(pc2.white(` cd ${projectName}`));
|
|
834
|
+
console.log(pc2.white(" # Start coding with Claude Code"));
|
|
835
|
+
} else {
|
|
836
|
+
console.log(pc2.cyan("Ready to code with Claude Code!"));
|
|
837
|
+
}
|
|
838
|
+
console.log("");
|
|
839
|
+
console.log(pc2.gray("Useful commands:"));
|
|
840
|
+
console.log(pc2.gray(" ak status - Check file status"));
|
|
841
|
+
console.log(pc2.gray(" ak add <item> - Add more agents/skills"));
|
|
842
|
+
console.log(pc2.gray(" ak update - Sync from source"));
|
|
843
|
+
console.log("");
|
|
844
|
+
} catch (error) {
|
|
845
|
+
spinner.fail(pc2.red("Failed to create project"));
|
|
846
|
+
console.error(pc2.red(error.message));
|
|
847
|
+
if (process.env.DEBUG) {
|
|
848
|
+
console.error(error.stack);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
var init_init = __esm({
|
|
853
|
+
"src/commands/init.ts"() {
|
|
854
|
+
"use strict";
|
|
855
|
+
init_kits();
|
|
856
|
+
init_paths();
|
|
857
|
+
init_copy();
|
|
858
|
+
init_state();
|
|
859
|
+
init_prompts();
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
// src/commands/add.ts
|
|
864
|
+
var add_exports = {};
|
|
865
|
+
__export(add_exports, {
|
|
866
|
+
addCommand: () => addCommand
|
|
867
|
+
});
|
|
868
|
+
import fs5 from "fs-extra";
|
|
869
|
+
import { join as join6 } from "path";
|
|
870
|
+
import pc3 from "picocolors";
|
|
871
|
+
import ora2 from "ora";
|
|
872
|
+
async function addCommand(item, options = {}) {
|
|
873
|
+
const parts = item.split(":");
|
|
874
|
+
if (parts.length !== 2) {
|
|
875
|
+
console.log(pc3.red("Invalid format. Use: ak add <type>:<name>"));
|
|
876
|
+
console.log(pc3.gray("Examples:"));
|
|
877
|
+
console.log(pc3.gray(" ak add skill:databases"));
|
|
878
|
+
console.log(pc3.gray(" ak add agent:debugger"));
|
|
879
|
+
console.log(pc3.gray(" ak add command:fix/ci"));
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const [type, name] = parts;
|
|
883
|
+
const validTypes = ["agent", "agents", "skill", "skills", "command", "commands", "workflow", "workflows"];
|
|
884
|
+
if (!validTypes.includes(type)) {
|
|
885
|
+
console.log(pc3.red(`Invalid type: ${type}`));
|
|
886
|
+
console.log(pc3.gray(`Valid types: agent, skill, command, workflow`));
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const typeMap = {
|
|
890
|
+
agent: "agents",
|
|
891
|
+
agents: "agents",
|
|
892
|
+
skill: "skills",
|
|
893
|
+
skills: "skills",
|
|
894
|
+
command: "commands",
|
|
895
|
+
commands: "commands",
|
|
896
|
+
workflow: "workflows",
|
|
897
|
+
workflows: "workflows"
|
|
898
|
+
};
|
|
899
|
+
const normalizedType = typeMap[type];
|
|
900
|
+
const projectDir = options.path || process.cwd();
|
|
901
|
+
if (!isAkProject(projectDir)) {
|
|
902
|
+
console.log(pc3.red("Not in an ak project."));
|
|
903
|
+
console.log(pc3.gray('Run "ak init" first or use --path to specify project directory.'));
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const state = await loadState(projectDir);
|
|
907
|
+
if (!state) {
|
|
908
|
+
console.log(pc3.yellow("Warning: No state file found. Creating fresh state after add."));
|
|
909
|
+
}
|
|
910
|
+
const sourceFlag = options.source || (state ? state.source : null);
|
|
911
|
+
const source = resolveSource(sourceFlag);
|
|
912
|
+
if ("error" in source) {
|
|
913
|
+
console.log(pc3.red(`Error: ${source.error}`));
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const available = listAvailable(normalizedType, source.claudeDir);
|
|
917
|
+
const exists = available.some((a) => a.name === name);
|
|
918
|
+
if (!exists) {
|
|
919
|
+
console.log(pc3.red(`${type} "${name}" not found in source.`));
|
|
920
|
+
console.log(pc3.gray(`Use "ak list ${normalizedType}" to see available options.`));
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const targetFolder = state?.target || ".claude";
|
|
924
|
+
const targetDir = join6(projectDir, targetFolder);
|
|
925
|
+
const destPath = join6(targetDir, normalizedType, name);
|
|
926
|
+
const destPathMd = destPath + ".md";
|
|
927
|
+
if (fs5.existsSync(destPath) || fs5.existsSync(destPathMd)) {
|
|
928
|
+
console.log(pc3.yellow(`${type} "${name}" already exists in project.`));
|
|
929
|
+
console.log(pc3.gray('Use "ak update" to refresh from source.'));
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const spinner = ora2(`Adding ${type} "${name}"...`).start();
|
|
933
|
+
try {
|
|
934
|
+
const result = await copyItems([name], normalizedType, source.claudeDir, targetDir);
|
|
935
|
+
if (result.copied.length > 0) {
|
|
936
|
+
spinner.succeed(pc3.green(`Added ${type}: ${name}`));
|
|
937
|
+
if (state) {
|
|
938
|
+
const installed = state.installed || {};
|
|
939
|
+
const typeArray = installed[normalizedType];
|
|
940
|
+
if (!typeArray) {
|
|
941
|
+
installed[normalizedType] = [name];
|
|
942
|
+
} else if (Array.isArray(typeArray) && !typeArray.includes(name)) {
|
|
943
|
+
typeArray.push(name);
|
|
944
|
+
}
|
|
945
|
+
const newHashes = await hashDirectory(targetDir);
|
|
946
|
+
await updateState(projectDir, {
|
|
947
|
+
installed,
|
|
948
|
+
originalHashes: newHashes
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
console.log(pc3.gray(`Location: ${targetFolder}/${normalizedType}/${name}`));
|
|
952
|
+
} else if (result.skipped.length > 0) {
|
|
953
|
+
spinner.fail(pc3.red(`Could not find ${type}: ${name}`));
|
|
954
|
+
} else if (result.errors.length > 0) {
|
|
955
|
+
spinner.fail(pc3.red(`Error adding ${type}: ${result.errors[0].error}`));
|
|
956
|
+
}
|
|
957
|
+
} catch (error) {
|
|
958
|
+
spinner.fail(pc3.red(`Failed to add ${type}`));
|
|
959
|
+
console.error(pc3.red(error.message));
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
var init_add = __esm({
|
|
963
|
+
"src/commands/add.ts"() {
|
|
964
|
+
"use strict";
|
|
965
|
+
init_paths();
|
|
966
|
+
init_copy();
|
|
967
|
+
init_state();
|
|
968
|
+
init_hash();
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// src/commands/list.ts
|
|
973
|
+
var list_exports = {};
|
|
974
|
+
__export(list_exports, {
|
|
975
|
+
listCommand: () => listCommand
|
|
976
|
+
});
|
|
977
|
+
import pc4 from "picocolors";
|
|
978
|
+
async function listCommand(type, options = {}) {
|
|
979
|
+
const validTypes = ["kits", "agents", "skills", "commands", "workflows"];
|
|
980
|
+
if (!type) {
|
|
981
|
+
console.log(pc4.cyan("\nAvailable list commands:"));
|
|
982
|
+
console.log(pc4.white(" ak list kits - List available kits"));
|
|
983
|
+
console.log(pc4.white(" ak list agents - List available agents"));
|
|
984
|
+
console.log(pc4.white(" ak list skills - List available skills"));
|
|
985
|
+
console.log(pc4.white(" ak list commands - List available commands"));
|
|
986
|
+
console.log(pc4.white(" ak list workflows - List available workflows"));
|
|
987
|
+
console.log("");
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
type = type.toLowerCase();
|
|
991
|
+
if (!validTypes.includes(type)) {
|
|
992
|
+
console.log(pc4.red(`Unknown type: ${type}`));
|
|
993
|
+
console.log(pc4.gray(`Valid types: ${validTypes.join(", ")}`));
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (type === "kits") {
|
|
997
|
+
listKits();
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const source = resolveSource(options.source);
|
|
1001
|
+
if ("error" in source) {
|
|
1002
|
+
console.log(pc4.red(`Error: ${source.error}`));
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
console.log(pc4.gray(`Source: ${source.path}
|
|
1006
|
+
`));
|
|
1007
|
+
switch (type) {
|
|
1008
|
+
case "agents":
|
|
1009
|
+
listAgents(source.claudeDir);
|
|
1010
|
+
break;
|
|
1011
|
+
case "skills":
|
|
1012
|
+
listSkills(source.claudeDir);
|
|
1013
|
+
break;
|
|
1014
|
+
case "commands":
|
|
1015
|
+
listCommandsList(source.claudeDir);
|
|
1016
|
+
break;
|
|
1017
|
+
case "workflows":
|
|
1018
|
+
listWorkflows(source.claudeDir);
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
function listKits() {
|
|
1023
|
+
const kits = getKitList();
|
|
1024
|
+
console.log(pc4.bold(pc4.cyan("\nAvailable Kits:\n")));
|
|
1025
|
+
for (const kit of kits) {
|
|
1026
|
+
const colorFn = pc4[kit.color] || pc4.white;
|
|
1027
|
+
console.log(` ${kit.emoji} ${colorFn(pc4.bold(kit.name.padEnd(12)))} - ${kit.description}`);
|
|
1028
|
+
if (Array.isArray(kit.agents)) {
|
|
1029
|
+
const agentCount = Array.isArray(kit.agents) ? kit.agents.length : "all";
|
|
1030
|
+
const skillCount = Array.isArray(kit.skills) ? kit.skills.length : "all";
|
|
1031
|
+
const cmdCount = Array.isArray(kit.commands) ? kit.commands.length : "all";
|
|
1032
|
+
console.log(pc4.gray(` Agents: ${agentCount} | Skills: ${skillCount} | Commands: ${cmdCount}`));
|
|
1033
|
+
} else {
|
|
1034
|
+
console.log(pc4.gray(" Includes: ALL agents, skills, commands"));
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
console.log(`
|
|
1038
|
+
\u{1F527} ${pc4.bold("custom".padEnd(12))} - Pick your own agents, skills, and commands`);
|
|
1039
|
+
console.log("");
|
|
1040
|
+
}
|
|
1041
|
+
function listAgents(sourceDir) {
|
|
1042
|
+
const agents = listAvailable("agents", sourceDir);
|
|
1043
|
+
console.log(pc4.bold(pc4.cyan(`Available Agents (${agents.length}):
|
|
1044
|
+
`)));
|
|
1045
|
+
if (agents.length === 0) {
|
|
1046
|
+
console.log(pc4.gray(" No agents found"));
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const cols = 3;
|
|
1050
|
+
const rows = Math.ceil(agents.length / cols);
|
|
1051
|
+
for (let i = 0; i < rows; i++) {
|
|
1052
|
+
let line = " ";
|
|
1053
|
+
for (let j = 0; j < cols; j++) {
|
|
1054
|
+
const idx = i + j * rows;
|
|
1055
|
+
if (idx < agents.length) {
|
|
1056
|
+
line += pc4.white(agents[idx].name.padEnd(25));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
console.log(line);
|
|
1060
|
+
}
|
|
1061
|
+
console.log("");
|
|
1062
|
+
}
|
|
1063
|
+
function listSkills(sourceDir) {
|
|
1064
|
+
const skills = listAvailable("skills", sourceDir).filter((s) => s.isDir);
|
|
1065
|
+
console.log(pc4.bold(pc4.cyan(`Available Skills (${skills.length}):
|
|
1066
|
+
`)));
|
|
1067
|
+
if (skills.length === 0) {
|
|
1068
|
+
console.log(pc4.gray(" No skills found"));
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const cols = 2;
|
|
1072
|
+
const rows = Math.ceil(skills.length / cols);
|
|
1073
|
+
for (let i = 0; i < rows; i++) {
|
|
1074
|
+
let line = " ";
|
|
1075
|
+
for (let j = 0; j < cols; j++) {
|
|
1076
|
+
const idx = i + j * rows;
|
|
1077
|
+
if (idx < skills.length) {
|
|
1078
|
+
line += pc4.white(skills[idx].name.padEnd(35));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
console.log(line);
|
|
1082
|
+
}
|
|
1083
|
+
console.log("");
|
|
1084
|
+
}
|
|
1085
|
+
function listCommandsList(sourceDir) {
|
|
1086
|
+
const commands = listAvailable("commands", sourceDir);
|
|
1087
|
+
console.log(pc4.bold(pc4.cyan(`Available Commands (${commands.length}):
|
|
1088
|
+
`)));
|
|
1089
|
+
if (commands.length === 0) {
|
|
1090
|
+
console.log(pc4.gray(" No commands found"));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const files = commands.filter((c) => !c.isDir);
|
|
1094
|
+
const dirs = commands.filter((c) => c.isDir);
|
|
1095
|
+
console.log(pc4.gray(" Main commands:"));
|
|
1096
|
+
const cols = 4;
|
|
1097
|
+
const rows = Math.ceil(files.length / cols);
|
|
1098
|
+
for (let i = 0; i < rows; i++) {
|
|
1099
|
+
let line = " ";
|
|
1100
|
+
for (let j = 0; j < cols; j++) {
|
|
1101
|
+
const idx = i + j * rows;
|
|
1102
|
+
if (idx < files.length) {
|
|
1103
|
+
line += pc4.white(("/" + files[idx].name).padEnd(18));
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
console.log(line);
|
|
1107
|
+
}
|
|
1108
|
+
if (dirs.length > 0) {
|
|
1109
|
+
console.log(pc4.gray("\n Command groups:"));
|
|
1110
|
+
for (const dir of dirs) {
|
|
1111
|
+
console.log(pc4.yellow(` /${dir.name}/`));
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
console.log("");
|
|
1115
|
+
}
|
|
1116
|
+
function listWorkflows(sourceDir) {
|
|
1117
|
+
const workflows = listAvailable("workflows", sourceDir);
|
|
1118
|
+
console.log(pc4.bold(pc4.cyan(`Available Workflows (${workflows.length}):
|
|
1119
|
+
`)));
|
|
1120
|
+
if (workflows.length === 0) {
|
|
1121
|
+
console.log(pc4.gray(" No workflows found"));
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
for (const wf of workflows) {
|
|
1125
|
+
console.log(pc4.white(` \u2022 ${wf.name}`));
|
|
1126
|
+
}
|
|
1127
|
+
console.log("");
|
|
1128
|
+
}
|
|
1129
|
+
var init_list = __esm({
|
|
1130
|
+
"src/commands/list.ts"() {
|
|
1131
|
+
"use strict";
|
|
1132
|
+
init_kits();
|
|
1133
|
+
init_paths();
|
|
1134
|
+
init_copy();
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
// src/commands/update.ts
|
|
1139
|
+
var update_exports = {};
|
|
1140
|
+
__export(update_exports, {
|
|
1141
|
+
updateCommand: () => updateCommand
|
|
1142
|
+
});
|
|
1143
|
+
import fs6 from "fs-extra";
|
|
1144
|
+
import { join as join7 } from "path";
|
|
1145
|
+
import pc5 from "picocolors";
|
|
1146
|
+
import ora3 from "ora";
|
|
1147
|
+
async function updateCommand(options = {}) {
|
|
1148
|
+
const projectDir = process.cwd();
|
|
1149
|
+
if (!isAkProject(projectDir)) {
|
|
1150
|
+
console.log(pc5.red("Not in an ak project."));
|
|
1151
|
+
console.log(pc5.gray('Run "ak init" first.'));
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const state = await loadState(projectDir);
|
|
1155
|
+
if (!state) {
|
|
1156
|
+
console.log(pc5.red("No state file found."));
|
|
1157
|
+
console.log(pc5.gray('This project may have been created without ak. Run "ak doctor" for more info.'));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const sourceFlag = options.source || state.source;
|
|
1161
|
+
const source = resolveSource(sourceFlag);
|
|
1162
|
+
if ("error" in source) {
|
|
1163
|
+
console.log(pc5.red(`Error: ${source.error}`));
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
console.log(pc5.gray(`Source: ${source.path}`));
|
|
1167
|
+
console.log(pc5.gray(`Target: ${state.target}`));
|
|
1168
|
+
console.log("");
|
|
1169
|
+
const spinner = ora3("Checking for updates...").start();
|
|
1170
|
+
try {
|
|
1171
|
+
const result = await getFileStatuses(projectDir);
|
|
1172
|
+
if ("error" in result) {
|
|
1173
|
+
spinner.fail(pc5.red(result.error));
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const { statuses, targetDir } = result;
|
|
1177
|
+
let typesToUpdate = ["agents", "skills", "commands", "workflows"];
|
|
1178
|
+
if (options.agents) typesToUpdate = ["agents"];
|
|
1179
|
+
if (options.skills) typesToUpdate = ["skills"];
|
|
1180
|
+
if (options.commands) typesToUpdate = ["commands"];
|
|
1181
|
+
const sourceHashes = {};
|
|
1182
|
+
for (const type of typesToUpdate) {
|
|
1183
|
+
const typeDir = join7(source.claudeDir, type);
|
|
1184
|
+
if (fs6.existsSync(typeDir)) {
|
|
1185
|
+
const hashes = await hashDirectory(typeDir);
|
|
1186
|
+
for (const [path, hash] of Object.entries(hashes)) {
|
|
1187
|
+
sourceHashes[`${type}/${path}`] = hash;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const toUpdate = [];
|
|
1192
|
+
const skipped = [];
|
|
1193
|
+
const newFiles = [];
|
|
1194
|
+
for (const [path, sourceHash] of Object.entries(sourceHashes)) {
|
|
1195
|
+
const currentPath = join7(targetDir, path);
|
|
1196
|
+
const originalHash = state.originalHashes?.[path];
|
|
1197
|
+
const currentHash = fs6.existsSync(currentPath) ? hashFile(currentPath) : null;
|
|
1198
|
+
if (!currentHash) {
|
|
1199
|
+
newFiles.push(path);
|
|
1200
|
+
} else if (currentHash === originalHash) {
|
|
1201
|
+
if (sourceHash !== currentHash) {
|
|
1202
|
+
toUpdate.push(path);
|
|
1203
|
+
}
|
|
1204
|
+
} else {
|
|
1205
|
+
if (sourceHash !== originalHash) {
|
|
1206
|
+
skipped.push(path);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
spinner.stop();
|
|
1211
|
+
if (toUpdate.length === 0 && newFiles.length === 0) {
|
|
1212
|
+
console.log(pc5.green("\u2713 Already up to date!"));
|
|
1213
|
+
if (skipped.length > 0) {
|
|
1214
|
+
console.log(pc5.yellow(` ${skipped.length} file(s) skipped (modified locally)`));
|
|
1215
|
+
}
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
console.log(pc5.cyan("Updates available:"));
|
|
1219
|
+
console.log(pc5.green(` ${toUpdate.length} file(s) to update`));
|
|
1220
|
+
console.log(pc5.blue(` ${newFiles.length} new file(s)`));
|
|
1221
|
+
if (skipped.length > 0) {
|
|
1222
|
+
console.log(pc5.yellow(` ${skipped.length} file(s) skipped (modified locally)`));
|
|
1223
|
+
}
|
|
1224
|
+
console.log("");
|
|
1225
|
+
if (options.dryRun) {
|
|
1226
|
+
console.log(pc5.cyan("Dry run - no changes made"));
|
|
1227
|
+
console.log(pc5.gray("\nFiles that would be updated:"));
|
|
1228
|
+
[...toUpdate, ...newFiles].forEach((f) => console.log(pc5.gray(` ${f}`)));
|
|
1229
|
+
if (skipped.length > 0) {
|
|
1230
|
+
console.log(pc5.gray("\nFiles that would be skipped:"));
|
|
1231
|
+
skipped.forEach((f) => console.log(pc5.yellow(` ~ ${f}`)));
|
|
1232
|
+
}
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
if (!options.force) {
|
|
1236
|
+
const confirmed = await promptUpdateConfirm({
|
|
1237
|
+
toUpdate: [...toUpdate, ...newFiles],
|
|
1238
|
+
skipped
|
|
1239
|
+
});
|
|
1240
|
+
if (!confirmed) {
|
|
1241
|
+
console.log(pc5.yellow("Cancelled."));
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const updateSpinner = ora3("Applying updates...").start();
|
|
1246
|
+
let updated = 0;
|
|
1247
|
+
let failed = 0;
|
|
1248
|
+
for (const path of [...toUpdate, ...newFiles]) {
|
|
1249
|
+
try {
|
|
1250
|
+
const srcPath = join7(source.claudeDir, path);
|
|
1251
|
+
const destPath = join7(targetDir, path);
|
|
1252
|
+
await fs6.ensureDir(join7(targetDir, path.split("/").slice(0, -1).join("/")));
|
|
1253
|
+
await fs6.copy(srcPath, destPath, { overwrite: true });
|
|
1254
|
+
updated++;
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
failed++;
|
|
1257
|
+
if (process.env.DEBUG) {
|
|
1258
|
+
console.error(`Failed to update ${path}: ${err.message}`);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const newHashes = await hashDirectory(targetDir);
|
|
1263
|
+
await updateState(projectDir, {
|
|
1264
|
+
source: source.path,
|
|
1265
|
+
originalHashes: newHashes
|
|
1266
|
+
});
|
|
1267
|
+
updateSpinner.succeed(pc5.green(`Updated ${updated} file(s)`));
|
|
1268
|
+
if (failed > 0) {
|
|
1269
|
+
console.log(pc5.yellow(` ${failed} file(s) failed to update`));
|
|
1270
|
+
}
|
|
1271
|
+
if (skipped.length > 0) {
|
|
1272
|
+
console.log(pc5.gray(`
|
|
1273
|
+
Skipped files (modified locally):`));
|
|
1274
|
+
skipped.slice(0, 5).forEach((f) => console.log(pc5.yellow(` ~ ${f}`)));
|
|
1275
|
+
if (skipped.length > 5) {
|
|
1276
|
+
console.log(pc5.gray(` ... and ${skipped.length - 5} more`));
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
spinner.fail(pc5.red("Update failed"));
|
|
1281
|
+
console.error(pc5.red(error.message));
|
|
1282
|
+
if (process.env.DEBUG) {
|
|
1283
|
+
console.error(error.stack);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
var init_update = __esm({
|
|
1288
|
+
"src/commands/update.ts"() {
|
|
1289
|
+
"use strict";
|
|
1290
|
+
init_paths();
|
|
1291
|
+
init_state();
|
|
1292
|
+
init_hash();
|
|
1293
|
+
init_prompts();
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
// src/commands/status.ts
|
|
1298
|
+
var status_exports = {};
|
|
1299
|
+
__export(status_exports, {
|
|
1300
|
+
statusCommand: () => statusCommand
|
|
1301
|
+
});
|
|
1302
|
+
import pc6 from "picocolors";
|
|
1303
|
+
import fs7 from "fs-extra";
|
|
1304
|
+
async function statusCommand(options = {}) {
|
|
1305
|
+
const projectDir = process.cwd();
|
|
1306
|
+
if (!isAkProject(projectDir)) {
|
|
1307
|
+
console.log(pc6.red("Not in an ak project."));
|
|
1308
|
+
console.log(pc6.gray('Run "ak init" first.'));
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const state = await loadState(projectDir);
|
|
1312
|
+
if (!state) {
|
|
1313
|
+
console.log(pc6.yellow("No state file found."));
|
|
1314
|
+
console.log(pc6.gray("This project may have been created without ak."));
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
console.log(pc6.bold(pc6.cyan("\nProject Status\n")));
|
|
1318
|
+
console.log(pc6.white(` Kit: ${state.kit}`));
|
|
1319
|
+
console.log(pc6.white(` Target: ${state.target}`));
|
|
1320
|
+
console.log(pc6.white(` Source: ${state.source}`));
|
|
1321
|
+
console.log(pc6.gray(` Created: ${new Date(state.createdAt).toLocaleDateString()}`));
|
|
1322
|
+
console.log(pc6.gray(` Updated: ${new Date(state.lastUpdate).toLocaleDateString()}`));
|
|
1323
|
+
console.log("");
|
|
1324
|
+
const result = await getFileStatuses(projectDir);
|
|
1325
|
+
if ("error" in result) {
|
|
1326
|
+
console.log(pc6.red(`Error: ${result.error}`));
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
const { statuses } = result;
|
|
1330
|
+
const total = statuses.unchanged.length + statuses.modified.length + statuses.added.length;
|
|
1331
|
+
console.log(pc6.cyan("File Status:"));
|
|
1332
|
+
console.log(pc6.green(` \u2713 ${statuses.unchanged.length} unchanged`));
|
|
1333
|
+
if (statuses.modified.length > 0) {
|
|
1334
|
+
console.log(pc6.yellow(` ~ ${statuses.modified.length} modified`));
|
|
1335
|
+
}
|
|
1336
|
+
if (statuses.added.length > 0) {
|
|
1337
|
+
console.log(pc6.blue(` + ${statuses.added.length} added locally`));
|
|
1338
|
+
}
|
|
1339
|
+
if (statuses.deleted.length > 0) {
|
|
1340
|
+
console.log(pc6.red(` - ${statuses.deleted.length} deleted`));
|
|
1341
|
+
}
|
|
1342
|
+
console.log(pc6.gray(` Total: ${total} files tracked`));
|
|
1343
|
+
console.log("");
|
|
1344
|
+
if (statuses.modified.length > 0 && (options.verbose || statuses.modified.length <= 10)) {
|
|
1345
|
+
console.log(pc6.yellow("Modified files:"));
|
|
1346
|
+
for (const file of statuses.modified) {
|
|
1347
|
+
console.log(pc6.yellow(` ~ ${file}`));
|
|
1348
|
+
}
|
|
1349
|
+
console.log("");
|
|
1350
|
+
} else if (statuses.modified.length > 10) {
|
|
1351
|
+
console.log(pc6.yellow(`Modified files: (showing first 10, use --verbose for all)`));
|
|
1352
|
+
for (const file of statuses.modified.slice(0, 10)) {
|
|
1353
|
+
console.log(pc6.yellow(` ~ ${file}`));
|
|
1354
|
+
}
|
|
1355
|
+
console.log(pc6.gray(` ... and ${statuses.modified.length - 10} more`));
|
|
1356
|
+
console.log("");
|
|
1357
|
+
}
|
|
1358
|
+
if (statuses.added.length > 0 && (options.verbose || statuses.added.length <= 5)) {
|
|
1359
|
+
console.log(pc6.blue("Added locally:"));
|
|
1360
|
+
for (const file of statuses.added) {
|
|
1361
|
+
console.log(pc6.blue(` + ${file}`));
|
|
1362
|
+
}
|
|
1363
|
+
console.log("");
|
|
1364
|
+
}
|
|
1365
|
+
if (state.installed) {
|
|
1366
|
+
console.log(pc6.cyan("Installed Components:"));
|
|
1367
|
+
const { agents, skills, commands, workflows, router, hooks } = state.installed;
|
|
1368
|
+
if (agents && agents.length > 0) {
|
|
1369
|
+
console.log(pc6.gray(` Agents: ${agents.includes("all") ? "ALL" : agents.length}`));
|
|
1370
|
+
}
|
|
1371
|
+
if (skills && skills.length > 0) {
|
|
1372
|
+
console.log(pc6.gray(` Skills: ${skills.includes("all") ? "ALL" : skills.length}`));
|
|
1373
|
+
}
|
|
1374
|
+
if (commands && commands.length > 0) {
|
|
1375
|
+
console.log(pc6.gray(` Commands: ${commands.includes("all") ? "ALL" : commands.length}`));
|
|
1376
|
+
}
|
|
1377
|
+
if (workflows && workflows.length > 0) {
|
|
1378
|
+
console.log(pc6.gray(` Workflows: ${workflows.includes("all") ? "ALL" : workflows.length}`));
|
|
1379
|
+
}
|
|
1380
|
+
if (router) console.log(pc6.gray(" Router: \u2713"));
|
|
1381
|
+
if (hooks) console.log(pc6.gray(" Hooks: \u2713"));
|
|
1382
|
+
console.log("");
|
|
1383
|
+
}
|
|
1384
|
+
if (state.source && !fs7.existsSync(state.source)) {
|
|
1385
|
+
console.log(pc6.yellow("\u26A0 Source directory not found. Update may not work."));
|
|
1386
|
+
console.log(pc6.gray(` Expected: ${state.source}`));
|
|
1387
|
+
console.log("");
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
var init_status = __esm({
|
|
1391
|
+
"src/commands/status.ts"() {
|
|
1392
|
+
"use strict";
|
|
1393
|
+
init_paths();
|
|
1394
|
+
init_state();
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
// src/commands/doctor.ts
|
|
1399
|
+
var doctor_exports = {};
|
|
1400
|
+
__export(doctor_exports, {
|
|
1401
|
+
doctorCommand: () => doctorCommand
|
|
1402
|
+
});
|
|
1403
|
+
import fs8 from "fs-extra";
|
|
1404
|
+
import { join as join8 } from "path";
|
|
1405
|
+
import pc7 from "picocolors";
|
|
1406
|
+
async function doctorCommand(options = {}) {
|
|
1407
|
+
const projectDir = process.cwd();
|
|
1408
|
+
let targetDir = null;
|
|
1409
|
+
for (const [name, folder] of Object.entries(TARGETS)) {
|
|
1410
|
+
const dir = join8(projectDir, folder);
|
|
1411
|
+
if (fs8.existsSync(dir)) {
|
|
1412
|
+
targetDir = { name, folder, dir };
|
|
1413
|
+
break;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
let state = await loadState(projectDir);
|
|
1417
|
+
const checks = [
|
|
1418
|
+
{
|
|
1419
|
+
name: "project",
|
|
1420
|
+
label: "ak project detected",
|
|
1421
|
+
severity: "error",
|
|
1422
|
+
check: () => isAkProject(projectDir),
|
|
1423
|
+
fix: async () => {
|
|
1424
|
+
await fs8.ensureDir(join8(projectDir, ".ak"));
|
|
1425
|
+
}
|
|
1426
|
+
},
|
|
1427
|
+
{
|
|
1428
|
+
name: "state",
|
|
1429
|
+
label: "State file (.ak/state.json) exists",
|
|
1430
|
+
severity: "warning",
|
|
1431
|
+
check: () => state !== null,
|
|
1432
|
+
fix: async () => {
|
|
1433
|
+
const kit = "unknown";
|
|
1434
|
+
const source = "";
|
|
1435
|
+
const target = targetDir ? targetDir.folder : ".claude";
|
|
1436
|
+
const installed = {};
|
|
1437
|
+
if (targetDir) {
|
|
1438
|
+
const dirs = ["agents", "skills", "commands", "workflows"];
|
|
1439
|
+
for (const dir of dirs) {
|
|
1440
|
+
const fullPath = join8(targetDir.dir, dir);
|
|
1441
|
+
if (fs8.existsSync(fullPath)) {
|
|
1442
|
+
const items = fs8.readdirSync(fullPath).filter((f) => !f.startsWith("."));
|
|
1443
|
+
if (items.length > 0) {
|
|
1444
|
+
installed[dir] = items;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
await createInitialState(projectDir, { kit, source, target, installed });
|
|
1450
|
+
state = await loadState(projectDir);
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
name: "target",
|
|
1455
|
+
label: "Target directory exists",
|
|
1456
|
+
severity: "error",
|
|
1457
|
+
check: () => targetDir !== null,
|
|
1458
|
+
getMeta: () => targetDir ? { folder: targetDir.folder } : {}
|
|
1459
|
+
},
|
|
1460
|
+
{
|
|
1461
|
+
name: "agents_md",
|
|
1462
|
+
label: "AGENTS.md exists",
|
|
1463
|
+
severity: "warning",
|
|
1464
|
+
check: () => fs8.existsSync(join8(projectDir, "AGENTS.md"))
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
name: "source",
|
|
1468
|
+
label: "Source directory accessible",
|
|
1469
|
+
severity: "error",
|
|
1470
|
+
check: () => {
|
|
1471
|
+
if (!state || !state.source) return true;
|
|
1472
|
+
return fs8.existsSync(state.source);
|
|
1473
|
+
},
|
|
1474
|
+
getMeta: () => state && state.source ? { source: state.source } : {}
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
name: "subdirs_agents",
|
|
1478
|
+
label: "agents/ exists",
|
|
1479
|
+
severity: "warning",
|
|
1480
|
+
check: () => {
|
|
1481
|
+
if (!targetDir) return false;
|
|
1482
|
+
return fs8.existsSync(join8(targetDir.dir, "agents"));
|
|
1483
|
+
},
|
|
1484
|
+
fix: async () => {
|
|
1485
|
+
if (targetDir) {
|
|
1486
|
+
await fs8.ensureDir(join8(targetDir.dir, "agents"));
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1489
|
+
getMeta: () => {
|
|
1490
|
+
if (!targetDir) return {};
|
|
1491
|
+
const fullPath = join8(targetDir.dir, "agents");
|
|
1492
|
+
if (fs8.existsSync(fullPath)) {
|
|
1493
|
+
const items = fs8.readdirSync(fullPath).length;
|
|
1494
|
+
return { items };
|
|
1495
|
+
}
|
|
1496
|
+
return {};
|
|
1497
|
+
}
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
name: "subdirs_commands",
|
|
1501
|
+
label: "commands/ exists",
|
|
1502
|
+
severity: "warning",
|
|
1503
|
+
check: () => {
|
|
1504
|
+
if (!targetDir) return false;
|
|
1505
|
+
return fs8.existsSync(join8(targetDir.dir, "commands"));
|
|
1506
|
+
},
|
|
1507
|
+
fix: async () => {
|
|
1508
|
+
if (targetDir) {
|
|
1509
|
+
await fs8.ensureDir(join8(targetDir.dir, "commands"));
|
|
1510
|
+
}
|
|
1511
|
+
},
|
|
1512
|
+
getMeta: () => {
|
|
1513
|
+
if (!targetDir) return {};
|
|
1514
|
+
const fullPath = join8(targetDir.dir, "commands");
|
|
1515
|
+
if (fs8.existsSync(fullPath)) {
|
|
1516
|
+
const items = fs8.readdirSync(fullPath).length;
|
|
1517
|
+
return { items };
|
|
1518
|
+
}
|
|
1519
|
+
return {};
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
name: "subdirs_skills",
|
|
1524
|
+
label: "skills/ exists",
|
|
1525
|
+
severity: "warning",
|
|
1526
|
+
check: () => {
|
|
1527
|
+
if (!targetDir) return false;
|
|
1528
|
+
return fs8.existsSync(join8(targetDir.dir, "skills"));
|
|
1529
|
+
},
|
|
1530
|
+
fix: async () => {
|
|
1531
|
+
if (targetDir) {
|
|
1532
|
+
await fs8.ensureDir(join8(targetDir.dir, "skills"));
|
|
1533
|
+
}
|
|
1534
|
+
},
|
|
1535
|
+
getMeta: () => {
|
|
1536
|
+
if (!targetDir) return {};
|
|
1537
|
+
const fullPath = join8(targetDir.dir, "skills");
|
|
1538
|
+
if (fs8.existsSync(fullPath)) {
|
|
1539
|
+
const items = fs8.readdirSync(fullPath).length;
|
|
1540
|
+
return { items };
|
|
1541
|
+
}
|
|
1542
|
+
return {};
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
];
|
|
1546
|
+
const results = await runChecks(checks, options);
|
|
1547
|
+
if (options.json) {
|
|
1548
|
+
await outputJson(projectDir, results);
|
|
1549
|
+
return computeSummary(results);
|
|
1550
|
+
}
|
|
1551
|
+
if (options.report) {
|
|
1552
|
+
await outputReport(projectDir, results, options);
|
|
1553
|
+
}
|
|
1554
|
+
if (options.checkOnly) {
|
|
1555
|
+
const summary = computeSummary(results);
|
|
1556
|
+
if (summary.issues > 0) {
|
|
1557
|
+
process.exit(1);
|
|
1558
|
+
} else {
|
|
1559
|
+
process.exit(0);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
await outputColored(projectDir, results, state, targetDir);
|
|
1563
|
+
return computeSummary(results);
|
|
1564
|
+
}
|
|
1565
|
+
async function runChecks(checks, options) {
|
|
1566
|
+
const results = [];
|
|
1567
|
+
for (const c of checks) {
|
|
1568
|
+
const passed = await c.check();
|
|
1569
|
+
const meta = c.getMeta ? await c.getMeta() : void 0;
|
|
1570
|
+
const result = {
|
|
1571
|
+
name: c.name,
|
|
1572
|
+
label: c.label,
|
|
1573
|
+
passed,
|
|
1574
|
+
severity: c.severity,
|
|
1575
|
+
meta
|
|
1576
|
+
};
|
|
1577
|
+
if (!passed && options.fix && c.fix) {
|
|
1578
|
+
try {
|
|
1579
|
+
await c.fix();
|
|
1580
|
+
result.fixed = true;
|
|
1581
|
+
result.passed = true;
|
|
1582
|
+
} catch (e) {
|
|
1583
|
+
result.fixError = e.message;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
results.push(result);
|
|
1587
|
+
}
|
|
1588
|
+
return results;
|
|
1589
|
+
}
|
|
1590
|
+
function computeSummary(results) {
|
|
1591
|
+
let issues = 0;
|
|
1592
|
+
let warnings = 0;
|
|
1593
|
+
for (const r of results) {
|
|
1594
|
+
if (!r.passed && r.severity === "error") {
|
|
1595
|
+
issues++;
|
|
1596
|
+
}
|
|
1597
|
+
if (!r.passed && r.severity === "warning") {
|
|
1598
|
+
warnings++;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return { issues, warnings };
|
|
1602
|
+
}
|
|
1603
|
+
async function outputJson(projectDir, results) {
|
|
1604
|
+
const summary = computeSummary(results);
|
|
1605
|
+
const fixed = results.filter((r) => r.fixed).length;
|
|
1606
|
+
const output = {
|
|
1607
|
+
project: projectDir,
|
|
1608
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1609
|
+
checks: results,
|
|
1610
|
+
summary: {
|
|
1611
|
+
total: results.length,
|
|
1612
|
+
passed: results.filter((r) => r.passed).length,
|
|
1613
|
+
failed: results.filter((r) => !r.passed).length,
|
|
1614
|
+
warnings: summary.warnings,
|
|
1615
|
+
fixed
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1619
|
+
}
|
|
1620
|
+
async function outputReport(projectDir, results, options) {
|
|
1621
|
+
const summary = computeSummary(results);
|
|
1622
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1623
|
+
let markdown = `# Apero Kit Doctor Report
|
|
1624
|
+
|
|
1625
|
+
`;
|
|
1626
|
+
markdown += `**Project:** ${projectDir}
|
|
1627
|
+
`;
|
|
1628
|
+
markdown += `**Timestamp:** ${timestamp}
|
|
1629
|
+
|
|
1630
|
+
`;
|
|
1631
|
+
markdown += `## Summary
|
|
1632
|
+
|
|
1633
|
+
`;
|
|
1634
|
+
markdown += `- Total checks: ${results.length}
|
|
1635
|
+
`;
|
|
1636
|
+
markdown += `- Passed: ${results.filter((r) => r.passed).length}
|
|
1637
|
+
`;
|
|
1638
|
+
markdown += `- Failed: ${results.filter((r) => !r.passed).length}
|
|
1639
|
+
`;
|
|
1640
|
+
markdown += `- Errors: ${summary.issues}
|
|
1641
|
+
`;
|
|
1642
|
+
markdown += `- Warnings: ${summary.warnings}
|
|
1643
|
+
`;
|
|
1644
|
+
if (options.fix) {
|
|
1645
|
+
markdown += `- Fixed: ${results.filter((r) => r.fixed).length}
|
|
1646
|
+
`;
|
|
1647
|
+
}
|
|
1648
|
+
markdown += `
|
|
1649
|
+
`;
|
|
1650
|
+
markdown += `## Check Results
|
|
1651
|
+
|
|
1652
|
+
`;
|
|
1653
|
+
for (const r of results) {
|
|
1654
|
+
const icon = r.passed ? "\u2713" : r.severity === "error" ? "\u2717" : "\u26A0";
|
|
1655
|
+
markdown += `### ${icon} ${r.label}
|
|
1656
|
+
|
|
1657
|
+
`;
|
|
1658
|
+
markdown += `- **Status:** ${r.passed ? "PASS" : "FAIL"}
|
|
1659
|
+
`;
|
|
1660
|
+
markdown += `- **Severity:** ${r.severity}
|
|
1661
|
+
`;
|
|
1662
|
+
if (r.fixed) {
|
|
1663
|
+
markdown += `- **Fixed:** Yes
|
|
1664
|
+
`;
|
|
1665
|
+
}
|
|
1666
|
+
if (r.fixError) {
|
|
1667
|
+
markdown += `- **Fix Error:** ${r.fixError}
|
|
1668
|
+
`;
|
|
1669
|
+
}
|
|
1670
|
+
if (r.meta && Object.keys(r.meta).length > 0) {
|
|
1671
|
+
markdown += `- **Details:** ${JSON.stringify(r.meta)}
|
|
1672
|
+
`;
|
|
1673
|
+
}
|
|
1674
|
+
markdown += `
|
|
1675
|
+
`;
|
|
1676
|
+
}
|
|
1677
|
+
markdown += `## Suggestions
|
|
1678
|
+
|
|
1679
|
+
`;
|
|
1680
|
+
const failedChecks = results.filter((r) => !r.passed);
|
|
1681
|
+
if (failedChecks.length === 0) {
|
|
1682
|
+
markdown += `All checks passed! No suggestions.
|
|
1683
|
+
`;
|
|
1684
|
+
} else {
|
|
1685
|
+
for (const r of failedChecks) {
|
|
1686
|
+
if (r.name === "project") {
|
|
1687
|
+
markdown += `- Run "ak init ." to initialize this directory
|
|
1688
|
+
`;
|
|
1689
|
+
}
|
|
1690
|
+
if (r.name === "state") {
|
|
1691
|
+
markdown += `- State file is missing. Re-run "ak init" or create manually
|
|
1692
|
+
`;
|
|
1693
|
+
}
|
|
1694
|
+
if (r.name === "source" && r.meta?.source) {
|
|
1695
|
+
markdown += `- Update source path: ak update --source <new-path>
|
|
1696
|
+
`;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const reportPath = join8(projectDir, ".ak", "doctor-report.md");
|
|
1701
|
+
await fs8.ensureDir(join8(projectDir, ".ak"));
|
|
1702
|
+
await fs8.writeFile(reportPath, markdown, "utf-8");
|
|
1703
|
+
if (!options.json) {
|
|
1704
|
+
console.log(pc7.green(`
|
|
1705
|
+
\u2713 Report saved to ${reportPath}
|
|
1706
|
+
`));
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
async function outputColored(projectDir, results, state, targetDir) {
|
|
1710
|
+
console.log(pc7.bold(pc7.cyan("\nApero Kit Doctor\n")));
|
|
1711
|
+
console.log(pc7.gray("Checking project health...\n"));
|
|
1712
|
+
for (const r of results) {
|
|
1713
|
+
if (r.passed) {
|
|
1714
|
+
let msg = `\u2713 ${r.label}`;
|
|
1715
|
+
if (r.name === "target" && r.meta?.folder) {
|
|
1716
|
+
msg += ` (${r.meta.folder})`;
|
|
1717
|
+
}
|
|
1718
|
+
if (r.name.startsWith("subdirs_") && r.meta?.items !== void 0) {
|
|
1719
|
+
msg += ` (${r.meta.items} items)`;
|
|
1720
|
+
}
|
|
1721
|
+
console.log(pc7.green(msg));
|
|
1722
|
+
} else {
|
|
1723
|
+
const icon = r.severity === "error" ? "\u2717" : "\u26A0";
|
|
1724
|
+
const color = r.severity === "error" ? pc7.red : pc7.yellow;
|
|
1725
|
+
let msg = `${icon} ${r.label}`;
|
|
1726
|
+
if (r.name === "target") {
|
|
1727
|
+
msg = `${icon} No target directory (.claude/, .opencode/, .agent/)`;
|
|
1728
|
+
}
|
|
1729
|
+
if (r.name === "agents_md") {
|
|
1730
|
+
msg = `${icon} AGENTS.md not found (optional but recommended)`;
|
|
1731
|
+
}
|
|
1732
|
+
if (r.name === "source" && r.meta?.source) {
|
|
1733
|
+
msg = `${icon} Source directory not found: ${r.meta.source}`;
|
|
1734
|
+
}
|
|
1735
|
+
if (r.name.startsWith("subdirs_")) {
|
|
1736
|
+
const dir = r.name.replace("subdirs_", "");
|
|
1737
|
+
msg = `${icon} ${dir}/ not found`;
|
|
1738
|
+
}
|
|
1739
|
+
console.log(color(msg));
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
console.log("");
|
|
1743
|
+
console.log(pc7.gray("Source detection:"));
|
|
1744
|
+
const source = resolveSource();
|
|
1745
|
+
if ("error" in source) {
|
|
1746
|
+
console.log(pc7.yellow(` \u26A0 ${source.error}`));
|
|
1747
|
+
} else {
|
|
1748
|
+
console.log(pc7.green(` \u2713 Found source: ${source.path}`));
|
|
1749
|
+
console.log(pc7.gray(` Type: ${source.type || "unknown"}`));
|
|
1750
|
+
}
|
|
1751
|
+
const summary = computeSummary(results);
|
|
1752
|
+
console.log("");
|
|
1753
|
+
console.log(pc7.cyan("\u2500".repeat(40)));
|
|
1754
|
+
if (summary.issues === 0 && summary.warnings === 0) {
|
|
1755
|
+
console.log(pc7.bold(pc7.green("\n\u2713 All checks passed!\n")));
|
|
1756
|
+
} else if (summary.issues === 0) {
|
|
1757
|
+
console.log(pc7.yellow(`
|
|
1758
|
+
\u26A0 ${summary.warnings} warning(s), no critical issues
|
|
1759
|
+
`));
|
|
1760
|
+
} else {
|
|
1761
|
+
console.log(pc7.red(`
|
|
1762
|
+
\u2717 ${summary.issues} issue(s), ${summary.warnings} warning(s)
|
|
1763
|
+
`));
|
|
1764
|
+
}
|
|
1765
|
+
if (summary.issues > 0 || summary.warnings > 0) {
|
|
1766
|
+
console.log(pc7.cyan("Suggestions:"));
|
|
1767
|
+
const isProject = results.find((r) => r.name === "project")?.passed;
|
|
1768
|
+
const hasState = results.find((r) => r.name === "state")?.passed;
|
|
1769
|
+
if (!isProject) {
|
|
1770
|
+
console.log(pc7.white(' \u2022 Run "ak init ." to initialize this directory'));
|
|
1771
|
+
}
|
|
1772
|
+
if (!hasState && isProject) {
|
|
1773
|
+
console.log(pc7.white(' \u2022 State file is missing. Re-run "ak init" or create manually'));
|
|
1774
|
+
}
|
|
1775
|
+
if (state && state.source && !fs8.existsSync(state.source)) {
|
|
1776
|
+
console.log(pc7.white(" \u2022 Update source path: ak update --source <new-path>"));
|
|
1777
|
+
}
|
|
1778
|
+
console.log("");
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
var init_doctor = __esm({
|
|
1782
|
+
"src/commands/doctor.ts"() {
|
|
1783
|
+
"use strict";
|
|
1784
|
+
init_paths();
|
|
1785
|
+
init_state();
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
// src/commands/help.ts
|
|
1790
|
+
var help_exports = {};
|
|
1791
|
+
__export(help_exports, {
|
|
1792
|
+
helpCommand: () => helpCommand
|
|
1793
|
+
});
|
|
1794
|
+
import { exec } from "child_process";
|
|
1795
|
+
import pc8 from "picocolors";
|
|
1796
|
+
async function helpCommand(options) {
|
|
1797
|
+
console.log(pc8.cyan("\n\u{1F4DA} Opening VividKit documentation...\n"));
|
|
1798
|
+
console.log(pc8.green(` ${HELP_URL}
|
|
1799
|
+
`));
|
|
1800
|
+
const openCommand = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1801
|
+
exec(`${openCommand} ${HELP_URL}`);
|
|
1802
|
+
}
|
|
1803
|
+
var HELP_URL;
|
|
1804
|
+
var init_help = __esm({
|
|
1805
|
+
"src/commands/help.ts"() {
|
|
1806
|
+
"use strict";
|
|
1807
|
+
HELP_URL = "https://www.vividkit.dev/vi/guides/what-is-claudekit";
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
// src/commands/uninstall.ts
|
|
1812
|
+
var uninstall_exports = {};
|
|
1813
|
+
__export(uninstall_exports, {
|
|
1814
|
+
uninstallCommand: () => uninstallCommand
|
|
1815
|
+
});
|
|
1816
|
+
import fs9 from "fs-extra";
|
|
1817
|
+
import { join as join9 } from "path";
|
|
1818
|
+
import pc9 from "picocolors";
|
|
1819
|
+
import ora4 from "ora";
|
|
1820
|
+
import * as p2 from "@clack/prompts";
|
|
1821
|
+
async function uninstallCommand(options) {
|
|
1822
|
+
p2.intro(pc9.bgCyan(pc9.black(" AK Uninstall ")));
|
|
1823
|
+
const targets = [];
|
|
1824
|
+
if (options.local || !options.global && !options.local) {
|
|
1825
|
+
targets.push({ type: "local", dir: process.cwd() });
|
|
1826
|
+
}
|
|
1827
|
+
if (options.global) {
|
|
1828
|
+
targets.push({ type: "global", dir: getGlobalInstallPath() });
|
|
1829
|
+
}
|
|
1830
|
+
let totalRemoved = 0;
|
|
1831
|
+
for (const target of targets) {
|
|
1832
|
+
const count = await uninstallFromDir(target.type, target.dir, options);
|
|
1833
|
+
totalRemoved += count;
|
|
1834
|
+
}
|
|
1835
|
+
if (totalRemoved === 0) {
|
|
1836
|
+
p2.outro(pc9.yellow("No installations found to remove."));
|
|
1837
|
+
} else {
|
|
1838
|
+
p2.outro(pc9.green(`Successfully removed ${totalRemoved} item(s).`));
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
async function uninstallFromDir(type, dir, options) {
|
|
1842
|
+
const spinner = ora4(`Scanning ${type} installation at ${pc9.dim(dir)}`).start();
|
|
1843
|
+
const state = await loadState(dir);
|
|
1844
|
+
const existingTargets = [];
|
|
1845
|
+
for (const [name, folder] of Object.entries(TARGETS)) {
|
|
1846
|
+
const targetPath = join9(dir, folder);
|
|
1847
|
+
if (fs9.existsSync(targetPath)) {
|
|
1848
|
+
existingTargets.push({ name, path: targetPath });
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
const akDir = join9(dir, ".ak");
|
|
1852
|
+
const hasAkState = fs9.existsSync(akDir);
|
|
1853
|
+
if (existingTargets.length === 0 && !hasAkState) {
|
|
1854
|
+
spinner.warn(pc9.yellow(`No AK installation found in ${type}`));
|
|
1855
|
+
return 0;
|
|
1856
|
+
}
|
|
1857
|
+
spinner.succeed(pc9.green(`Found AK installation in ${type}`));
|
|
1858
|
+
const removalList = [];
|
|
1859
|
+
for (const target of existingTargets) {
|
|
1860
|
+
for (const subdir of AK_SUBDIRS) {
|
|
1861
|
+
const subdirPath = join9(target.path, subdir);
|
|
1862
|
+
if (fs9.existsSync(subdirPath)) {
|
|
1863
|
+
removalList.push(subdirPath);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
if (hasAkState) {
|
|
1868
|
+
removalList.push(akDir);
|
|
1869
|
+
}
|
|
1870
|
+
if (removalList.length === 0) {
|
|
1871
|
+
console.log(pc9.yellow(` No files to remove from ${type}`));
|
|
1872
|
+
return 0;
|
|
1873
|
+
}
|
|
1874
|
+
console.log(pc9.cyan(`
|
|
1875
|
+
Items to remove from ${type}:`));
|
|
1876
|
+
for (const path of removalList) {
|
|
1877
|
+
const relativePath = path.replace(dir + "/", "");
|
|
1878
|
+
console.log(pc9.dim(` \u2022 ${relativePath}`));
|
|
1879
|
+
}
|
|
1880
|
+
if (state) {
|
|
1881
|
+
console.log(pc9.dim(`
|
|
1882
|
+
Kit: ${state.kit}`));
|
|
1883
|
+
console.log(pc9.dim(` Target: ${state.target}`));
|
|
1884
|
+
}
|
|
1885
|
+
console.log(pc9.yellow(`
|
|
1886
|
+
Preserved files: ${PRESERVED_FILES.join(", ")}`));
|
|
1887
|
+
if (options.dryRun) {
|
|
1888
|
+
console.log(pc9.yellow(`
|
|
1889
|
+
[DRY RUN] Would remove ${removalList.length} item(s)`));
|
|
1890
|
+
return 0;
|
|
1891
|
+
}
|
|
1892
|
+
if (!options.yes) {
|
|
1893
|
+
const confirmed = await p2.confirm({
|
|
1894
|
+
message: `Remove ${removalList.length} item(s) from ${type}?`,
|
|
1895
|
+
initialValue: false
|
|
1896
|
+
});
|
|
1897
|
+
if (p2.isCancel(confirmed) || !confirmed) {
|
|
1898
|
+
console.log(pc9.dim(" Cancelled."));
|
|
1899
|
+
return 0;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
const result = await removeItems(removalList, dir);
|
|
1903
|
+
if (result.removed.length > 0) {
|
|
1904
|
+
console.log(pc9.green(`
|
|
1905
|
+
\u2713 Removed ${result.removed.length} item(s) from ${type}`));
|
|
1906
|
+
for (const path of result.removed) {
|
|
1907
|
+
const relativePath = path.replace(dir + "/", "");
|
|
1908
|
+
console.log(pc9.dim(` \u2022 ${relativePath}`));
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
if (result.errors.length > 0) {
|
|
1912
|
+
console.log(pc9.red(`
|
|
1913
|
+
\u2717 Failed to remove ${result.errors.length} item(s):`));
|
|
1914
|
+
for (const error of result.errors) {
|
|
1915
|
+
const relativePath = error.path.replace(dir + "/", "");
|
|
1916
|
+
console.log(pc9.red(` \u2022 ${relativePath}: ${error.error}`));
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
return result.removed.length;
|
|
1920
|
+
}
|
|
1921
|
+
async function removeItems(paths, baseDir) {
|
|
1922
|
+
const result = {
|
|
1923
|
+
removed: [],
|
|
1924
|
+
errors: []
|
|
1925
|
+
};
|
|
1926
|
+
for (const path of paths) {
|
|
1927
|
+
if (!path.startsWith(baseDir)) {
|
|
1928
|
+
result.errors.push({
|
|
1929
|
+
path,
|
|
1930
|
+
error: "Path outside base directory (security check failed)"
|
|
1931
|
+
});
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
const isExpectedPath = path.includes("/.ak") || AK_SUBDIRS.some((subdir) => path.includes(`/${subdir}`));
|
|
1935
|
+
if (!isExpectedPath) {
|
|
1936
|
+
result.errors.push({
|
|
1937
|
+
path,
|
|
1938
|
+
error: "Path is not an expected AK directory (security check failed)"
|
|
1939
|
+
});
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
try {
|
|
1943
|
+
await fs9.remove(path);
|
|
1944
|
+
result.removed.push(path);
|
|
1945
|
+
} catch (error) {
|
|
1946
|
+
result.errors.push({
|
|
1947
|
+
path,
|
|
1948
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
return result;
|
|
1953
|
+
}
|
|
1954
|
+
var PRESERVED_FILES, AK_SUBDIRS;
|
|
1955
|
+
var init_uninstall = __esm({
|
|
1956
|
+
"src/commands/uninstall.ts"() {
|
|
1957
|
+
"use strict";
|
|
1958
|
+
init_paths();
|
|
1959
|
+
init_state();
|
|
1960
|
+
PRESERVED_FILES = ["CLAUDE.md", "settings.json", "AGENTS.md"];
|
|
1961
|
+
AK_SUBDIRS = ["agents", "skills", "commands", "workflows", "hooks", "router"];
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
|
|
1965
|
+
// src/utils/git-tags.ts
|
|
1966
|
+
import { execSync } from "child_process";
|
|
1967
|
+
function fetchRemoteTags(repoUrl = REMOTE_REPO_URL) {
|
|
1968
|
+
const output = execSync(`git ls-remote --tags "${repoUrl}"`, {
|
|
1969
|
+
encoding: "utf-8",
|
|
1970
|
+
timeout: 15e3,
|
|
1971
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1972
|
+
});
|
|
1973
|
+
return output.split("\n").filter((line) => line.includes("refs/tags/")).map((line) => {
|
|
1974
|
+
const tag = line.split("refs/tags/")[1]?.replace("^{}", "");
|
|
1975
|
+
return tag;
|
|
1976
|
+
}).filter(Boolean).filter((v, i, a) => a.indexOf(v) === i);
|
|
1977
|
+
}
|
|
1978
|
+
function sortSemver(tags) {
|
|
1979
|
+
return [...tags].sort((a, b) => {
|
|
1980
|
+
const pa = a.replace(/^v/, "").split(/[.-]/);
|
|
1981
|
+
const pb = b.replace(/^v/, "").split(/[.-]/);
|
|
1982
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
1983
|
+
const na = parseInt(pa[i]) || 0;
|
|
1984
|
+
const nb = parseInt(pb[i]) || 0;
|
|
1985
|
+
if (na !== nb) return nb - na;
|
|
1986
|
+
}
|
|
1987
|
+
return 0;
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
var REMOTE_REPO_URL;
|
|
1991
|
+
var init_git_tags = __esm({
|
|
1992
|
+
"src/utils/git-tags.ts"() {
|
|
1993
|
+
"use strict";
|
|
1994
|
+
REMOTE_REPO_URL = "https://github.com/Thanhnguyen6702/CK-Internal.git";
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
// src/commands/versions.ts
|
|
1999
|
+
var versions_exports = {};
|
|
2000
|
+
__export(versions_exports, {
|
|
2001
|
+
versionsCommand: () => versionsCommand
|
|
2002
|
+
});
|
|
2003
|
+
import pc10 from "picocolors";
|
|
2004
|
+
import ora5 from "ora";
|
|
2005
|
+
async function versionsCommand(options) {
|
|
2006
|
+
const spinner = ora5("Fetching available versions...").start();
|
|
2007
|
+
try {
|
|
2008
|
+
const tags = fetchRemoteTags();
|
|
2009
|
+
spinner.stop();
|
|
2010
|
+
if (!tags || tags.length === 0) {
|
|
2011
|
+
console.log(pc10.yellow("\nNo versions found."));
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
let filtered = tags;
|
|
2015
|
+
if (!options.all) {
|
|
2016
|
+
filtered = tags.filter((tag) => !tag.includes("-"));
|
|
2017
|
+
}
|
|
2018
|
+
const sorted = sortSemver(filtered);
|
|
2019
|
+
const limit = options.limit ? parseInt(options.limit, 10) : 10;
|
|
2020
|
+
const limited = sorted.slice(0, limit);
|
|
2021
|
+
console.log(pc10.bold("\nAvailable versions:\n"));
|
|
2022
|
+
limited.forEach((tag, index) => {
|
|
2023
|
+
const isLatest = index === 0;
|
|
2024
|
+
const marker = isLatest ? pc10.green(" (latest)") : "";
|
|
2025
|
+
console.log(` ${pc10.cyan(tag)}${marker}`);
|
|
2026
|
+
});
|
|
2027
|
+
if (sorted.length > limited.length) {
|
|
2028
|
+
console.log(pc10.gray(`
|
|
2029
|
+
... and ${sorted.length - limited.length} more (use --limit or --all)`));
|
|
2030
|
+
}
|
|
2031
|
+
console.log("");
|
|
2032
|
+
} catch (err) {
|
|
2033
|
+
spinner.stop();
|
|
2034
|
+
console.log(pc10.red("\nFailed to fetch versions."));
|
|
2035
|
+
console.log(pc10.gray(err.message));
|
|
2036
|
+
if (err.message.includes("timeout")) {
|
|
2037
|
+
console.log(pc10.gray("Network timeout. Check your connection."));
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
var init_versions = __esm({
|
|
2042
|
+
"src/commands/versions.ts"() {
|
|
2043
|
+
"use strict";
|
|
2044
|
+
init_git_tags();
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
|
|
2048
|
+
// src/commands/update-cli.ts
|
|
2049
|
+
var update_cli_exports = {};
|
|
2050
|
+
__export(update_cli_exports, {
|
|
2051
|
+
updateCliCommand: () => updateCliCommand
|
|
2052
|
+
});
|
|
2053
|
+
import { execSync as execSync2 } from "child_process";
|
|
2054
|
+
import pc11 from "picocolors";
|
|
2055
|
+
import ora6 from "ora";
|
|
2056
|
+
import { readFileSync } from "fs";
|
|
2057
|
+
import { join as join10 } from "path";
|
|
2058
|
+
function getCurrentVersion() {
|
|
2059
|
+
try {
|
|
2060
|
+
const pkg = JSON.parse(readFileSync(join10(CLI_ROOT, "package.json"), "utf-8"));
|
|
2061
|
+
return pkg.version;
|
|
2062
|
+
} catch {
|
|
2063
|
+
return "0.0.0";
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
function getLatestVersion() {
|
|
2067
|
+
const output = execSync2("npm view apero-kit-cli version", {
|
|
2068
|
+
encoding: "utf-8",
|
|
2069
|
+
timeout: 15e3,
|
|
2070
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2071
|
+
});
|
|
2072
|
+
return output.trim();
|
|
2073
|
+
}
|
|
2074
|
+
async function updateCliCommand(options) {
|
|
2075
|
+
const current = getCurrentVersion();
|
|
2076
|
+
const spinner = ora6("Checking for updates...").start();
|
|
2077
|
+
try {
|
|
2078
|
+
const latest = options.version || getLatestVersion();
|
|
2079
|
+
spinner.stop();
|
|
2080
|
+
console.log(`
|
|
2081
|
+
Current: ${pc11.gray(current)}`);
|
|
2082
|
+
console.log(` Latest: ${pc11.green(latest)}`);
|
|
2083
|
+
if (current === latest && !options.version) {
|
|
2084
|
+
console.log(pc11.green("\nAlready up to date!"));
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
if (options.check) {
|
|
2088
|
+
console.log(pc11.yellow('\nUpdate available! Run "ak update-cli" to install.'));
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
const target = options.version || "latest";
|
|
2092
|
+
const installSpinner = ora6(`Installing apero-kit-cli@${target}...`).start();
|
|
2093
|
+
execSync2(`npm install -g apero-kit-cli@${target}`, {
|
|
2094
|
+
encoding: "utf-8",
|
|
2095
|
+
timeout: 6e4,
|
|
2096
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2097
|
+
});
|
|
2098
|
+
installSpinner.succeed(pc11.green(`Updated to ${target}!`));
|
|
2099
|
+
} catch (err) {
|
|
2100
|
+
spinner.stop();
|
|
2101
|
+
console.log(pc11.red("\nUpdate failed."));
|
|
2102
|
+
console.log(pc11.gray(err.message));
|
|
2103
|
+
console.log(pc11.gray("\nTry manually: npm install -g apero-kit-cli"));
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
var init_update_cli = __esm({
|
|
2107
|
+
"src/commands/update-cli.ts"() {
|
|
2108
|
+
"use strict";
|
|
2109
|
+
init_paths();
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
|
|
2113
|
+
// src/commands/skills.ts
|
|
2114
|
+
var skills_exports = {};
|
|
2115
|
+
__export(skills_exports, {
|
|
2116
|
+
skillsCommand: () => skillsCommand
|
|
2117
|
+
});
|
|
2118
|
+
import pc12 from "picocolors";
|
|
2119
|
+
import fs10 from "fs-extra";
|
|
2120
|
+
import { join as join11 } from "path";
|
|
2121
|
+
import * as p3 from "@clack/prompts";
|
|
2122
|
+
async function skillsCommand(options) {
|
|
2123
|
+
const { name, agent, list, installed, uninstall, yes } = options;
|
|
2124
|
+
if (list || !name && !installed) {
|
|
2125
|
+
await listSkillsWithStatus();
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (installed && !name) {
|
|
2129
|
+
await showInstalledSkills();
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
if (!name) {
|
|
2133
|
+
console.log(pc12.red("Error: --name <skill> is required"));
|
|
2134
|
+
console.log(pc12.gray("Usage: ak skills --name <skill> [--agent <agents>] [--uninstall]"));
|
|
2135
|
+
process.exit(1);
|
|
2136
|
+
}
|
|
2137
|
+
const targetAgents = parseAgents(agent);
|
|
2138
|
+
if (uninstall) {
|
|
2139
|
+
await uninstallSkill(name, targetAgents, yes);
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
await installSkill(name, targetAgents, yes);
|
|
2143
|
+
}
|
|
2144
|
+
async function listSkillsWithStatus() {
|
|
2145
|
+
const source = resolveSource();
|
|
2146
|
+
if ("error" in source) {
|
|
2147
|
+
console.log(pc12.red(`Error: ${source.error}`));
|
|
2148
|
+
process.exit(1);
|
|
2149
|
+
}
|
|
2150
|
+
const skills = listAvailable("skills", source.claudeDir).filter((s) => s.isDir);
|
|
2151
|
+
if (skills.length === 0) {
|
|
2152
|
+
console.log(pc12.yellow("No skills found in source"));
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
console.log(pc12.bold(pc12.cyan(`
|
|
2156
|
+
Available Skills (${skills.length}):`)));
|
|
2157
|
+
console.log(pc12.gray(`Source: ${source.path}
|
|
2158
|
+
`));
|
|
2159
|
+
const header = " Skill".padEnd(40) + "Claude Cursor Codex";
|
|
2160
|
+
console.log(pc12.bold(header));
|
|
2161
|
+
console.log(pc12.gray(" " + "\u2500".repeat(58)));
|
|
2162
|
+
for (const skill of skills) {
|
|
2163
|
+
const claudeInstalled = isSkillInstalled(skill.name, "claude");
|
|
2164
|
+
const cursorInstalled = isSkillInstalled(skill.name, "cursor");
|
|
2165
|
+
const codexInstalled = isSkillInstalled(skill.name, "codex");
|
|
2166
|
+
const statusLine = " " + skill.name.padEnd(38) + (claudeInstalled ? pc12.green("\u2713") : pc12.gray("-")).padEnd(8) + (cursorInstalled ? pc12.green("\u2713") : pc12.gray("-")).padEnd(8) + (codexInstalled ? pc12.green("\u2713") : pc12.gray("-"));
|
|
2167
|
+
console.log(statusLine);
|
|
2168
|
+
}
|
|
2169
|
+
console.log("");
|
|
2170
|
+
console.log(pc12.gray("Install: ak skills --name <skill> --agent <claude|cursor|codex>"));
|
|
2171
|
+
console.log("");
|
|
2172
|
+
}
|
|
2173
|
+
async function showInstalledSkills() {
|
|
2174
|
+
console.log(pc12.bold(pc12.cyan("\nInstalled Skills:\n")));
|
|
2175
|
+
let hasAny = false;
|
|
2176
|
+
for (const [agentType, skillPath] of Object.entries(AGENT_SKILL_PATHS)) {
|
|
2177
|
+
const installed = getInstalledSkills(agentType);
|
|
2178
|
+
if (installed.length > 0) {
|
|
2179
|
+
hasAny = true;
|
|
2180
|
+
console.log(pc12.bold(` ${agentType}:`));
|
|
2181
|
+
for (const skill of installed) {
|
|
2182
|
+
console.log(pc12.white(` \u2022 ${skill}`));
|
|
2183
|
+
}
|
|
2184
|
+
console.log("");
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
if (!hasAny) {
|
|
2188
|
+
console.log(pc12.gray(" No skills installed"));
|
|
2189
|
+
console.log("");
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
async function installSkill(skillName, agents, skipConfirm) {
|
|
2193
|
+
const source = resolveSource();
|
|
2194
|
+
if ("error" in source) {
|
|
2195
|
+
console.log(pc12.red(`Error: ${source.error}`));
|
|
2196
|
+
process.exit(1);
|
|
2197
|
+
}
|
|
2198
|
+
const skillPath = join11(source.claudeDir, "skills", skillName);
|
|
2199
|
+
if (!fs10.existsSync(skillPath)) {
|
|
2200
|
+
console.log(pc12.red(`Error: Skill "${skillName}" not found in source`));
|
|
2201
|
+
console.log(pc12.gray(`Available skills: ak skills --list`));
|
|
2202
|
+
process.exit(1);
|
|
2203
|
+
}
|
|
2204
|
+
if (!skipConfirm) {
|
|
2205
|
+
const confirm4 = await p3.confirm({
|
|
2206
|
+
message: `Install skill "${skillName}" to ${agents.join(", ")}?`
|
|
2207
|
+
});
|
|
2208
|
+
if (p3.isCancel(confirm4) || !confirm4) {
|
|
2209
|
+
console.log(pc12.gray("Cancelled"));
|
|
2210
|
+
process.exit(0);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
const results = [];
|
|
2214
|
+
for (const agent of agents) {
|
|
2215
|
+
try {
|
|
2216
|
+
const targetPath = join11(process.cwd(), AGENT_SKILL_PATHS[agent], skillName);
|
|
2217
|
+
await fs10.ensureDir(join11(process.cwd(), AGENT_SKILL_PATHS[agent]));
|
|
2218
|
+
await fs10.copy(skillPath, targetPath, { overwrite: true });
|
|
2219
|
+
results.push({ agent, success: true });
|
|
2220
|
+
} catch (err) {
|
|
2221
|
+
results.push({ agent, success: false, error: err.message });
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
console.log("");
|
|
2225
|
+
const allSuccess = results.every((r) => r.success);
|
|
2226
|
+
if (allSuccess) {
|
|
2227
|
+
console.log(pc12.green(`\u2713 Installed "${skillName}" to ${agents.join(", ")}`));
|
|
2228
|
+
} else {
|
|
2229
|
+
for (const result of results) {
|
|
2230
|
+
if (result.success) {
|
|
2231
|
+
console.log(pc12.green(`\u2713 ${result.agent}: installed`));
|
|
2232
|
+
} else {
|
|
2233
|
+
console.log(pc12.red(`\u2717 ${result.agent}: ${result.error}`));
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
console.log("");
|
|
2238
|
+
}
|
|
2239
|
+
async function uninstallSkill(skillName, agents, skipConfirm) {
|
|
2240
|
+
const installedIn = agents.filter((agent) => isSkillInstalled(skillName, agent));
|
|
2241
|
+
if (installedIn.length === 0) {
|
|
2242
|
+
console.log(pc12.yellow(`Skill "${skillName}" is not installed in ${agents.join(", ")}`));
|
|
2243
|
+
process.exit(0);
|
|
2244
|
+
}
|
|
2245
|
+
if (!skipConfirm) {
|
|
2246
|
+
const confirm4 = await p3.confirm({
|
|
2247
|
+
message: `Uninstall skill "${skillName}" from ${installedIn.join(", ")}?`
|
|
2248
|
+
});
|
|
2249
|
+
if (p3.isCancel(confirm4) || !confirm4) {
|
|
2250
|
+
console.log(pc12.gray("Cancelled"));
|
|
2251
|
+
process.exit(0);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
const results = [];
|
|
2255
|
+
for (const agent of installedIn) {
|
|
2256
|
+
try {
|
|
2257
|
+
const targetPath = join11(process.cwd(), AGENT_SKILL_PATHS[agent], skillName);
|
|
2258
|
+
await fs10.remove(targetPath);
|
|
2259
|
+
results.push({ agent, success: true });
|
|
2260
|
+
} catch (err) {
|
|
2261
|
+
results.push({ agent, success: false, error: err.message });
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
console.log("");
|
|
2265
|
+
const allSuccess = results.every((r) => r.success);
|
|
2266
|
+
if (allSuccess) {
|
|
2267
|
+
console.log(pc12.green(`\u2713 Uninstalled "${skillName}" from ${installedIn.join(", ")}`));
|
|
2268
|
+
} else {
|
|
2269
|
+
for (const result of results) {
|
|
2270
|
+
if (result.success) {
|
|
2271
|
+
console.log(pc12.green(`\u2713 ${result.agent}: uninstalled`));
|
|
2272
|
+
} else {
|
|
2273
|
+
console.log(pc12.red(`\u2717 ${result.agent}: ${result.error}`));
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
console.log("");
|
|
2278
|
+
}
|
|
2279
|
+
function parseAgents(agentFlag) {
|
|
2280
|
+
if (!agentFlag) {
|
|
2281
|
+
return ["claude"];
|
|
2282
|
+
}
|
|
2283
|
+
const agents = agentFlag.split(",").map((a) => a.trim().toLowerCase());
|
|
2284
|
+
const valid = agents.filter((a) => a in AGENT_SKILL_PATHS);
|
|
2285
|
+
if (valid.length === 0) {
|
|
2286
|
+
console.log(pc12.red("Error: No valid agents specified"));
|
|
2287
|
+
console.log(pc12.gray("Valid agents: claude, cursor, codex"));
|
|
2288
|
+
process.exit(1);
|
|
2289
|
+
}
|
|
2290
|
+
return valid;
|
|
2291
|
+
}
|
|
2292
|
+
function isSkillInstalled(skillName, agent) {
|
|
2293
|
+
const skillPath = join11(process.cwd(), AGENT_SKILL_PATHS[agent], skillName);
|
|
2294
|
+
return fs10.existsSync(skillPath);
|
|
2295
|
+
}
|
|
2296
|
+
function getInstalledSkills(agent) {
|
|
2297
|
+
const skillsDir = join11(process.cwd(), AGENT_SKILL_PATHS[agent]);
|
|
2298
|
+
if (!fs10.existsSync(skillsDir)) {
|
|
2299
|
+
return [];
|
|
2300
|
+
}
|
|
2301
|
+
try {
|
|
2302
|
+
const items = fs10.readdirSync(skillsDir);
|
|
2303
|
+
return items.filter((item) => {
|
|
2304
|
+
const itemPath = join11(skillsDir, item);
|
|
2305
|
+
return fs10.statSync(itemPath).isDirectory();
|
|
2306
|
+
});
|
|
2307
|
+
} catch {
|
|
2308
|
+
return [];
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
var AGENT_SKILL_PATHS;
|
|
2312
|
+
var init_skills = __esm({
|
|
2313
|
+
"src/commands/skills.ts"() {
|
|
2314
|
+
"use strict";
|
|
2315
|
+
init_paths();
|
|
2316
|
+
init_copy();
|
|
2317
|
+
AGENT_SKILL_PATHS = {
|
|
2318
|
+
claude: ".claude/skills",
|
|
2319
|
+
cursor: ".cursor/rules",
|
|
2320
|
+
codex: ".codex/skills"
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
|
|
2325
|
+
// src/index.ts
|
|
2326
|
+
import cac from "cac";
|
|
2327
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2328
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2329
|
+
import { dirname as dirname2, join as join13 } from "path";
|
|
2330
|
+
import pc13 from "picocolors";
|
|
2331
|
+
|
|
2332
|
+
// src/cli/command-registry.ts
|
|
2333
|
+
function registerCommands(cli2) {
|
|
2334
|
+
cli2.command("init [project-name]", "Initialize a new project with an agent kit").option("-k, --kit <type>", "Kit type (engineer, researcher, designer, minimal, full, custom)").option("-t, --target <target>", "Target folder (claude, opencode, generic)").option("-s, --source <path>", "Custom source path for templates").option("-f, --force", "Overwrite existing directory").option("-g, --global", "Install to global ~/.claude/ directory").option("--fresh", "Remove existing installation before re-init").option("-y, --yes", "Skip prompts, use defaults").option("--exclude <patterns>", "Exclude components (comma-separated)").option("--only <patterns>", "Include only matching components (comma-separated)").action(async (projectName, options) => {
|
|
2335
|
+
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2336
|
+
await initCommand2(projectName, options);
|
|
2337
|
+
});
|
|
2338
|
+
cli2.command("add <item>", "Add agent, skill, or command (e.g., ak add skill:databases)").option("-s, --source <path>", "Custom source path").option("-p, --path <path>", "Target project path").action(async (item, options) => {
|
|
2339
|
+
const { addCommand: addCommand2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
2340
|
+
await addCommand2(item, options);
|
|
2341
|
+
});
|
|
2342
|
+
cli2.command("list [type]", "List available kits, agents, skills, or commands").option("-s, --source <path>", "Custom source path").action(async (type, options) => {
|
|
2343
|
+
const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
2344
|
+
await listCommand2(type, options);
|
|
2345
|
+
});
|
|
2346
|
+
cli2.command("update", "Update/sync from source templates").option("-s, --source <path>", "Source path to sync from").option("--agents", "Update only agents").option("--skills", "Update only skills").option("--commands", "Update only commands").option("--all", "Update everything").option("-n, --dry-run", "Show what would be updated without making changes").option("-f, --force", "Force update without confirmation").action(async (options) => {
|
|
2347
|
+
const { updateCommand: updateCommand2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
2348
|
+
await updateCommand2(options);
|
|
2349
|
+
});
|
|
2350
|
+
cli2.command("status", "Show project status and file changes").option("-v, --verbose", "Show all files").action(async (options) => {
|
|
2351
|
+
const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
2352
|
+
await statusCommand2(options);
|
|
2353
|
+
});
|
|
2354
|
+
cli2.command("doctor", "Check project health and diagnose issues").option("--fix", "Auto-fix common issues").option("--report", "Generate diagnostic report").option("--json", "Output JSON format").option("--check-only", "CI mode: exit 1 on failures").action(async (options) => {
|
|
2355
|
+
const { doctorCommand: doctorCommand2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
2356
|
+
await doctorCommand2(options);
|
|
2357
|
+
});
|
|
2358
|
+
cli2.command("kits", "List all available kits").action(async () => {
|
|
2359
|
+
const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
2360
|
+
await listCommand2("kits", {});
|
|
2361
|
+
});
|
|
2362
|
+
cli2.command("help", "Open interactive help documentation in browser").option("-s, --source <path>", "Custom source path").action(async (options) => {
|
|
2363
|
+
const { helpCommand: helpCommand2 } = await Promise.resolve().then(() => (init_help(), help_exports));
|
|
2364
|
+
await helpCommand2(options);
|
|
2365
|
+
});
|
|
2366
|
+
cli2.command("uninstall", "Remove ClaudeKit installations").option("-y, --yes", "Skip confirmation").option("-l, --local", "Uninstall only local installation").option("-g, --global", "Uninstall only global installation").option("--dry-run", "Preview what would be removed").action(async (options) => {
|
|
2367
|
+
const { uninstallCommand: uninstallCommand2 } = await Promise.resolve().then(() => (init_uninstall(), uninstall_exports));
|
|
2368
|
+
await uninstallCommand2(options);
|
|
2369
|
+
});
|
|
2370
|
+
cli2.command("versions", "List available versions").option("--kit <kit>", "Filter by kit").option("--limit <limit>", "Number of versions to show").option("--all", "Show all including prereleases").action(async (options) => {
|
|
2371
|
+
const { versionsCommand: versionsCommand2 } = await Promise.resolve().then(() => (init_versions(), versions_exports));
|
|
2372
|
+
await versionsCommand2(options);
|
|
2373
|
+
});
|
|
2374
|
+
cli2.command("update-cli", "Update the CLI itself to latest version").option("--check", "Check for updates without installing").option("--version <version>", "Update to specific version").action(async (options) => {
|
|
2375
|
+
const { updateCliCommand: updateCliCommand2 } = await Promise.resolve().then(() => (init_update_cli(), update_cli_exports));
|
|
2376
|
+
await updateCliCommand2(options);
|
|
2377
|
+
});
|
|
2378
|
+
cli2.command("skills", "Manage skills across coding agents").option("-n, --name <skill>", "Skill name").option("-a, --agent <agents>", "Target agents").option("-l, --list", "List available skills").option("--installed", "Show installed skills").option("-u, --uninstall", "Uninstall skill").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
2379
|
+
const { skillsCommand: skillsCommand2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
2380
|
+
await skillsCommand2(options);
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
// src/utils/version-check.ts
|
|
2385
|
+
import { join as join12 } from "path";
|
|
2386
|
+
import { homedir as homedir2 } from "os";
|
|
2387
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
2388
|
+
var CACHE_DIR = join12(homedir2(), ".apero-kit");
|
|
2389
|
+
var CACHE_FILE = join12(CACHE_DIR, "version-check.json");
|
|
2390
|
+
var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
2391
|
+
function getCachedVersionNoFetch() {
|
|
2392
|
+
try {
|
|
2393
|
+
if (existsSync2(CACHE_FILE)) {
|
|
2394
|
+
const cache = JSON.parse(readFileSync2(CACHE_FILE, "utf-8"));
|
|
2395
|
+
return cache.version;
|
|
2396
|
+
}
|
|
2397
|
+
} catch {
|
|
2398
|
+
}
|
|
2399
|
+
return null;
|
|
2400
|
+
}
|
|
2401
|
+
function isNewerVersion(current, latest) {
|
|
2402
|
+
const c = current.replace(/^v/, "").split(".").map(Number);
|
|
2403
|
+
const l = latest.replace(/^v/, "").split(".").map(Number);
|
|
2404
|
+
for (let i = 0; i < 3; i++) {
|
|
2405
|
+
if ((l[i] || 0) > (c[i] || 0)) return true;
|
|
2406
|
+
if ((l[i] || 0) < (c[i] || 0)) return false;
|
|
2407
|
+
}
|
|
2408
|
+
return false;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
// src/index.ts
|
|
2412
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2413
|
+
var __dirname2 = dirname2(__filename2);
|
|
2414
|
+
function getVersion() {
|
|
2415
|
+
try {
|
|
2416
|
+
const pkg = JSON.parse(readFileSync3(join13(__dirname2, "..", "package.json"), "utf-8"));
|
|
2417
|
+
return pkg.version;
|
|
2418
|
+
} catch {
|
|
2419
|
+
return "0.0.0";
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
var cli = cac("ak");
|
|
2423
|
+
registerCommands(cli);
|
|
2424
|
+
cli.help();
|
|
2425
|
+
cli.version(getVersion());
|
|
2426
|
+
cli.parse();
|
|
2427
|
+
try {
|
|
2428
|
+
const version = getVersion();
|
|
2429
|
+
const cachedLatest = getCachedVersionNoFetch();
|
|
2430
|
+
if (cachedLatest && isNewerVersion(version, cachedLatest)) {
|
|
2431
|
+
console.log(pc13.yellow(`
|
|
2432
|
+
Update available: v${version} \u2192 v${cachedLatest}`));
|
|
2433
|
+
console.log(pc13.gray(' Run "ak update-cli" to update\n'));
|
|
2434
|
+
}
|
|
2435
|
+
} catch {
|
|
2436
|
+
}
|
|
2437
|
+
//# sourceMappingURL=index.js.map
|