@izkac/forgekit 0.1.5 → 0.1.6
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/package.json +1 -1
- package/src/init.mjs +5 -3
- package/src/plan-engine.mjs +22 -4
- package/src/plan-engine.test.mjs +38 -0
package/package.json
CHANGED
package/src/init.mjs
CHANGED
|
@@ -469,17 +469,18 @@ async function promptOpenSpecSetup() {
|
|
|
469
469
|
|
|
470
470
|
/**
|
|
471
471
|
* Resolve the planning engine for `forge init`, offering OpenSpec setup when needed.
|
|
472
|
-
* @param {{ cwd: string, openspec: boolean | null }} opts
|
|
472
|
+
* @param {{ cwd: string, openspec: boolean | null, agents?: string[] }} opts
|
|
473
473
|
* @returns {Promise<string>} 'openspec' | 'specs'
|
|
474
474
|
*/
|
|
475
475
|
async function resolveInitPlanEngine(opts) {
|
|
476
476
|
const configured = hasOpenSpecConfig(opts.cwd);
|
|
477
|
+
const tools = opts.agents;
|
|
477
478
|
|
|
478
479
|
if (opts.openspec === false) return 'specs';
|
|
479
480
|
|
|
480
481
|
if (opts.openspec === true) {
|
|
481
482
|
if (!configured && process.stdin.isTTY) {
|
|
482
|
-
const setup = setupOpenSpec(opts.cwd);
|
|
483
|
+
const setup = setupOpenSpec(opts.cwd, { tools });
|
|
483
484
|
for (const s of setup.steps) {
|
|
484
485
|
process.stdout.write(` [${s.ok ? 'ok' : 'FAIL'}] ${s.step}${s.detail ? ` — ${s.detail}` : ''}\n`);
|
|
485
486
|
}
|
|
@@ -505,7 +506,7 @@ async function resolveInitPlanEngine(opts) {
|
|
|
505
506
|
const accepted = await promptOpenSpecSetup();
|
|
506
507
|
if (!accepted) return 'specs';
|
|
507
508
|
|
|
508
|
-
const setup = setupOpenSpec(opts.cwd);
|
|
509
|
+
const setup = setupOpenSpec(opts.cwd, { tools });
|
|
509
510
|
for (const s of setup.steps) {
|
|
510
511
|
process.stdout.write(` [${s.ok ? 'ok' : 'FAIL'}] ${s.step}${s.detail ? ` — ${s.detail}` : ''}\n`);
|
|
511
512
|
}
|
|
@@ -564,6 +565,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
564
565
|
const planEngine = await resolveInitPlanEngine({
|
|
565
566
|
cwd: opts.cwd,
|
|
566
567
|
openspec: opts.openspec,
|
|
568
|
+
agents: selected,
|
|
567
569
|
});
|
|
568
570
|
|
|
569
571
|
let adr = opts.adr;
|
package/src/plan-engine.mjs
CHANGED
|
@@ -204,12 +204,28 @@ export function checkOpenSpecCliQuick(runCommand) {
|
|
|
204
204
|
return { ok: false, version: null };
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Map forgekit environment ids → OpenSpec tool ids (mostly identity).
|
|
209
|
+
* @type {Record<string, string>}
|
|
210
|
+
*/
|
|
211
|
+
const OPENSPEC_TOOL_ID = { copilot: 'github-copilot' };
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {string[]} agentIds forgekit environment ids
|
|
215
|
+
* @returns {string[]} OpenSpec tool ids
|
|
216
|
+
*/
|
|
217
|
+
export function toOpenSpecToolIds(agentIds) {
|
|
218
|
+
return agentIds.map((id) => OPENSPEC_TOOL_ID[id] ?? id);
|
|
219
|
+
}
|
|
220
|
+
|
|
207
221
|
/**
|
|
208
222
|
* Install the OpenSpec CLI (if missing) and run `openspec init` in the project.
|
|
209
|
-
* Interactive: inherits stdio so `openspec init` can prompt
|
|
223
|
+
* Interactive: inherits stdio so `openspec init` can prompt — unless `tools`
|
|
224
|
+
* is given, in which case those tools are configured non-interactively
|
|
225
|
+
* (`openspec init --tools <ids>`), skipping OpenSpec's own tool picker.
|
|
210
226
|
*
|
|
211
227
|
* @param {string} cwd
|
|
212
|
-
* @param {{ runCommand?: Function, interactive?: boolean }} [opts]
|
|
228
|
+
* @param {{ runCommand?: Function, interactive?: boolean, tools?: string[] }} [opts]
|
|
213
229
|
* @returns {{ ok: boolean, steps: { step: string, ok: boolean, detail?: string }[] }}
|
|
214
230
|
*/
|
|
215
231
|
export function setupOpenSpec(cwd, opts = {}) {
|
|
@@ -249,10 +265,12 @@ export function setupOpenSpec(cwd, opts = {}) {
|
|
|
249
265
|
return { ok: true, steps };
|
|
250
266
|
}
|
|
251
267
|
|
|
252
|
-
const
|
|
268
|
+
const tools = opts.tools?.length ? toOpenSpecToolIds(opts.tools) : null;
|
|
269
|
+
const initArgs = tools ? ['init', '--tools', tools.join(',')] : ['init'];
|
|
270
|
+
const init = runInherit('openspec', initArgs);
|
|
253
271
|
const initOk = init.status === 0;
|
|
254
272
|
steps.push({
|
|
255
|
-
step:
|
|
273
|
+
step: `openspec ${initArgs.join(' ')}`,
|
|
256
274
|
ok: initOk,
|
|
257
275
|
detail: initOk ? undefined : String(init.error ?? `exit ${init.status}`),
|
|
258
276
|
});
|
package/src/plan-engine.test.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
saveUserPlanEngine,
|
|
13
13
|
scaffoldSpecs,
|
|
14
14
|
setupOpenSpec,
|
|
15
|
+
toOpenSpecToolIds,
|
|
15
16
|
writeProjectPlanConfig,
|
|
16
17
|
} from './plan-engine.mjs';
|
|
17
18
|
import { loadProjectConfig, loadUserConfig, saveUserConfig } from './adr.mjs';
|
|
@@ -148,6 +149,43 @@ test('setupOpenSpec runs install + init via injected runner', () => {
|
|
|
148
149
|
}
|
|
149
150
|
});
|
|
150
151
|
|
|
152
|
+
test('toOpenSpecToolIds maps copilot → github-copilot, else identity', () => {
|
|
153
|
+
assert.deepEqual(
|
|
154
|
+
toOpenSpecToolIds(['claude', 'cursor', 'codex', 'opencode', 'copilot', 'gemini', 'windsurf']),
|
|
155
|
+
['claude', 'cursor', 'codex', 'opencode', 'github-copilot', 'gemini', 'windsurf'],
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('setupOpenSpec passes selected tools to `openspec init --tools`', () => {
|
|
160
|
+
const cwd = tmpdir('forgekit-openspec-tools-');
|
|
161
|
+
try {
|
|
162
|
+
/** @type {string[]} */
|
|
163
|
+
const calls = [];
|
|
164
|
+
const runCommand = (cmd, args) => {
|
|
165
|
+
const line = [cmd, ...args].join(' ');
|
|
166
|
+
calls.push(line);
|
|
167
|
+
if (line === 'openspec --version') return { status: 0, stdout: '1.0.0' };
|
|
168
|
+
if (line.startsWith('openspec init')) {
|
|
169
|
+
fs.mkdirSync(path.join(cwd, 'openspec'), { recursive: true });
|
|
170
|
+
fs.writeFileSync(path.join(cwd, 'openspec', 'config.yaml'), 'x: 1\n', 'utf8');
|
|
171
|
+
return { status: 0, stdout: '' };
|
|
172
|
+
}
|
|
173
|
+
return { status: 1, stdout: '' };
|
|
174
|
+
};
|
|
175
|
+
const result = setupOpenSpec(cwd, {
|
|
176
|
+
runCommand,
|
|
177
|
+
tools: ['claude', 'cursor', 'codex', 'copilot'],
|
|
178
|
+
});
|
|
179
|
+
assert.equal(result.ok, true);
|
|
180
|
+
assert.ok(
|
|
181
|
+
calls.includes('openspec init --tools claude,cursor,codex,github-copilot'),
|
|
182
|
+
`expected --tools call, got: ${calls.join(' | ')}`,
|
|
183
|
+
);
|
|
184
|
+
} finally {
|
|
185
|
+
fs.rmSync(cwd, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
151
189
|
test('setupOpenSpec reports failure when install fails', () => {
|
|
152
190
|
const cwd = tmpdir('forgekit-openspec-fail-');
|
|
153
191
|
try {
|