@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izkac/forgekit",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Forgekit CLIs — forgekit (install), forge (workflow), review (code review)",
5
5
  "type": "module",
6
6
  "bin": {
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;
@@ -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 init = runInherit('openspec', ['init']);
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: 'openspec init',
273
+ step: `openspec ${initArgs.join(' ')}`,
256
274
  ok: initOk,
257
275
  detail: initOk ? undefined : String(init.error ?? `exit ${init.status}`),
258
276
  });
@@ -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 {