@boopkit/cli 0.1.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/README.md +65 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +85 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.js +173 -0
- package/dist/commands/adopt.d.ts +5 -0
- package/dist/commands/adopt.js +286 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +295 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.js +105 -0
- package/dist/commands/loop.d.ts +63 -0
- package/dist/commands/loop.js +765 -0
- package/dist/commands/status.d.ts +40 -0
- package/dist/commands/status.js +303 -0
- package/dist/commands/status.test.d.ts +1 -0
- package/dist/commands/status.test.js +118 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +204 -0
- package/dist/lib/capability.d.ts +17 -0
- package/dist/lib/capability.js +172 -0
- package/dist/lib/config.d.ts +70 -0
- package/dist/lib/config.js +59 -0
- package/dist/lib/db-status.d.ts +64 -0
- package/dist/lib/db-status.js +257 -0
- package/dist/lib/env.d.ts +25 -0
- package/dist/lib/env.js +116 -0
- package/dist/lib/execute.d.ts +21 -0
- package/dist/lib/execute.js +228 -0
- package/dist/lib/interpolate.d.ts +4 -0
- package/dist/lib/interpolate.js +13 -0
- package/dist/lib/manifest.d.ts +54 -0
- package/dist/lib/manifest.js +11 -0
- package/dist/lib/preflight.d.ts +31 -0
- package/dist/lib/preflight.js +402 -0
- package/dist/lib/profile.d.ts +11 -0
- package/dist/lib/profile.js +73 -0
- package/dist/lib/project.d.ts +36 -0
- package/dist/lib/project.js +71 -0
- package/dist/lib/project.test.d.ts +1 -0
- package/dist/lib/project.test.js +118 -0
- package/dist/lib/rewire.d.ts +19 -0
- package/dist/lib/rewire.js +222 -0
- package/dist/lib/safe-io.d.ts +66 -0
- package/dist/lib/safe-io.js +167 -0
- package/dist/lib/scaffold.d.ts +87 -0
- package/dist/lib/scaffold.js +548 -0
- package/dist/lib/update.d.ts +73 -0
- package/dist/lib/update.js +379 -0
- package/package.json +46 -0
- package/profiles/web-app/profile.md +113 -0
- package/profiles/web-app/reference/architecture.ref.md +65 -0
- package/profiles/web-app/reference/message-queue-patterns.ref.md +247 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runInit = runInit;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const config_1 = require("../lib/config");
|
|
41
|
+
const profile_1 = require("../lib/profile");
|
|
42
|
+
const capability_1 = require("../lib/capability");
|
|
43
|
+
const scaffold_1 = require("../lib/scaffold");
|
|
44
|
+
const project_1 = require("../lib/project");
|
|
45
|
+
const env_1 = require("../lib/env");
|
|
46
|
+
const safe_io_1 = require("../lib/safe-io");
|
|
47
|
+
function runInit(name, options) {
|
|
48
|
+
const profileName = options.profile ?? 'web-app';
|
|
49
|
+
const shouldInstall = options.install !== false;
|
|
50
|
+
// --- Validate ---
|
|
51
|
+
// Guard: project name must not be empty
|
|
52
|
+
if (!name || !name.trim()) {
|
|
53
|
+
console.error('Error: Project name is required and cannot be empty.');
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const targetDir = path.resolve(process.cwd(), name);
|
|
58
|
+
// Guard: target directory must not exist or must be empty
|
|
59
|
+
if (fs.existsSync(targetDir)) {
|
|
60
|
+
const entries = fs.readdirSync(targetDir);
|
|
61
|
+
if (entries.length > 0) {
|
|
62
|
+
console.error(`Error: Directory "${name}" already exists and is not empty.`);
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// --- Resolve profile ---
|
|
68
|
+
const profile = (0, profile_1.getProfile)(profileName);
|
|
69
|
+
if (!profile) {
|
|
70
|
+
console.error(`Error: Profile "${profileName}" not found.`);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// --- Node version check ---
|
|
75
|
+
const requiredNodeVersion = profile.kernel.nodeVersion;
|
|
76
|
+
if (requiredNodeVersion && !process.env.BOOP_SKIP_VERSION_CHECK) {
|
|
77
|
+
const minVersion = requiredNodeVersion.replace(/^[>=^~]+/, '');
|
|
78
|
+
const currentMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
79
|
+
const requiredMajor = parseInt(minVersion.split('.')[0], 10);
|
|
80
|
+
if (currentMajor < requiredMajor) {
|
|
81
|
+
console.error(`Error: Node.js v${requiredMajor}+ required. You're running v${process.versions.node}.`);
|
|
82
|
+
console.error(`Run \`nvm install ${requiredMajor}\` to upgrade.`);
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// --- Pre-flight: detect script conflicts before writing anything ---
|
|
88
|
+
const capabilityNames = profile.defaultCapabilities;
|
|
89
|
+
const preflightScripts = {};
|
|
90
|
+
for (const capName of capabilityNames) {
|
|
91
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
92
|
+
if (!cap?.scripts)
|
|
93
|
+
continue;
|
|
94
|
+
for (const scriptName of Object.keys(cap.scripts)) {
|
|
95
|
+
if (scriptName in preflightScripts) {
|
|
96
|
+
console.error(`Error: Script "${scriptName}" conflicts between capabilities "${preflightScripts[scriptName]}" and "${capName}".`);
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
preflightScripts[scriptName] = capName;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// --- Create directory ---
|
|
104
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
105
|
+
// --- Collect hooks from capabilities ---
|
|
106
|
+
const hookEntries = [];
|
|
107
|
+
for (const capName of capabilityNames) {
|
|
108
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
109
|
+
if (cap?.hooks) {
|
|
110
|
+
hookEntries.push({ name: capName, hooks: cap.hooks });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const hookVars = (0, scaffold_1.collectHooks)(hookEntries);
|
|
114
|
+
// --- Scaffold kernel ---
|
|
115
|
+
const variables = {
|
|
116
|
+
projectName: name,
|
|
117
|
+
port: profile.kernel.port,
|
|
118
|
+
scripts: profile.kernel.scripts,
|
|
119
|
+
nodeVersion: profile.kernel.nodeVersion ?? '>=18.0.0',
|
|
120
|
+
...hookVars,
|
|
121
|
+
};
|
|
122
|
+
const kernelFiles = (0, scaffold_1.scaffoldKernel)(targetDir, variables);
|
|
123
|
+
// --- Merge kernel dependencies ---
|
|
124
|
+
(0, scaffold_1.mergePackageJson)(targetDir, profile.kernel.dependencies, profile.kernel.devDependencies, undefined, { skipBackup: true });
|
|
125
|
+
// --- Create minimal manifest so capability hash storage can find config.json ---
|
|
126
|
+
(0, project_1.writeProjectManifest)(targetDir, {
|
|
127
|
+
name,
|
|
128
|
+
profile: profileName,
|
|
129
|
+
boopVersion: '0.1.0',
|
|
130
|
+
capabilities: [],
|
|
131
|
+
createdAt: new Date().toISOString(),
|
|
132
|
+
});
|
|
133
|
+
// --- Resolve and install capabilities ---
|
|
134
|
+
const installedCapabilities = [];
|
|
135
|
+
const allScripts = {};
|
|
136
|
+
const scriptSources = {};
|
|
137
|
+
const capabilityFiles = [];
|
|
138
|
+
const cssCapMap = {};
|
|
139
|
+
for (const capName of capabilityNames) {
|
|
140
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
141
|
+
if (!cap) {
|
|
142
|
+
console.error(`Warning: Capability "${capName}" not found, skipping.`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Copy capability files (skills, scaffolded files, etc.)
|
|
146
|
+
const copied = (0, scaffold_1.copyCapabilityFiles)(targetDir, capName);
|
|
147
|
+
capabilityFiles.push(...copied);
|
|
148
|
+
// Track CSS partials for annotation
|
|
149
|
+
for (const f of copied) {
|
|
150
|
+
if (f.startsWith('styles/') && f.endsWith('.css') && f !== 'styles/styles.css') {
|
|
151
|
+
cssCapMap[path.basename(f)] = capName;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Merge dependencies
|
|
155
|
+
(0, scaffold_1.mergePackageJson)(targetDir, cap.dependencies, cap.devDependencies, undefined, { skipBackup: true });
|
|
156
|
+
// Add capability package as semver dependency if specified
|
|
157
|
+
if (cap.package) {
|
|
158
|
+
(0, scaffold_1.mergePackageJson)(targetDir, undefined, undefined, {
|
|
159
|
+
[cap.package]: (0, scaffold_1.getBoopkitVersion)(cap.package),
|
|
160
|
+
}, { skipBackup: true });
|
|
161
|
+
}
|
|
162
|
+
// Collect scripts (conflicts already checked in pre-flight)
|
|
163
|
+
if (cap.scripts) {
|
|
164
|
+
for (const [scriptName, scriptDef] of Object.entries(cap.scripts)) {
|
|
165
|
+
allScripts[scriptName] = scriptDef.cmd;
|
|
166
|
+
scriptSources[scriptName] = capName;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
installedCapabilities.push(capName);
|
|
170
|
+
}
|
|
171
|
+
// --- Inject CSS imports for capability partials ---
|
|
172
|
+
(0, scaffold_1.injectCssImports)(targetDir, cssCapMap, { skipBackup: true });
|
|
173
|
+
// --- Merge capability scripts into package.json ---
|
|
174
|
+
if (Object.keys(allScripts).length > 0) {
|
|
175
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
176
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
177
|
+
if (!pkg.scripts)
|
|
178
|
+
pkg.scripts = {};
|
|
179
|
+
for (const [scriptName, cmd] of Object.entries(allScripts)) {
|
|
180
|
+
pkg.scripts[scriptName] = cmd;
|
|
181
|
+
}
|
|
182
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
// --- Copy reference docs from profile ---
|
|
185
|
+
const boopRoot = (0, config_1.getBoopRoot)();
|
|
186
|
+
const profileDir = path.join(boopRoot, 'profiles', profileName);
|
|
187
|
+
const profileRefDir = path.join(profileDir, 'reference');
|
|
188
|
+
if (fs.existsSync(profileRefDir)) {
|
|
189
|
+
(0, safe_io_1.safeCopyDir)(profileRefDir, path.join(targetDir, 'reference'), { onConflict: 'skip' });
|
|
190
|
+
}
|
|
191
|
+
// --- Generate skills index from installed skill files ---
|
|
192
|
+
(0, scaffold_1.generateSkillsIndex)(targetDir);
|
|
193
|
+
// --- Build capabilityConfig for capabilities with env vars ---
|
|
194
|
+
const capabilityConfig = {};
|
|
195
|
+
const envEntries = [];
|
|
196
|
+
for (const capName of installedCapabilities) {
|
|
197
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
198
|
+
if (cap?.env && cap.env.length > 0) {
|
|
199
|
+
const envConfig = {};
|
|
200
|
+
for (const e of cap.env) {
|
|
201
|
+
envConfig[e.name] = { required: e.required, set: false };
|
|
202
|
+
envEntries.push({ name: e.name, description: e.description, default: e.default, capability: capName });
|
|
203
|
+
}
|
|
204
|
+
capabilityConfig[capName] = { env: envConfig };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// --- Generate .env and .env.example ---
|
|
208
|
+
if (envEntries.length > 0) {
|
|
209
|
+
(0, env_1.generateEnvFiles)(targetDir, envEntries, name);
|
|
210
|
+
}
|
|
211
|
+
// --- Write final manifest, preserving capabilityMeta from hash storage ---
|
|
212
|
+
const existingManifest = (0, project_1.readProjectManifest)(targetDir);
|
|
213
|
+
const manifest = {
|
|
214
|
+
name,
|
|
215
|
+
profile: profileName,
|
|
216
|
+
boopVersion: '0.1.0',
|
|
217
|
+
capabilities: installedCapabilities,
|
|
218
|
+
...(Object.keys(capabilityConfig).length > 0 && { capabilityConfig }),
|
|
219
|
+
...(existingManifest?.capabilityMeta && { capabilityMeta: existingManifest.capabilityMeta }),
|
|
220
|
+
createdAt: new Date().toISOString(),
|
|
221
|
+
};
|
|
222
|
+
(0, project_1.writeProjectManifest)(targetDir, manifest);
|
|
223
|
+
// --- Print summary ---
|
|
224
|
+
console.log(`\nCreated project "${name}" with profile "${profileName}"\n`);
|
|
225
|
+
const scriptCount = Object.keys(allScripts).length;
|
|
226
|
+
const parts = [
|
|
227
|
+
`${kernelFiles.length} kernel files`,
|
|
228
|
+
`${installedCapabilities.length} capabilities`,
|
|
229
|
+
`${capabilityFiles.length} capability files`,
|
|
230
|
+
...(scriptCount > 0 ? [`${scriptCount} scripts`] : []),
|
|
231
|
+
];
|
|
232
|
+
console.log(` ${parts.join(' · ')}`);
|
|
233
|
+
const typeLabels = {
|
|
234
|
+
application: 'Application',
|
|
235
|
+
development: 'Development',
|
|
236
|
+
operations: 'Operations',
|
|
237
|
+
demo: 'Demo',
|
|
238
|
+
};
|
|
239
|
+
const typeOrder = ['application', 'development', 'operations', 'demo'];
|
|
240
|
+
const grouped = new Map();
|
|
241
|
+
for (const capName of installedCapabilities) {
|
|
242
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
243
|
+
const key = cap?.type ?? 'other';
|
|
244
|
+
if (!grouped.has(key))
|
|
245
|
+
grouped.set(key, []);
|
|
246
|
+
grouped.get(key).push(capName);
|
|
247
|
+
}
|
|
248
|
+
for (const type of [...typeOrder, ...grouped.keys()]) {
|
|
249
|
+
const caps = grouped.get(type);
|
|
250
|
+
if (!caps)
|
|
251
|
+
continue;
|
|
252
|
+
grouped.delete(type);
|
|
253
|
+
const label = typeLabels[type] ?? type;
|
|
254
|
+
console.log(` ${label}: ${caps.join(', ')}`);
|
|
255
|
+
}
|
|
256
|
+
// --- npm install (after summary so output is clear) ---
|
|
257
|
+
if (shouldInstall) {
|
|
258
|
+
try {
|
|
259
|
+
console.log('\nInstalling dependencies...');
|
|
260
|
+
(0, child_process_1.execSync)('npm install', { cwd: targetDir, stdio: 'inherit' });
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
console.error('Warning: npm install failed. You can run it manually later.');
|
|
264
|
+
}
|
|
265
|
+
// Build CSS if tailwind:build script exists
|
|
266
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
267
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
268
|
+
if (pkg.scripts?.['tailwind:build']) {
|
|
269
|
+
try {
|
|
270
|
+
console.log('\nBuilding CSS...');
|
|
271
|
+
(0, child_process_1.execSync)('npm run tailwind:build', { cwd: targetDir, stdio: 'inherit' });
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
console.error('Warning: CSS build failed. Run `npm run tailwind:build` manually.');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Generate Prisma client if db:generate script exists
|
|
278
|
+
if (pkg.scripts?.['db:generate']) {
|
|
279
|
+
try {
|
|
280
|
+
console.log('\nGenerating Prisma client...');
|
|
281
|
+
(0, child_process_1.execSync)('npm run db:generate', { cwd: targetDir, stdio: 'inherit' });
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
console.error('Warning: Prisma generate failed. Run `npm run db:generate` manually.');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
console.log(`\nNext steps:`);
|
|
289
|
+
console.log(` cd ${name}`);
|
|
290
|
+
if (!shouldInstall) {
|
|
291
|
+
console.log(' npm install');
|
|
292
|
+
}
|
|
293
|
+
console.log(' boop status ← check what needs configuration');
|
|
294
|
+
console.log(' npm run dev ← start development server');
|
|
295
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runList = runList;
|
|
4
|
+
exports.printGroupedCapabilities = printGroupedCapabilities;
|
|
5
|
+
const capability_1 = require("../lib/capability");
|
|
6
|
+
const profile_1 = require("../lib/profile");
|
|
7
|
+
const project_1 = require("../lib/project");
|
|
8
|
+
function runList(type) {
|
|
9
|
+
switch (type) {
|
|
10
|
+
case 'profiles':
|
|
11
|
+
listProfilesCommand();
|
|
12
|
+
break;
|
|
13
|
+
case 'capabilities':
|
|
14
|
+
listCapabilitiesCommand();
|
|
15
|
+
break;
|
|
16
|
+
default:
|
|
17
|
+
console.error(`Unknown type: ${type}. Use 'profiles' or 'capabilities'.`);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function listProfilesCommand() {
|
|
22
|
+
const profiles = (0, profile_1.listProfiles)();
|
|
23
|
+
if (profiles.length === 0) {
|
|
24
|
+
console.log('No profiles found.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log('Available profiles:\n');
|
|
28
|
+
const typeLabels = {
|
|
29
|
+
application: 'Application',
|
|
30
|
+
development: 'Development',
|
|
31
|
+
operations: 'Operations',
|
|
32
|
+
demo: 'Demo',
|
|
33
|
+
};
|
|
34
|
+
const typeOrder = ['application', 'development', 'operations', 'demo'];
|
|
35
|
+
for (const p of profiles) {
|
|
36
|
+
console.log(` ${p.name}`);
|
|
37
|
+
console.log(` ${p.description}`);
|
|
38
|
+
console.log(` runtime: ${p.kernel.runtime} | deploy: ${p.kernel.deploy} | port: ${p.kernel.port}`);
|
|
39
|
+
const grouped = new Map();
|
|
40
|
+
for (const capName of p.defaultCapabilities) {
|
|
41
|
+
const cap = (0, capability_1.getCapability)(capName);
|
|
42
|
+
const key = cap?.type ?? 'other';
|
|
43
|
+
if (!grouped.has(key))
|
|
44
|
+
grouped.set(key, []);
|
|
45
|
+
grouped.get(key).push(capName);
|
|
46
|
+
}
|
|
47
|
+
console.log(' capabilities:');
|
|
48
|
+
for (const type of [...typeOrder, ...grouped.keys()]) {
|
|
49
|
+
const caps = grouped.get(type);
|
|
50
|
+
if (!caps)
|
|
51
|
+
continue;
|
|
52
|
+
grouped.delete(type);
|
|
53
|
+
const label = typeLabels[type] ?? type;
|
|
54
|
+
console.log(` ${label}: ${caps.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
console.log();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Print capabilities grouped by type with install status markers.
|
|
61
|
+
* Reused by both `boop list capabilities` and `boop add` (no arg).
|
|
62
|
+
*/
|
|
63
|
+
function printGroupedCapabilities() {
|
|
64
|
+
const capabilities = (0, capability_1.listCapabilities)().filter(c => !c.name.startsWith('_'));
|
|
65
|
+
if (capabilities.length === 0) {
|
|
66
|
+
console.log('No capabilities found.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const typeLabels = {
|
|
70
|
+
application: 'Application',
|
|
71
|
+
development: 'Development',
|
|
72
|
+
operations: 'Operations',
|
|
73
|
+
demo: 'Demo',
|
|
74
|
+
};
|
|
75
|
+
const typeOrder = ['application', 'development', 'operations', 'demo'];
|
|
76
|
+
const grouped = new Map();
|
|
77
|
+
for (const c of capabilities) {
|
|
78
|
+
const key = c.type ?? 'other';
|
|
79
|
+
if (!grouped.has(key))
|
|
80
|
+
grouped.set(key, []);
|
|
81
|
+
grouped.get(key).push(c);
|
|
82
|
+
}
|
|
83
|
+
const manifest = (0, project_1.readProjectManifest)(process.cwd());
|
|
84
|
+
const installed = new Set(manifest?.capabilities ?? []);
|
|
85
|
+
console.log('Available capabilities:\n');
|
|
86
|
+
for (const type of [...typeOrder, ...grouped.keys()]) {
|
|
87
|
+
const caps = grouped.get(type);
|
|
88
|
+
if (!caps)
|
|
89
|
+
continue;
|
|
90
|
+
grouped.delete(type);
|
|
91
|
+
const label = typeLabels[type] ?? type;
|
|
92
|
+
console.log(` ${label}`);
|
|
93
|
+
for (const c of caps) {
|
|
94
|
+
const isInstalled = installed.has(c.name);
|
|
95
|
+
const mark = isInstalled ? '\x1b[2m\u2713\x1b[0m ' : '\x1b[32m\u25cb\x1b[0m ';
|
|
96
|
+
const name = isInstalled ? `\x1b[2m${c.name}\x1b[0m` : `\x1b[32m${c.name}\x1b[0m`;
|
|
97
|
+
const desc = isInstalled ? `\x1b[2m${c.description}\x1b[0m` : c.description;
|
|
98
|
+
console.log(` ${mark}${name} \u2014 ${desc}`);
|
|
99
|
+
}
|
|
100
|
+
console.log();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function listCapabilitiesCommand() {
|
|
104
|
+
printGroupedCapabilities();
|
|
105
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface LoopOptions {
|
|
2
|
+
run?: boolean;
|
|
3
|
+
prefix?: string;
|
|
4
|
+
work?: string;
|
|
5
|
+
flow?: string;
|
|
6
|
+
max?: string;
|
|
7
|
+
budget?: string;
|
|
8
|
+
resumeLimit?: string;
|
|
9
|
+
poll?: boolean;
|
|
10
|
+
pollInterval?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
perIssueBranch?: boolean;
|
|
13
|
+
noBranch?: boolean;
|
|
14
|
+
features?: boolean;
|
|
15
|
+
skipPerms?: boolean;
|
|
16
|
+
bare?: boolean;
|
|
17
|
+
json?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface ClaudeRunResult {
|
|
20
|
+
exitCode: number;
|
|
21
|
+
limitHit: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface PlanResult {
|
|
24
|
+
featureId: string | null;
|
|
25
|
+
costUsd: number | null;
|
|
26
|
+
}
|
|
27
|
+
export declare function buildPlanningPrompt(workContent: string, flowContent: string, prefix?: string): string;
|
|
28
|
+
export type RunClaudeFn = (prompt: string, opts: {
|
|
29
|
+
budget: string;
|
|
30
|
+
model: string;
|
|
31
|
+
skipPerms: boolean;
|
|
32
|
+
sessionId: string;
|
|
33
|
+
resume?: boolean;
|
|
34
|
+
bare?: boolean;
|
|
35
|
+
}, logFile: string, readableLogFile?: string) => Promise<ClaudeRunResult>;
|
|
36
|
+
export declare function planWork(opts: {
|
|
37
|
+
workPath: string;
|
|
38
|
+
flowPath: string;
|
|
39
|
+
prefix?: string;
|
|
40
|
+
budget: string;
|
|
41
|
+
model: string;
|
|
42
|
+
skipPerms: boolean;
|
|
43
|
+
bare: boolean;
|
|
44
|
+
logDir: string;
|
|
45
|
+
runClaudeFn?: RunClaudeFn;
|
|
46
|
+
execSyncFn?: (cmd: string, opts: {
|
|
47
|
+
encoding: string;
|
|
48
|
+
}) => string;
|
|
49
|
+
}): Promise<PlanResult>;
|
|
50
|
+
export declare function findFallbackFeature(features: Array<{
|
|
51
|
+
id?: string;
|
|
52
|
+
created_at?: string;
|
|
53
|
+
title?: string;
|
|
54
|
+
}>, workContent: string, workPath: string): string | null;
|
|
55
|
+
export declare function extractFeatureId(text: string): string | null;
|
|
56
|
+
export declare function isUsageLimitText(text: string): boolean;
|
|
57
|
+
export declare function isStandaloneUsageLimitMessage(text: string): boolean;
|
|
58
|
+
export declare function isIssueInScope(issue: {
|
|
59
|
+
id: string;
|
|
60
|
+
parent?: string;
|
|
61
|
+
}, prefix: string, getParentById: (id: string) => string | null): boolean;
|
|
62
|
+
export declare function runLoop(options: LoopOptions): Promise<void>;
|
|
63
|
+
export {};
|