ai-spec-dev 0.30.0 → 0.30.1

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.
@@ -402,8 +402,8 @@ var init_workspace_loader = __esm({
402
402
  const repos = [];
403
403
  for (const entry of entries) {
404
404
  const absPath = path17.join(this.workspaceRoot, entry);
405
- const stat4 = await fs18.stat(absPath).catch(() => null);
406
- if (!stat4 || !stat4.isDirectory()) continue;
405
+ const stat3 = await fs18.stat(absPath).catch(() => null);
406
+ if (!stat3 || !stat3.isDirectory()) continue;
407
407
  if (entry.startsWith(".") || entry === "node_modules") continue;
408
408
  if (names && !names.includes(entry)) continue;
409
409
  const hasManifest = await fs18.pathExists(path17.join(absPath, "package.json")) || await fs18.pathExists(path17.join(absPath, "go.mod")) || await fs18.pathExists(path17.join(absPath, "Cargo.toml")) || await fs18.pathExists(path17.join(absPath, "pom.xml")) || await fs18.pathExists(path17.join(absPath, "build.gradle")) || await fs18.pathExists(path17.join(absPath, "requirements.txt")) || await fs18.pathExists(path17.join(absPath, "pyproject.toml")) || await fs18.pathExists(path17.join(absPath, "composer.json"));
@@ -467,9 +467,9 @@ var init_workspace_loader = __esm({
467
467
 
468
468
  // cli/index.ts
469
469
  import { Command } from "commander";
470
- import * as path23 from "path";
471
- import * as fs24 from "fs-extra";
472
- import chalk19 from "chalk";
470
+ import * as path22 from "path";
471
+ import * as fs23 from "fs-extra";
472
+ import chalk18 from "chalk";
473
473
  import * as dotenv from "dotenv";
474
474
  import { input, confirm as confirm2, select as select3 } from "@inquirer/prompts";
475
475
 
@@ -4849,221 +4849,221 @@ function validateDsl(raw) {
4849
4849
  }
4850
4850
  return { valid: true, dsl: raw };
4851
4851
  }
4852
- function validateFeature(raw, path24, errors) {
4852
+ function validateFeature(raw, path23, errors) {
4853
4853
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4854
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4854
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4855
4855
  return;
4856
4856
  }
4857
4857
  const f = raw;
4858
- requireNonEmptyString(f["id"], `${path24}.id`, errors);
4859
- requireNonEmptyString(f["title"], `${path24}.title`, errors);
4860
- requireNonEmptyString(f["description"], `${path24}.description`, errors);
4858
+ requireNonEmptyString(f["id"], `${path23}.id`, errors);
4859
+ requireNonEmptyString(f["title"], `${path23}.title`, errors);
4860
+ requireNonEmptyString(f["description"], `${path23}.description`, errors);
4861
4861
  }
4862
- function validateModel(raw, path24, errors) {
4862
+ function validateModel(raw, path23, errors) {
4863
4863
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4864
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4864
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4865
4865
  return;
4866
4866
  }
4867
4867
  const m = raw;
4868
- requireNonEmptyString(m["name"], `${path24}.name`, errors);
4868
+ requireNonEmptyString(m["name"], `${path23}.name`, errors);
4869
4869
  if (!Array.isArray(m["fields"])) {
4870
- errors.push({ path: `${path24}.fields`, message: `Must be an array, got: ${typeLabel(m["fields"])}` });
4870
+ errors.push({ path: `${path23}.fields`, message: `Must be an array, got: ${typeLabel(m["fields"])}` });
4871
4871
  } else {
4872
4872
  const fields = m["fields"];
4873
4873
  if (fields.length > MAX_FIELDS_PER_MODEL) {
4874
- errors.push({ path: `${path24}.fields`, message: `Too many fields (${fields.length} > ${MAX_FIELDS_PER_MODEL})` });
4874
+ errors.push({ path: `${path23}.fields`, message: `Too many fields (${fields.length} > ${MAX_FIELDS_PER_MODEL})` });
4875
4875
  }
4876
4876
  for (let j2 = 0; j2 < Math.min(fields.length, MAX_FIELDS_PER_MODEL); j2++) {
4877
- validateModelField(fields[j2], `${path24}.fields[${j2}]`, errors);
4877
+ validateModelField(fields[j2], `${path23}.fields[${j2}]`, errors);
4878
4878
  }
4879
4879
  }
4880
4880
  if (m["relations"] !== void 0) {
4881
4881
  if (!Array.isArray(m["relations"])) {
4882
- errors.push({ path: `${path24}.relations`, message: "Must be an array of strings if present" });
4882
+ errors.push({ path: `${path23}.relations`, message: "Must be an array of strings if present" });
4883
4883
  } else {
4884
4884
  const rels = m["relations"];
4885
4885
  for (let j2 = 0; j2 < rels.length; j2++) {
4886
4886
  if (typeof rels[j2] !== "string") {
4887
- errors.push({ path: `${path24}.relations[${j2}]`, message: "Must be a string" });
4887
+ errors.push({ path: `${path23}.relations[${j2}]`, message: "Must be a string" });
4888
4888
  }
4889
4889
  }
4890
4890
  }
4891
4891
  }
4892
4892
  }
4893
- function validateModelField(raw, path24, errors) {
4893
+ function validateModelField(raw, path23, errors) {
4894
4894
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4895
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4895
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4896
4896
  return;
4897
4897
  }
4898
4898
  const f = raw;
4899
- requireNonEmptyString(f["name"], `${path24}.name`, errors);
4900
- requireNonEmptyString(f["type"], `${path24}.type`, errors);
4899
+ requireNonEmptyString(f["name"], `${path23}.name`, errors);
4900
+ requireNonEmptyString(f["type"], `${path23}.type`, errors);
4901
4901
  if (typeof f["required"] !== "boolean") {
4902
- errors.push({ path: `${path24}.required`, message: `Must be boolean, got: ${typeLabel(f["required"])}` });
4902
+ errors.push({ path: `${path23}.required`, message: `Must be boolean, got: ${typeLabel(f["required"])}` });
4903
4903
  }
4904
4904
  }
4905
- function validateEndpoint(raw, path24, errors) {
4905
+ function validateEndpoint(raw, path23, errors) {
4906
4906
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4907
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4907
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4908
4908
  return;
4909
4909
  }
4910
4910
  const e = raw;
4911
- requireNonEmptyString(e["id"], `${path24}.id`, errors);
4912
- requireNonEmptyString(e["description"], `${path24}.description`, errors);
4911
+ requireNonEmptyString(e["id"], `${path23}.id`, errors);
4912
+ requireNonEmptyString(e["description"], `${path23}.description`, errors);
4913
4913
  if (!VALID_METHODS.includes(e["method"])) {
4914
4914
  errors.push({
4915
- path: `${path24}.method`,
4915
+ path: `${path23}.method`,
4916
4916
  message: `Must be one of ${VALID_METHODS.join("|")}, got: ${JSON.stringify(e["method"])}`
4917
4917
  });
4918
4918
  }
4919
4919
  if (typeof e["path"] !== "string" || !e["path"].startsWith("/")) {
4920
4920
  errors.push({
4921
- path: `${path24}.path`,
4921
+ path: `${path23}.path`,
4922
4922
  message: `Must be a string starting with "/", got: ${JSON.stringify(e["path"])}`
4923
4923
  });
4924
4924
  }
4925
4925
  if (typeof e["auth"] !== "boolean") {
4926
- errors.push({ path: `${path24}.auth`, message: `Must be boolean, got: ${typeLabel(e["auth"])}` });
4926
+ errors.push({ path: `${path23}.auth`, message: `Must be boolean, got: ${typeLabel(e["auth"])}` });
4927
4927
  }
4928
4928
  if (typeof e["successStatus"] !== "number" || e["successStatus"] < 100 || e["successStatus"] > 599) {
4929
4929
  errors.push({
4930
- path: `${path24}.successStatus`,
4930
+ path: `${path23}.successStatus`,
4931
4931
  message: `Must be an HTTP status code (100-599), got: ${JSON.stringify(e["successStatus"])}`
4932
4932
  });
4933
4933
  }
4934
- requireNonEmptyString(e["successDescription"], `${path24}.successDescription`, errors);
4934
+ requireNonEmptyString(e["successDescription"], `${path23}.successDescription`, errors);
4935
4935
  if (e["request"] !== void 0) {
4936
- validateRequestSchema(e["request"], `${path24}.request`, errors);
4936
+ validateRequestSchema(e["request"], `${path23}.request`, errors);
4937
4937
  }
4938
4938
  if (e["errors"] !== void 0) {
4939
4939
  if (!Array.isArray(e["errors"])) {
4940
- errors.push({ path: `${path24}.errors`, message: "Must be an array if present" });
4940
+ errors.push({ path: `${path23}.errors`, message: "Must be an array if present" });
4941
4941
  } else {
4942
4942
  const errs = e["errors"];
4943
4943
  if (errs.length > MAX_ERRORS_PER_ENDPOINT) {
4944
- errors.push({ path: `${path24}.errors`, message: `Too many error entries (${errs.length} > ${MAX_ERRORS_PER_ENDPOINT})` });
4944
+ errors.push({ path: `${path23}.errors`, message: `Too many error entries (${errs.length} > ${MAX_ERRORS_PER_ENDPOINT})` });
4945
4945
  }
4946
4946
  for (let j2 = 0; j2 < Math.min(errs.length, MAX_ERRORS_PER_ENDPOINT); j2++) {
4947
- validateResponseError(errs[j2], `${path24}.errors[${j2}]`, errors);
4947
+ validateResponseError(errs[j2], `${path23}.errors[${j2}]`, errors);
4948
4948
  }
4949
4949
  }
4950
4950
  }
4951
4951
  }
4952
- function validateRequestSchema(raw, path24, errors) {
4952
+ function validateRequestSchema(raw, path23, errors) {
4953
4953
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4954
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4954
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4955
4955
  return;
4956
4956
  }
4957
4957
  const r = raw;
4958
4958
  for (const key of ["body", "query", "params"]) {
4959
4959
  if (r[key] !== void 0) {
4960
- validateFieldMap(r[key], `${path24}.${key}`, errors);
4960
+ validateFieldMap(r[key], `${path23}.${key}`, errors);
4961
4961
  }
4962
4962
  }
4963
4963
  }
4964
- function validateFieldMap(raw, path24, errors) {
4964
+ function validateFieldMap(raw, path23, errors) {
4965
4965
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4966
- errors.push({ path: path24, message: `Must be a flat object (FieldMap), got: ${typeLabel(raw)}` });
4966
+ errors.push({ path: path23, message: `Must be a flat object (FieldMap), got: ${typeLabel(raw)}` });
4967
4967
  return;
4968
4968
  }
4969
4969
  const map = raw;
4970
4970
  for (const [k2, v2] of Object.entries(map)) {
4971
4971
  if (typeof v2 !== "string") {
4972
- errors.push({ path: `${path24}.${k2}`, message: `Value must be a type-description string, got: ${typeLabel(v2)}` });
4972
+ errors.push({ path: `${path23}.${k2}`, message: `Value must be a type-description string, got: ${typeLabel(v2)}` });
4973
4973
  }
4974
4974
  }
4975
4975
  }
4976
- function validateResponseError(raw, path24, errors) {
4976
+ function validateResponseError(raw, path23, errors) {
4977
4977
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4978
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4978
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4979
4979
  return;
4980
4980
  }
4981
4981
  const e = raw;
4982
4982
  if (typeof e["status"] !== "number" || e["status"] < 100 || e["status"] > 599) {
4983
- errors.push({ path: `${path24}.status`, message: `Must be an HTTP status code (100-599), got: ${JSON.stringify(e["status"])}` });
4983
+ errors.push({ path: `${path23}.status`, message: `Must be an HTTP status code (100-599), got: ${JSON.stringify(e["status"])}` });
4984
4984
  }
4985
- requireNonEmptyString(e["code"], `${path24}.code`, errors);
4986
- requireNonEmptyString(e["description"], `${path24}.description`, errors);
4985
+ requireNonEmptyString(e["code"], `${path23}.code`, errors);
4986
+ requireNonEmptyString(e["description"], `${path23}.description`, errors);
4987
4987
  }
4988
- function validateBehavior(raw, path24, errors) {
4988
+ function validateBehavior(raw, path23, errors) {
4989
4989
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
4990
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
4990
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
4991
4991
  return;
4992
4992
  }
4993
4993
  const b = raw;
4994
- requireNonEmptyString(b["id"], `${path24}.id`, errors);
4995
- requireNonEmptyString(b["description"], `${path24}.description`, errors);
4994
+ requireNonEmptyString(b["id"], `${path23}.id`, errors);
4995
+ requireNonEmptyString(b["description"], `${path23}.description`, errors);
4996
4996
  if (b["constraints"] !== void 0) {
4997
4997
  if (!Array.isArray(b["constraints"])) {
4998
- errors.push({ path: `${path24}.constraints`, message: "Must be an array of strings if present" });
4998
+ errors.push({ path: `${path23}.constraints`, message: "Must be an array of strings if present" });
4999
4999
  } else {
5000
5000
  const cs2 = b["constraints"];
5001
5001
  for (let j2 = 0; j2 < cs2.length; j2++) {
5002
5002
  if (typeof cs2[j2] !== "string") {
5003
- errors.push({ path: `${path24}.constraints[${j2}]`, message: "Must be a string" });
5003
+ errors.push({ path: `${path23}.constraints[${j2}]`, message: "Must be a string" });
5004
5004
  }
5005
5005
  }
5006
5006
  }
5007
5007
  }
5008
5008
  }
5009
- function validateComponent(raw, path24, errors) {
5009
+ function validateComponent(raw, path23, errors) {
5010
5010
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
5011
- errors.push({ path: path24, message: `Must be an object, got: ${typeLabel(raw)}` });
5011
+ errors.push({ path: path23, message: `Must be an object, got: ${typeLabel(raw)}` });
5012
5012
  return;
5013
5013
  }
5014
5014
  const c = raw;
5015
- requireNonEmptyString(c["id"], `${path24}.id`, errors);
5016
- requireNonEmptyString(c["name"], `${path24}.name`, errors);
5017
- requireNonEmptyString(c["description"], `${path24}.description`, errors);
5015
+ requireNonEmptyString(c["id"], `${path23}.id`, errors);
5016
+ requireNonEmptyString(c["name"], `${path23}.name`, errors);
5017
+ requireNonEmptyString(c["description"], `${path23}.description`, errors);
5018
5018
  if (c["props"] !== void 0) {
5019
5019
  if (!Array.isArray(c["props"])) {
5020
- errors.push({ path: `${path24}.props`, message: "Must be an array if present" });
5020
+ errors.push({ path: `${path23}.props`, message: "Must be an array if present" });
5021
5021
  } else {
5022
5022
  const props = c["props"];
5023
5023
  for (let j2 = 0; j2 < props.length; j2++) {
5024
5024
  const p = props[j2];
5025
5025
  if (typeof p !== "object" || p === null) {
5026
- errors.push({ path: `${path24}.props[${j2}]`, message: "Must be an object" });
5026
+ errors.push({ path: `${path23}.props[${j2}]`, message: "Must be an object" });
5027
5027
  continue;
5028
5028
  }
5029
- requireNonEmptyString(p["name"], `${path24}.props[${j2}].name`, errors);
5030
- requireNonEmptyString(p["type"], `${path24}.props[${j2}].type`, errors);
5029
+ requireNonEmptyString(p["name"], `${path23}.props[${j2}].name`, errors);
5030
+ requireNonEmptyString(p["type"], `${path23}.props[${j2}].type`, errors);
5031
5031
  if (typeof p["required"] !== "boolean") {
5032
- errors.push({ path: `${path24}.props[${j2}].required`, message: "Must be boolean" });
5032
+ errors.push({ path: `${path23}.props[${j2}].required`, message: "Must be boolean" });
5033
5033
  }
5034
5034
  }
5035
5035
  }
5036
5036
  }
5037
5037
  if (c["events"] !== void 0) {
5038
5038
  if (!Array.isArray(c["events"])) {
5039
- errors.push({ path: `${path24}.events`, message: "Must be an array if present" });
5039
+ errors.push({ path: `${path23}.events`, message: "Must be an array if present" });
5040
5040
  } else {
5041
5041
  const events = c["events"];
5042
5042
  for (let j2 = 0; j2 < events.length; j2++) {
5043
5043
  const e = events[j2];
5044
5044
  if (typeof e !== "object" || e === null) {
5045
- errors.push({ path: `${path24}.events[${j2}]`, message: "Must be an object" });
5045
+ errors.push({ path: `${path23}.events[${j2}]`, message: "Must be an object" });
5046
5046
  continue;
5047
5047
  }
5048
- requireNonEmptyString(e["name"], `${path24}.events[${j2}].name`, errors);
5048
+ requireNonEmptyString(e["name"], `${path23}.events[${j2}].name`, errors);
5049
5049
  }
5050
5050
  }
5051
5051
  }
5052
5052
  if (c["state"] !== void 0) {
5053
5053
  if (typeof c["state"] !== "object" || Array.isArray(c["state"]) || c["state"] === null) {
5054
- errors.push({ path: `${path24}.state`, message: "Must be a flat object (Record<string, string>) if present" });
5054
+ errors.push({ path: `${path23}.state`, message: "Must be a flat object (Record<string, string>) if present" });
5055
5055
  }
5056
5056
  }
5057
5057
  if (c["apiCalls"] !== void 0) {
5058
5058
  if (!Array.isArray(c["apiCalls"])) {
5059
- errors.push({ path: `${path24}.apiCalls`, message: "Must be an array of strings if present" });
5059
+ errors.push({ path: `${path23}.apiCalls`, message: "Must be an array of strings if present" });
5060
5060
  }
5061
5061
  }
5062
5062
  }
5063
- function requireNonEmptyString(v2, path24, errors) {
5063
+ function requireNonEmptyString(v2, path23, errors) {
5064
5064
  if (typeof v2 !== "string" || v2.trim().length === 0) {
5065
5065
  errors.push({
5066
- path: path24,
5066
+ path: path23,
5067
5067
  message: `Must be a non-empty string, got: ${typeLabel(v2)}`
5068
5068
  });
5069
5069
  }
@@ -8318,108 +8318,6 @@ async function clearKey(provider) {
8318
8318
  await writeStore(store);
8319
8319
  }
8320
8320
 
8321
- // cli/welcome.ts
8322
- import chalk17 from "chalk";
8323
- import * as os4 from "os";
8324
- import * as path19 from "path";
8325
- import * as fs20 from "fs-extra";
8326
- var VERSION = "0.14.1";
8327
- var TOTAL_W = 76;
8328
- var L_WIDTH = 44;
8329
- var R_WIDTH = TOTAL_W - L_WIDTH - 4;
8330
- var ROBOT_COLOR = chalk17.hex("#E8885A");
8331
- var ROBOT = [
8332
- ROBOT_COLOR(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),
8333
- ROBOT_COLOR(" \u2502") + chalk17.bold.white(" \u25C9 ") + ROBOT_COLOR(" ") + chalk17.bold.white("\u25C9 ") + ROBOT_COLOR("\u2502"),
8334
- ROBOT_COLOR(" \u2502") + chalk17.dim(" \u2570\u2500\u256F ") + ROBOT_COLOR("\u2502"),
8335
- ROBOT_COLOR(" \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2518"),
8336
- ROBOT_COLOR(" \u2502"),
8337
- ROBOT_COLOR(" \u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500")
8338
- ];
8339
- function visLen(s) {
8340
- return s.replace(/\x1b\[[0-9;]*m/g, "").length;
8341
- }
8342
- function padR(s, width) {
8343
- const vl = visLen(s);
8344
- return vl >= width ? s : s + " ".repeat(width - vl);
8345
- }
8346
- function row(left, right) {
8347
- return padR(left, L_WIDTH) + " " + chalk17.gray("\u2502") + " " + padR(right, R_WIDTH);
8348
- }
8349
- function center(s, width) {
8350
- const vl = visLen(s);
8351
- const pad = Math.max(0, Math.floor((width - vl) / 2));
8352
- return " ".repeat(pad) + s;
8353
- }
8354
- async function getRecentSpecs(dir) {
8355
- const specsDir = path19.join(dir, "specs");
8356
- if (!await fs20.pathExists(specsDir)) return [];
8357
- try {
8358
- const files = await fs20.readdir(specsDir);
8359
- const mdFiles = files.filter((f) => f.endsWith(".md"));
8360
- const withStats = await Promise.all(
8361
- mdFiles.map(async (f) => {
8362
- const stat4 = await fs20.stat(path19.join(specsDir, f));
8363
- return { name: f, mtime: stat4.mtime.getTime() };
8364
- })
8365
- );
8366
- withStats.sort((a, b) => b.mtime - a.mtime);
8367
- return withStats.slice(0, 3).map(({ name, mtime }) => {
8368
- const ms2 = Date.now() - mtime;
8369
- const hours = Math.floor(ms2 / 36e5);
8370
- const days = Math.floor(hours / 24);
8371
- const age = days > 0 ? `${days}d ago` : hours > 0 ? `${hours}h ago` : "just now";
8372
- const slug = name.replace(/\.md$/, "").slice(0, R_WIDTH - age.length - 2);
8373
- return chalk17.white(slug) + chalk17.dim(" " + age);
8374
- });
8375
- } catch {
8376
- return [];
8377
- }
8378
- }
8379
- async function printWelcome(currentDir, config2) {
8380
- const username = os4.userInfo().username;
8381
- const homeDir = os4.homedir();
8382
- const shortDir = currentDir.startsWith(homeDir) ? "~" + currentDir.slice(homeDir.length) : currentDir;
8383
- const recentSpecs = await getRecentSpecs(currentDir);
8384
- const providerBit = config2?.provider ? config2.provider + (config2.model ? " \xB7 " + config2.model : "") : "";
8385
- const bottomRaw = [providerBit, shortDir].filter(Boolean).join(" \xB7 ");
8386
- const maxInfoLen = L_WIDTH - 2;
8387
- const bottomTruncated = bottomRaw.length > maxInfoLen ? bottomRaw.slice(0, maxInfoLen - 1) + "\u2026" : bottomRaw;
8388
- const bottomLine = " " + chalk17.dim(bottomTruncated);
8389
- const titleInner = `ai-spec v${VERSION} `;
8390
- const titleDashes = "\u2500".repeat(Math.max(0, TOTAL_W - titleInner.length - 4));
8391
- console.log(
8392
- "\n" + chalk17.hex("#FF6B35")("\u2500\u2500\u2500 " + titleInner + titleDashes)
8393
- );
8394
- const welcomeText = "Welcome back, " + chalk17.bold.white(username) + "!";
8395
- const leftLines = [
8396
- "",
8397
- center(welcomeText, L_WIDTH),
8398
- "",
8399
- ...ROBOT,
8400
- "",
8401
- bottomLine,
8402
- ""
8403
- ];
8404
- const rightLines = [
8405
- chalk17.hex("#FF8C00").bold("Tips for getting started"),
8406
- chalk17.gray("\u2500".repeat(R_WIDTH)),
8407
- chalk17.white('ai-spec create "feature"'),
8408
- chalk17.gray("ai-spec workspace run"),
8409
- chalk17.gray('ai-spec update "change"'),
8410
- "",
8411
- chalk17.hex("#FF8C00").bold("Recent activity"),
8412
- chalk17.gray("\u2500".repeat(R_WIDTH)),
8413
- ...recentSpecs.length > 0 ? recentSpecs : [chalk17.dim("No recent activity")]
8414
- ];
8415
- const maxLines = Math.max(leftLines.length, rightLines.length);
8416
- for (let i = 0; i < maxLines; i++) {
8417
- console.log(row(leftLines[i] ?? "", rightLines[i] ?? ""));
8418
- }
8419
- console.log(chalk17.gray("\u2500".repeat(TOTAL_W)));
8420
- console.log();
8421
- }
8422
-
8423
8321
  // prompts/global-constitution.prompt.ts
8424
8322
  var globalConstitutionSystemPrompt = `You are a Senior Software Architect. Analyze the provided multi-project context and generate a "Global Constitution" \u2014 a team-level baseline document that captures cross-project rules, shared conventions, and universal constraints that every repository in this workspace must follow.
8425
8323
 
@@ -8929,8 +8827,8 @@ Existing API/service files:`);
8929
8827
  }
8930
8828
 
8931
8829
  // core/mock-server-generator.ts
8932
- import * as path20 from "path";
8933
- import * as fs21 from "fs-extra";
8830
+ import * as path19 from "path";
8831
+ import * as fs20 from "fs-extra";
8934
8832
  import { spawn } from "child_process";
8935
8833
  function typeToFixture(fieldName, typeDesc) {
8936
8834
  const t = typeDesc.toLowerCase();
@@ -9066,22 +8964,22 @@ function generateMockServerJs(dsl, port) {
9066
8964
  }
9067
8965
  function detectFrontendFramework(projectDir) {
9068
8966
  for (const f of ["vite.config.ts", "vite.config.js", "vite.config.mts"]) {
9069
- if (fs21.existsSync(path20.join(projectDir, f))) return "vite";
8967
+ if (fs20.existsSync(path19.join(projectDir, f))) return "vite";
9070
8968
  }
9071
8969
  for (const f of ["next.config.js", "next.config.ts", "next.config.mjs"]) {
9072
- if (fs21.existsSync(path20.join(projectDir, f))) return "next";
8970
+ if (fs20.existsSync(path19.join(projectDir, f))) return "next";
9073
8971
  }
9074
- const pkgPath = path20.join(projectDir, "package.json");
9075
- if (fs21.existsSync(pkgPath)) {
8972
+ const pkgPath = path19.join(projectDir, "package.json");
8973
+ if (fs20.existsSync(pkgPath)) {
9076
8974
  try {
9077
- const pkg = JSON.parse(fs21.readFileSync(pkgPath, "utf-8"));
8975
+ const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
9078
8976
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
9079
8977
  if (deps["react-scripts"]) return "cra";
9080
8978
  } catch {
9081
8979
  }
9082
8980
  }
9083
8981
  for (const f of ["webpack.config.js", "webpack.config.ts"]) {
9084
- if (fs21.existsSync(path20.join(projectDir, f))) return "webpack";
8982
+ if (fs20.existsSync(path19.join(projectDir, f))) return "webpack";
9085
8983
  }
9086
8984
  return "unknown";
9087
8985
  }
@@ -9261,7 +9159,7 @@ export const worker = setupWorker(...handlers);
9261
9159
  var MOCK_LOCK_FILE = ".ai-spec-mock.lock.json";
9262
9160
  function findViteConfigFile(projectDir) {
9263
9161
  for (const f of ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"]) {
9264
- if (fs21.existsSync(path20.join(projectDir, f))) return f;
9162
+ if (fs20.existsSync(path19.join(projectDir, f))) return f;
9265
9163
  }
9266
9164
  return null;
9267
9165
  }
@@ -9307,73 +9205,73 @@ async function applyMockProxy(frontendDir, mockPort, endpoints = []) {
9307
9205
  if (framework === "vite") {
9308
9206
  const viteConfigFile = findViteConfigFile(frontendDir) ?? "vite.config.ts";
9309
9207
  const mockConfigContent = generateViteMockConfigTs(viteConfigFile, mockPort, endpoints);
9310
- const mockConfigPath = path20.join(frontendDir, "vite.config.ai-spec-mock.ts");
9311
- await fs21.writeFile(mockConfigPath, mockConfigContent, "utf-8");
9208
+ const mockConfigPath = path19.join(frontendDir, "vite.config.ai-spec-mock.ts");
9209
+ await fs20.writeFile(mockConfigPath, mockConfigContent, "utf-8");
9312
9210
  actions.push({ type: "wrote-file", filePath: "vite.config.ai-spec-mock.ts" });
9313
- const pkgPath = path20.join(frontendDir, "package.json");
9314
- if (await fs21.pathExists(pkgPath)) {
9315
- const pkg = await fs21.readJson(pkgPath);
9211
+ const pkgPath = path19.join(frontendDir, "package.json");
9212
+ if (await fs20.pathExists(pkgPath)) {
9213
+ const pkg = await fs20.readJson(pkgPath);
9316
9214
  pkg.scripts = pkg.scripts ?? {};
9317
9215
  const originalValue = pkg.scripts["dev:mock"] ?? null;
9318
9216
  pkg.scripts["dev:mock"] = "vite --config vite.config.ai-spec-mock.ts";
9319
- await fs21.writeJson(pkgPath, pkg, { spaces: 2 });
9217
+ await fs20.writeJson(pkgPath, pkg, { spaces: 2 });
9320
9218
  actions.push({ type: "added-pkg-script", key: "dev:mock", originalValue });
9321
9219
  }
9322
9220
  const lock2 = { framework, mockPort, frontendDir, actions };
9323
- await fs21.writeJson(path20.join(frontendDir, MOCK_LOCK_FILE), lock2, { spaces: 2 });
9221
+ await fs20.writeJson(path19.join(frontendDir, MOCK_LOCK_FILE), lock2, { spaces: 2 });
9324
9222
  return { framework, applied: true, devCommand: "npm run dev:mock" };
9325
9223
  }
9326
9224
  if (framework === "cra") {
9327
- const pkgPath = path20.join(frontendDir, "package.json");
9328
- if (await fs21.pathExists(pkgPath)) {
9329
- const pkg = await fs21.readJson(pkgPath);
9225
+ const pkgPath = path19.join(frontendDir, "package.json");
9226
+ if (await fs20.pathExists(pkgPath)) {
9227
+ const pkg = await fs20.readJson(pkgPath);
9330
9228
  const originalProxy = pkg.proxy ?? null;
9331
9229
  pkg.proxy = `http://localhost:${mockPort}`;
9332
- await fs21.writeJson(pkgPath, pkg, { spaces: 2 });
9230
+ await fs20.writeJson(pkgPath, pkg, { spaces: 2 });
9333
9231
  actions.push({ type: "patched-pkg-proxy", originalProxy });
9334
9232
  const lock2 = { framework, mockPort, frontendDir, actions };
9335
- await fs21.writeJson(path20.join(frontendDir, MOCK_LOCK_FILE), lock2, { spaces: 2 });
9233
+ await fs20.writeJson(path19.join(frontendDir, MOCK_LOCK_FILE), lock2, { spaces: 2 });
9336
9234
  return { framework, applied: true, devCommand: "npm start" };
9337
9235
  }
9338
9236
  return { framework, applied: false, devCommand: null, note: "No package.json found." };
9339
9237
  }
9340
9238
  const lock = { framework, mockPort, frontendDir, actions };
9341
- await fs21.writeJson(path20.join(frontendDir, MOCK_LOCK_FILE), lock, { spaces: 2 });
9239
+ await fs20.writeJson(path19.join(frontendDir, MOCK_LOCK_FILE), lock, { spaces: 2 });
9342
9240
  const manualNote = framework === "next" ? `Add rewrites in next.config.js to proxy API calls to http://localhost:${mockPort}` : `Add proxy in webpack.config.js devServer to target http://localhost:${mockPort}`;
9343
9241
  return { framework, applied: false, devCommand: null, note: manualNote };
9344
9242
  }
9345
9243
  async function restoreMockProxy(frontendDir) {
9346
- const lockPath = path20.join(frontendDir, MOCK_LOCK_FILE);
9347
- if (!await fs21.pathExists(lockPath)) {
9244
+ const lockPath = path19.join(frontendDir, MOCK_LOCK_FILE);
9245
+ if (!await fs20.pathExists(lockPath)) {
9348
9246
  return { restored: false, note: "No lock file found \u2014 nothing to restore." };
9349
9247
  }
9350
- const lock = await fs21.readJson(lockPath);
9248
+ const lock = await fs20.readJson(lockPath);
9351
9249
  for (const action of lock.actions) {
9352
9250
  if (action.type === "wrote-file") {
9353
- const fp = path20.join(frontendDir, action.filePath);
9354
- if (await fs21.pathExists(fp)) await fs21.remove(fp);
9251
+ const fp = path19.join(frontendDir, action.filePath);
9252
+ if (await fs20.pathExists(fp)) await fs20.remove(fp);
9355
9253
  } else if (action.type === "added-pkg-script") {
9356
- const pkgPath = path20.join(frontendDir, "package.json");
9357
- if (await fs21.pathExists(pkgPath)) {
9358
- const pkg = await fs21.readJson(pkgPath);
9254
+ const pkgPath = path19.join(frontendDir, "package.json");
9255
+ if (await fs20.pathExists(pkgPath)) {
9256
+ const pkg = await fs20.readJson(pkgPath);
9359
9257
  if (action.originalValue == null) {
9360
9258
  delete pkg.scripts?.[action.key];
9361
9259
  } else {
9362
9260
  pkg.scripts = pkg.scripts ?? {};
9363
9261
  pkg.scripts[action.key] = action.originalValue;
9364
9262
  }
9365
- await fs21.writeJson(pkgPath, pkg, { spaces: 2 });
9263
+ await fs20.writeJson(pkgPath, pkg, { spaces: 2 });
9366
9264
  }
9367
9265
  } else if (action.type === "patched-pkg-proxy") {
9368
- const pkgPath = path20.join(frontendDir, "package.json");
9369
- if (await fs21.pathExists(pkgPath)) {
9370
- const pkg = await fs21.readJson(pkgPath);
9266
+ const pkgPath = path19.join(frontendDir, "package.json");
9267
+ if (await fs20.pathExists(pkgPath)) {
9268
+ const pkg = await fs20.readJson(pkgPath);
9371
9269
  if (action.originalProxy == null) {
9372
9270
  delete pkg.proxy;
9373
9271
  } else {
9374
9272
  pkg.proxy = action.originalProxy;
9375
9273
  }
9376
- await fs21.writeJson(pkgPath, pkg, { spaces: 2 });
9274
+ await fs20.writeJson(pkgPath, pkg, { spaces: 2 });
9377
9275
  }
9378
9276
  }
9379
9277
  }
@@ -9383,7 +9281,7 @@ async function restoreMockProxy(frontendDir) {
9383
9281
  } catch {
9384
9282
  }
9385
9283
  }
9386
- await fs21.remove(lockPath);
9284
+ await fs20.remove(lockPath);
9387
9285
  return { restored: true };
9388
9286
  }
9389
9287
  function startMockServerBackground(serverJsPath, port) {
@@ -9396,23 +9294,23 @@ function startMockServerBackground(serverJsPath, port) {
9396
9294
  return child.pid;
9397
9295
  }
9398
9296
  async function saveMockServerPid(frontendDir, pid) {
9399
- const lockPath = path20.join(frontendDir, MOCK_LOCK_FILE);
9400
- if (await fs21.pathExists(lockPath)) {
9401
- const lock = await fs21.readJson(lockPath);
9297
+ const lockPath = path19.join(frontendDir, MOCK_LOCK_FILE);
9298
+ if (await fs20.pathExists(lockPath)) {
9299
+ const lock = await fs20.readJson(lockPath);
9402
9300
  lock.mockServerPid = pid;
9403
- await fs21.writeJson(lockPath, lock, { spaces: 2 });
9301
+ await fs20.writeJson(lockPath, lock, { spaces: 2 });
9404
9302
  }
9405
9303
  }
9406
9304
  async function generateMockAssets(dsl, projectDir, opts = {}) {
9407
9305
  const port = opts.port ?? 3001;
9408
- const outputDir = path20.join(projectDir, opts.outputDir ?? "mock");
9306
+ const outputDir = path19.join(projectDir, opts.outputDir ?? "mock");
9409
9307
  const result = { files: [] };
9410
- await fs21.ensureDir(outputDir);
9308
+ await fs20.ensureDir(outputDir);
9411
9309
  const serverJs = generateMockServerJs(dsl, port);
9412
- const serverPath = path20.join(outputDir, "server.js");
9413
- await fs21.writeFile(serverPath, serverJs, "utf-8");
9310
+ const serverPath = path19.join(outputDir, "server.js");
9311
+ await fs20.writeFile(serverPath, serverJs, "utf-8");
9414
9312
  result.files.push({
9415
- path: path20.relative(projectDir, serverPath),
9313
+ path: path19.relative(projectDir, serverPath),
9416
9314
  description: `Express mock server \u2014 run with: node mock/server.js`
9417
9315
  });
9418
9316
  const mockReadme = `# Mock Server
@@ -9442,52 +9340,52 @@ ${dsl.endpoints.map(
9442
9340
 
9443
9341
  Append \`?simulate_error=1\` to any request to test error handling (not yet auto-wired \u2014 edit server.js manually).
9444
9342
  `;
9445
- const readmePath = path20.join(outputDir, "README.md");
9446
- await fs21.writeFile(readmePath, mockReadme, "utf-8");
9343
+ const readmePath = path19.join(outputDir, "README.md");
9344
+ await fs20.writeFile(readmePath, mockReadme, "utf-8");
9447
9345
  result.files.push({
9448
- path: path20.relative(projectDir, readmePath),
9346
+ path: path19.relative(projectDir, readmePath),
9449
9347
  description: "Mock server usage guide"
9450
9348
  });
9451
9349
  if (opts.proxy) {
9452
9350
  const { content, filename } = generateProxyConfig(dsl, port, projectDir);
9453
- const proxyPath = path20.join(projectDir, filename);
9454
- await fs21.ensureDir(path20.dirname(proxyPath));
9455
- await fs21.writeFile(proxyPath, content, "utf-8");
9351
+ const proxyPath = path19.join(projectDir, filename);
9352
+ await fs20.ensureDir(path19.dirname(proxyPath));
9353
+ await fs20.writeFile(proxyPath, content, "utf-8");
9456
9354
  result.files.push({
9457
9355
  path: filename,
9458
9356
  description: "Proxy config snippet \u2014 copy instructions into your framework config"
9459
9357
  });
9460
9358
  }
9461
9359
  if (opts.msw) {
9462
- const mswDir = path20.join(projectDir, "src", "mocks");
9463
- await fs21.ensureDir(mswDir);
9360
+ const mswDir = path19.join(projectDir, "src", "mocks");
9361
+ await fs20.ensureDir(mswDir);
9464
9362
  const handlersContent = generateMswHandlers(dsl);
9465
- const handlersPath = path20.join(mswDir, "handlers.ts");
9466
- await fs21.writeFile(handlersPath, handlersContent, "utf-8");
9363
+ const handlersPath = path19.join(mswDir, "handlers.ts");
9364
+ await fs20.writeFile(handlersPath, handlersContent, "utf-8");
9467
9365
  result.files.push({
9468
- path: path20.relative(projectDir, handlersPath),
9366
+ path: path19.relative(projectDir, handlersPath),
9469
9367
  description: "MSW request handlers"
9470
9368
  });
9471
9369
  const browserContent = generateMswBrowser();
9472
- const browserPath = path20.join(mswDir, "browser.ts");
9473
- await fs21.writeFile(browserPath, browserContent, "utf-8");
9370
+ const browserPath = path19.join(mswDir, "browser.ts");
9371
+ await fs20.writeFile(browserPath, browserContent, "utf-8");
9474
9372
  result.files.push({
9475
- path: path20.relative(projectDir, browserPath),
9373
+ path: path19.relative(projectDir, browserPath),
9476
9374
  description: "MSW browser worker setup"
9477
9375
  });
9478
9376
  }
9479
9377
  return result;
9480
9378
  }
9481
9379
  async function findLatestDslFile(projectDir) {
9482
- const specDir = path20.join(projectDir, ".ai-spec");
9483
- if (!await fs21.pathExists(specDir)) return null;
9380
+ const specDir = path19.join(projectDir, ".ai-spec");
9381
+ if (!await fs20.pathExists(specDir)) return null;
9484
9382
  const allFiles = [];
9485
9383
  async function scan(dir) {
9486
- const entries = await fs21.readdir(dir);
9384
+ const entries = await fs20.readdir(dir);
9487
9385
  for (const entry of entries) {
9488
- const abs = path20.join(dir, entry);
9489
- const stat4 = await fs21.stat(abs);
9490
- if (stat4.isDirectory()) {
9386
+ const abs = path19.join(dir, entry);
9387
+ const stat3 = await fs20.stat(abs);
9388
+ if (stat3.isDirectory()) {
9491
9389
  await scan(abs);
9492
9390
  } else if (entry.endsWith(".dsl.json")) {
9493
9391
  allFiles.push(abs);
@@ -9497,16 +9395,16 @@ async function findLatestDslFile(projectDir) {
9497
9395
  await scan(specDir);
9498
9396
  if (allFiles.length === 0) return null;
9499
9397
  const withMtimes = await Promise.all(
9500
- allFiles.map(async (f) => ({ f, mtime: (await fs21.stat(f)).mtime }))
9398
+ allFiles.map(async (f) => ({ f, mtime: (await fs20.stat(f)).mtime }))
9501
9399
  );
9502
9400
  withMtimes.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
9503
9401
  return withMtimes[0].f;
9504
9402
  }
9505
9403
 
9506
9404
  // core/spec-updater.ts
9507
- import chalk18 from "chalk";
9508
- import * as path21 from "path";
9509
- import * as fs22 from "fs-extra";
9405
+ import chalk17 from "chalk";
9406
+ import * as path20 from "path";
9407
+ import * as fs21 from "fs-extra";
9510
9408
 
9511
9409
  // prompts/update.prompt.ts
9512
9410
  var specUpdateSystemPrompt = `You are a Senior Software Architect updating an existing Feature Spec based on a change request.
@@ -9652,8 +9550,8 @@ var SpecUpdater = class {
9652
9550
  * Returns all .md spec files sorted newest-first.
9653
9551
  */
9654
9552
  static async findLatestSpec(specsDir) {
9655
- if (!await fs22.pathExists(specsDir)) return null;
9656
- const files = await fs22.readdir(specsDir);
9553
+ if (!await fs21.pathExists(specsDir)) return null;
9554
+ const files = await fs21.readdir(specsDir);
9657
9555
  const pattern = /^feature-(.+)-v(\d+)\.md$/;
9658
9556
  let latest = null;
9659
9557
  for (const file of files) {
@@ -9661,8 +9559,8 @@ var SpecUpdater = class {
9661
9559
  if (!m) continue;
9662
9560
  const version = parseInt(m[2], 10);
9663
9561
  if (!latest || version > latest.version) {
9664
- const filePath = path21.join(specsDir, file);
9665
- const content = await fs22.readFile(filePath, "utf-8");
9562
+ const filePath = path20.join(specsDir, file);
9563
+ const content = await fs21.readFile(filePath, "utf-8");
9666
9564
  latest = { filePath, version, slug: m[1], content };
9667
9565
  }
9668
9566
  }
@@ -9673,16 +9571,16 @@ var SpecUpdater = class {
9673
9571
  * Generates a new version of the spec, re-extracts the DSL, and identifies affected files.
9674
9572
  */
9675
9573
  async update(changeRequest, existingSpecPath, projectDir, context, opts = {}) {
9676
- const existingSpec = await fs22.readFile(existingSpecPath, "utf-8");
9574
+ const existingSpec = await fs21.readFile(existingSpecPath, "utf-8");
9677
9575
  let existingDsl = null;
9678
9576
  const dslFile = await findLatestDslFile(projectDir);
9679
9577
  if (dslFile) {
9680
9578
  try {
9681
- existingDsl = await fs22.readJson(dslFile);
9579
+ existingDsl = await fs21.readJson(dslFile);
9682
9580
  } catch {
9683
9581
  }
9684
9582
  }
9685
- console.log(chalk18.blue(" [1/3] Generating updated spec..."));
9583
+ console.log(chalk17.blue(" [1/3] Generating updated spec..."));
9686
9584
  const updatePrompt = buildSpecUpdatePrompt(changeRequest, existingSpec, existingDsl, context);
9687
9585
  let updatedSpecContent;
9688
9586
  try {
@@ -9691,15 +9589,15 @@ var SpecUpdater = class {
9691
9589
  } catch (err) {
9692
9590
  throw new Error(`Spec update generation failed: ${err.message}`);
9693
9591
  }
9694
- const specBasename = path21.basename(existingSpecPath);
9592
+ const specBasename = path20.basename(existingSpecPath);
9695
9593
  const slugMatch = specBasename.match(/^feature-(.+)-v\d+\.md$/);
9696
9594
  const slug = slugMatch ? slugMatch[1] : "feature";
9697
- const specsDir = path21.dirname(existingSpecPath);
9595
+ const specsDir = path20.dirname(existingSpecPath);
9698
9596
  const { filePath: newSpecPath, version: newVersion } = await nextVersionPath(specsDir, slug);
9699
- await fs22.ensureDir(specsDir);
9700
- await fs22.writeFile(newSpecPath, updatedSpecContent, "utf-8");
9701
- console.log(chalk18.green(` \u2714 New spec written: ${path21.relative(projectDir, newSpecPath)}`));
9702
- console.log(chalk18.blue(" [2/3] Updating DSL..."));
9597
+ await fs21.ensureDir(specsDir);
9598
+ await fs21.writeFile(newSpecPath, updatedSpecContent, "utf-8");
9599
+ console.log(chalk17.green(` \u2714 New spec written: ${path20.relative(projectDir, newSpecPath)}`));
9600
+ console.log(chalk17.blue(" [2/3] Updating DSL..."));
9703
9601
  let updatedDsl = null;
9704
9602
  let newDslPath = null;
9705
9603
  if (existingDsl) {
@@ -9711,7 +9609,7 @@ var SpecUpdater = class {
9711
9609
  updatedDsl = parsed;
9712
9610
  }
9713
9611
  } catch {
9714
- console.log(chalk18.gray(" Targeted DSL update failed \u2014 falling back to full extraction."));
9612
+ console.log(chalk17.gray(" Targeted DSL update failed \u2014 falling back to full extraction."));
9715
9613
  }
9716
9614
  }
9717
9615
  if (!updatedDsl) {
@@ -9720,15 +9618,15 @@ var SpecUpdater = class {
9720
9618
  }
9721
9619
  if (updatedDsl) {
9722
9620
  const dslPath = newSpecPath.replace(/\.md$/, ".dsl.json");
9723
- await fs22.writeJson(dslPath, updatedDsl, { spaces: 2 });
9621
+ await fs21.writeJson(dslPath, updatedDsl, { spaces: 2 });
9724
9622
  newDslPath = dslPath;
9725
- console.log(chalk18.green(` \u2714 DSL updated: ${path21.relative(projectDir, dslPath)}`));
9623
+ console.log(chalk17.green(` \u2714 DSL updated: ${path20.relative(projectDir, dslPath)}`));
9726
9624
  } else {
9727
- console.log(chalk18.yellow(" \u26A0 DSL update failed \u2014 continuing without DSL."));
9625
+ console.log(chalk17.yellow(" \u26A0 DSL update failed \u2014 continuing without DSL."));
9728
9626
  }
9729
9627
  let affectedFiles = [];
9730
9628
  if (!opts.skipAffectedFiles && updatedDsl && existingDsl && context) {
9731
- console.log(chalk18.blue(" [3/3] Identifying affected files..."));
9629
+ console.log(chalk17.blue(" [3/3] Identifying affected files..."));
9732
9630
  const systemPrompt = getCodeGenSystemPrompt(opts.repoType);
9733
9631
  const affectedPrompt = buildAffectedFilesPrompt(
9734
9632
  changeRequest,
@@ -9739,9 +9637,9 @@ var SpecUpdater = class {
9739
9637
  try {
9740
9638
  const affectedRaw = await this.provider.generate(affectedPrompt, systemPrompt);
9741
9639
  affectedFiles = parseAffectedFiles(affectedRaw);
9742
- console.log(chalk18.green(` \u2714 ${affectedFiles.length} file(s) identified for update`));
9640
+ console.log(chalk17.green(` \u2714 ${affectedFiles.length} file(s) identified for update`));
9743
9641
  } catch {
9744
- console.log(chalk18.gray(" Could not identify affected files \u2014 use manual selection."));
9642
+ console.log(chalk17.gray(" Could not identify affected files \u2014 use manual selection."));
9745
9643
  }
9746
9644
  }
9747
9645
  return { newSpecPath, newVersion, newDslPath, affectedFiles, updatedDsl };
@@ -9749,8 +9647,8 @@ var SpecUpdater = class {
9749
9647
  };
9750
9648
 
9751
9649
  // core/openapi-exporter.ts
9752
- import * as path22 from "path";
9753
- import * as fs23 from "fs-extra";
9650
+ import * as path21 from "path";
9651
+ import * as fs22 from "fs-extra";
9754
9652
  function dslTypeToOASchema(typeDesc, fieldName = "") {
9755
9653
  const t = typeDesc.toLowerCase();
9756
9654
  if (t === "string" || t.includes("string")) {
@@ -9979,7 +9877,7 @@ async function exportOpenApi(dsl, projectDir, opts = {}) {
9979
9877
  const format = opts.format ?? "yaml";
9980
9878
  const serverUrl = opts.serverUrl ?? "http://localhost:3000";
9981
9879
  const defaultName = `openapi.${format}`;
9982
- const outputPath = opts.outputPath ? path22.isAbsolute(opts.outputPath) ? opts.outputPath : path22.join(projectDir, opts.outputPath) : path22.join(projectDir, defaultName);
9880
+ const outputPath = opts.outputPath ? path21.isAbsolute(opts.outputPath) ? opts.outputPath : path21.join(projectDir, opts.outputPath) : path21.join(projectDir, defaultName);
9983
9881
  const doc = dslToOpenApi(dsl, serverUrl);
9984
9882
  let content;
9985
9883
  if (format === "json") {
@@ -9987,8 +9885,8 @@ async function exportOpenApi(dsl, projectDir, opts = {}) {
9987
9885
  } else {
9988
9886
  content = buildYamlDoc(doc);
9989
9887
  }
9990
- await fs23.ensureDir(path22.dirname(outputPath));
9991
- await fs23.writeFile(outputPath, content, "utf-8");
9888
+ await fs22.ensureDir(path21.dirname(outputPath));
9889
+ await fs22.writeFile(outputPath, content, "utf-8");
9992
9890
  return outputPath;
9993
9891
  }
9994
9892
 
@@ -9996,9 +9894,9 @@ async function exportOpenApi(dsl, projectDir, opts = {}) {
9996
9894
  dotenv.config();
9997
9895
  var CONFIG_FILE = ".ai-spec.json";
9998
9896
  async function loadConfig(dir) {
9999
- const p = path23.join(dir, CONFIG_FILE);
10000
- if (await fs24.pathExists(p)) {
10001
- return fs24.readJson(p);
9897
+ const p = path22.join(dir, CONFIG_FILE);
9898
+ if (await fs23.pathExists(p)) {
9899
+ return fs23.readJson(p);
10002
9900
  }
10003
9901
  return {};
10004
9902
  }
@@ -10023,20 +9921,20 @@ async function resolveApiKey(providerName, cliKey) {
10023
9921
  validate: (v2) => v2.trim().length > 0 || "API key cannot be empty"
10024
9922
  });
10025
9923
  await saveKey(providerName, newKey.trim());
10026
- console.log(chalk19.gray(` Key saved to ${KEY_STORE_FILE}`));
9924
+ console.log(chalk18.gray(` Key saved to ${KEY_STORE_FILE}`));
10027
9925
  return newKey.trim();
10028
9926
  }
10029
9927
  function printBanner(opts) {
10030
- console.log(chalk19.blue("\n" + "\u2500".repeat(52)));
10031
- console.log(chalk19.bold(" ai-spec \u2014 AI-driven Development Orchestrator"));
10032
- console.log(chalk19.blue("\u2500".repeat(52)));
10033
- console.log(chalk19.gray(` Spec : ${opts.specProvider} / ${opts.specModel}`));
9928
+ console.log(chalk18.blue("\n" + "\u2500".repeat(52)));
9929
+ console.log(chalk18.bold(" ai-spec \u2014 AI-driven Development Orchestrator"));
9930
+ console.log(chalk18.blue("\u2500".repeat(52)));
9931
+ console.log(chalk18.gray(` Spec : ${opts.specProvider} / ${opts.specModel}`));
10034
9932
  console.log(
10035
- chalk19.gray(
9933
+ chalk18.gray(
10036
9934
  ` Codegen : ${opts.codegenMode} (${opts.codegenProvider} / ${opts.codegenModel})`
10037
9935
  )
10038
9936
  );
10039
- console.log(chalk19.blue("\u2500".repeat(52) + "\n"));
9937
+ console.log(chalk18.blue("\u2500".repeat(52) + "\n"));
10040
9938
  }
10041
9939
  var program = new Command();
10042
9940
  program.name("ai-spec").description("AI-driven Development Orchestrator \u2014 spec, generate, review").version("0.14.1");
@@ -10063,42 +9961,42 @@ program.command("create").description("Generate a feature spec and kick off code
10063
9961
  const workspaceLoader = new WorkspaceLoader(currentDir);
10064
9962
  const workspaceConfig = await workspaceLoader.load();
10065
9963
  if (workspaceConfig) {
10066
- console.log(chalk19.cyan(`
9964
+ console.log(chalk18.cyan(`
10067
9965
  [Workspace] Detected workspace: ${workspaceConfig.name}`));
10068
- console.log(chalk19.gray(` Repos: ${workspaceConfig.repos.map((r) => r.name).join(", ")}`));
9966
+ console.log(chalk18.gray(` Repos: ${workspaceConfig.repos.map((r) => r.name).join(", ")}`));
10069
9967
  const pipelineResults = await runMultiRepoPipeline(idea, workspaceConfig, opts, currentDir, config2);
10070
9968
  if (opts.serve) {
10071
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Auto-serve: starting mock server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9969
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Auto-serve: starting mock server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10072
9970
  const backendResult = pipelineResults.find((r) => r.role === "backend" && r.status === "success" && r.dsl);
10073
9971
  const frontendResult = pipelineResults.find((r) => (r.role === "frontend" || r.role === "mobile") && r.status === "success");
10074
9972
  if (!backendResult) {
10075
- console.log(chalk19.yellow(" No successful backend with DSL found \u2014 skipping auto-serve."));
9973
+ console.log(chalk18.yellow(" No successful backend with DSL found \u2014 skipping auto-serve."));
10076
9974
  } else {
10077
9975
  const mockPort = 3001;
10078
9976
  const mockResult = await generateMockAssets(backendResult.dsl, backendResult.repoAbsPath, { port: mockPort });
10079
- const serverJsPath = path23.join(backendResult.repoAbsPath, "mock", "server.js");
10080
- console.log(chalk19.green(` \u2714 Mock assets generated (${mockResult.files.length} file(s))`));
9977
+ const serverJsPath = path22.join(backendResult.repoAbsPath, "mock", "server.js");
9978
+ console.log(chalk18.green(` \u2714 Mock assets generated (${mockResult.files.length} file(s))`));
10081
9979
  const pid = startMockServerBackground(serverJsPath, mockPort);
10082
- console.log(chalk19.green(` \u2714 Mock server started (PID ${pid}) \u2192 http://localhost:${mockPort}`));
9980
+ console.log(chalk18.green(` \u2714 Mock server started (PID ${pid}) \u2192 http://localhost:${mockPort}`));
10083
9981
  if (frontendResult) {
10084
9982
  const proxyResult = await applyMockProxy(frontendResult.repoAbsPath, mockPort, backendResult.dsl.endpoints);
10085
9983
  await saveMockServerPid(frontendResult.repoAbsPath, pid);
10086
9984
  if (proxyResult.applied) {
10087
- console.log(chalk19.green(` \u2714 Frontend proxy patched (${proxyResult.framework})`));
10088
- console.log(chalk19.bold.cyan(`
9985
+ console.log(chalk18.green(` \u2714 Frontend proxy patched (${proxyResult.framework})`));
9986
+ console.log(chalk18.bold.cyan(`
10089
9987
  Ready! Run your frontend dev server:`));
10090
- console.log(chalk19.white(` cd ${frontendResult.repoAbsPath}`));
10091
- console.log(chalk19.white(` ${proxyResult.devCommand}`));
10092
- console.log(chalk19.gray(`
9988
+ console.log(chalk18.white(` cd ${frontendResult.repoAbsPath}`));
9989
+ console.log(chalk18.white(` ${proxyResult.devCommand}`));
9990
+ console.log(chalk18.gray(`
10093
9991
  When done, restore: ai-spec mock --restore --frontend ${frontendResult.repoAbsPath}`));
10094
9992
  } else {
10095
- console.log(chalk19.yellow(` \u26A0 Auto-patch not available for ${proxyResult.framework}.`));
10096
- if (proxyResult.note) console.log(chalk19.gray(` ${proxyResult.note}`));
10097
- console.log(chalk19.gray(` Mock server: http://localhost:${mockPort}`));
9993
+ console.log(chalk18.yellow(` \u26A0 Auto-patch not available for ${proxyResult.framework}.`));
9994
+ if (proxyResult.note) console.log(chalk18.gray(` ${proxyResult.note}`));
9995
+ console.log(chalk18.gray(` Mock server: http://localhost:${mockPort}`));
10098
9996
  }
10099
9997
  } else {
10100
- console.log(chalk19.gray(` No frontend repo found \u2014 mock server is running at http://localhost:${mockPort}`));
10101
- console.log(chalk19.gray(` Configure your frontend proxy manually to point to http://localhost:${mockPort}`));
9998
+ console.log(chalk18.gray(` No frontend repo found \u2014 mock server is running at http://localhost:${mockPort}`));
9999
+ console.log(chalk18.gray(` Configure your frontend proxy manually to point to http://localhost:${mockPort}`));
10102
10000
  }
10103
10001
  }
10104
10002
  }
@@ -10119,7 +10017,7 @@ program.command("create").description("Generate a feature spec and kick off code
10119
10017
  codegenModel: codegenModelName
10120
10018
  });
10121
10019
  const runId = generateRunId();
10122
- console.log(chalk19.gray(` Run ID: ${runId}`));
10020
+ console.log(chalk18.gray(` Run ID: ${runId}`));
10123
10021
  const runSnapshot = new RunSnapshot(currentDir, runId);
10124
10022
  setActiveSnapshot(runSnapshot);
10125
10023
  const runLogger = new RunLogger(currentDir, runId, {
@@ -10127,25 +10025,25 @@ program.command("create").description("Generate a feature spec and kick off code
10127
10025
  model: specModelName
10128
10026
  });
10129
10027
  setActiveLogger(runLogger);
10130
- console.log(chalk19.blue("[1/6] Loading project context..."));
10028
+ console.log(chalk18.blue("[1/6] Loading project context..."));
10131
10029
  runLogger.stageStart("context_load");
10132
10030
  const loader = new ContextLoader(currentDir);
10133
10031
  const context = await loader.loadProjectContext();
10134
10032
  const { type: detectedRepoType } = await detectRepoType(currentDir);
10135
10033
  runLogger.stageEnd("context_load", { techStack: context.techStack, repoType: detectedRepoType });
10136
- console.log(chalk19.gray(` Tech stack : ${context.techStack.join(", ") || "unknown"} [${detectedRepoType}]`));
10137
- console.log(chalk19.gray(` Dependencies: ${context.dependencies.length} packages`));
10138
- console.log(chalk19.gray(` API files : ${context.apiStructure.length} files`));
10034
+ console.log(chalk18.gray(` Tech stack : ${context.techStack.join(", ") || "unknown"} [${detectedRepoType}]`));
10035
+ console.log(chalk18.gray(` Dependencies: ${context.dependencies.length} packages`));
10036
+ console.log(chalk18.gray(` API files : ${context.apiStructure.length} files`));
10139
10037
  if (context.schema) {
10140
- console.log(chalk19.gray(` Prisma schema: found`));
10038
+ console.log(chalk18.gray(` Prisma schema: found`));
10141
10039
  }
10142
10040
  if (context.constitution) {
10143
- console.log(chalk19.green(` Constitution : found (.ai-spec-constitution.md)`));
10041
+ console.log(chalk18.green(` Constitution : found (.ai-spec-constitution.md)`));
10144
10042
  if (context.constitution.length > 6e3) {
10145
- console.log(chalk19.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
10043
+ console.log(chalk18.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
10146
10044
  }
10147
10045
  } else {
10148
- console.log(chalk19.yellow(" Constitution : not found \u2014 auto-generating..."));
10046
+ console.log(chalk18.yellow(" Constitution : not found \u2014 auto-generating..."));
10149
10047
  try {
10150
10048
  const constitutionGen = new ConstitutionGenerator(
10151
10049
  createProvider(specProviderName, specApiKey, specModelName)
@@ -10153,12 +10051,12 @@ program.command("create").description("Generate a feature spec and kick off code
10153
10051
  const constitutionContent = await constitutionGen.generate(currentDir);
10154
10052
  await constitutionGen.saveConstitution(currentDir, constitutionContent);
10155
10053
  context.constitution = constitutionContent;
10156
- console.log(chalk19.green(` Constitution : \u2714 generated and saved (.ai-spec-constitution.md)`));
10054
+ console.log(chalk18.green(` Constitution : \u2714 generated and saved (.ai-spec-constitution.md)`));
10157
10055
  } catch (err) {
10158
- console.log(chalk19.yellow(` Constitution : \u26A0 auto-generation failed (${err.message}), continuing without it.`));
10056
+ console.log(chalk18.yellow(` Constitution : \u26A0 auto-generation failed (${err.message}), continuing without it.`));
10159
10057
  }
10160
10058
  }
10161
- console.log(chalk19.blue(`
10059
+ console.log(chalk18.blue(`
10162
10060
  [2/6] Generating spec with ${specProviderName}/${specModelName}...`));
10163
10061
  const specProvider = createProvider(specProviderName, specApiKey, specModelName);
10164
10062
  let initialSpec;
@@ -10168,30 +10066,30 @@ program.command("create").description("Generate a feature spec and kick off code
10168
10066
  if (opts.skipTasks) {
10169
10067
  const generator = new SpecGenerator(specProvider);
10170
10068
  initialSpec = await generator.generateSpec(idea, context);
10171
- console.log(chalk19.green(" \u2714 Spec generated."));
10069
+ console.log(chalk18.green(" \u2714 Spec generated."));
10172
10070
  } else {
10173
10071
  const result = await generateSpecWithTasks(specProvider, idea, context);
10174
10072
  initialSpec = result.spec;
10175
10073
  initialTasks = result.tasks;
10176
- console.log(chalk19.green(` \u2714 Spec generated.`));
10074
+ console.log(chalk18.green(` \u2714 Spec generated.`));
10177
10075
  if (initialTasks.length > 0) {
10178
- console.log(chalk19.green(` \u2714 ${initialTasks.length} tasks generated (combined call).`));
10076
+ console.log(chalk18.green(` \u2714 ${initialTasks.length} tasks generated (combined call).`));
10179
10077
  } else {
10180
- console.log(chalk19.yellow(" \u26A0 Tasks not parsed from response \u2014 will retry separately after refinement."));
10078
+ console.log(chalk18.yellow(" \u26A0 Tasks not parsed from response \u2014 will retry separately after refinement."));
10181
10079
  }
10182
10080
  }
10183
10081
  runLogger.stageEnd("spec_gen", { taskCount: initialTasks.length });
10184
10082
  } catch (err) {
10185
10083
  runLogger.stageFail("spec_gen", err.message);
10186
- console.error(chalk19.red(" \u2718 Spec generation failed:"), err);
10084
+ console.error(chalk18.red(" \u2718 Spec generation failed:"), err);
10187
10085
  process.exit(1);
10188
10086
  }
10189
10087
  let finalSpec;
10190
10088
  if (opts.fast) {
10191
- console.log(chalk19.gray("\n[3/6] Skipping refinement (--fast)."));
10089
+ console.log(chalk18.gray("\n[3/6] Skipping refinement (--fast)."));
10192
10090
  finalSpec = initialSpec;
10193
10091
  } else {
10194
- console.log(chalk19.blue("\n[3/6] Interactive spec refinement..."));
10092
+ console.log(chalk18.blue("\n[3/6] Interactive spec refinement..."));
10195
10093
  runLogger.stageStart("spec_refine");
10196
10094
  const refiner = new SpecRefiner(specProvider);
10197
10095
  finalSpec = await refiner.refineLoop(initialSpec);
@@ -10202,7 +10100,7 @@ program.command("create").description("Generate a feature spec and kick off code
10202
10100
  const shouldRunAssessment = !opts.skipAssessment && (!opts.auto || minScore > 0);
10203
10101
  if (shouldRunAssessment) {
10204
10102
  if (!opts.auto) {
10205
- console.log(chalk19.blue("\n[3.4/6] Spec quality assessment..."));
10103
+ console.log(chalk18.blue("\n[3.4/6] Spec quality assessment..."));
10206
10104
  }
10207
10105
  runLogger.stageStart("spec_assess");
10208
10106
  const assessment = await assessSpec(specProvider, finalSpec, context.constitution ?? void 0);
@@ -10211,45 +10109,45 @@ program.command("create").description("Generate a feature spec and kick off code
10211
10109
  if (!opts.auto) printSpecAssessment(assessment);
10212
10110
  if (minScore > 0 && assessment.overallScore < minScore) {
10213
10111
  if (opts.force) {
10214
- console.log(chalk19.yellow(`
10112
+ console.log(chalk18.yellow(`
10215
10113
  \u26A0 Score gate: ${assessment.overallScore}/10 < minimum ${minScore}/10 \u2014 bypassed with --force.`));
10216
10114
  } else {
10217
10115
  runLogger.stageFail("spec_assess", `Score gate: ${assessment.overallScore} < ${minScore}`);
10218
- console.log(chalk19.red(`
10116
+ console.log(chalk18.red(`
10219
10117
  \u2718 Spec quality gate failed: overallScore ${assessment.overallScore}/10 < minimum ${minScore}/10`));
10220
10118
  if (!opts.auto) {
10221
- console.log(chalk19.gray(` Address the issues above and re-run, or use --force to bypass.`));
10119
+ console.log(chalk18.gray(` Address the issues above and re-run, or use --force to bypass.`));
10222
10120
  } else {
10223
- console.log(chalk19.gray(` Auto mode: gate enforced. Fix the spec or lower minSpecScore, or use --force to bypass.`));
10121
+ console.log(chalk18.gray(` Auto mode: gate enforced. Fix the spec or lower minSpecScore, or use --force to bypass.`));
10224
10122
  }
10225
- console.log(chalk19.gray(` Gate threshold set in .ai-spec.json \u2192 "minSpecScore": ${minScore}`));
10123
+ console.log(chalk18.gray(` Gate threshold set in .ai-spec.json \u2192 "minSpecScore": ${minScore}`));
10226
10124
  process.exit(1);
10227
10125
  }
10228
10126
  }
10229
10127
  } else {
10230
10128
  runLogger.stageEnd("spec_assess", { skipped: true });
10231
10129
  if (!opts.auto) {
10232
- console.log(chalk19.gray(" (Assessment skipped \u2014 AI call failed or timed out)"));
10130
+ console.log(chalk18.gray(" (Assessment skipped \u2014 AI call failed or timed out)"));
10233
10131
  }
10234
10132
  }
10235
10133
  }
10236
10134
  if (!opts.auto) {
10237
- console.log(chalk19.blue("\n[3.5/6] Approval Gate \u2014 review before code generation"));
10135
+ console.log(chalk18.blue("\n[3.5/6] Approval Gate \u2014 review before code generation"));
10238
10136
  const specLines = finalSpec.split("\n").length;
10239
10137
  const specWords = finalSpec.split(/\s+/).length;
10240
10138
  const taskCountHint = initialTasks.length > 0 ? ` Tasks generated : ${initialTasks.length}` : "";
10241
- console.log(chalk19.gray(` Spec length : ${specLines} lines / ${specWords} words`));
10242
- if (taskCountHint) console.log(chalk19.gray(taskCountHint));
10243
- const previewSpecsDir = path23.join(currentDir, "specs");
10139
+ console.log(chalk18.gray(` Spec length : ${specLines} lines / ${specWords} words`));
10140
+ if (taskCountHint) console.log(chalk18.gray(taskCountHint));
10141
+ const previewSpecsDir = path22.join(currentDir, "specs");
10244
10142
  const slug = featureSlug;
10245
10143
  const prevVersion = await findLatestVersion(previewSpecsDir, slug);
10246
10144
  if (prevVersion) {
10247
- console.log(chalk19.gray(` Previous version: v${prevVersion.version} (${prevVersion.filePath})`));
10145
+ console.log(chalk18.gray(` Previous version: v${prevVersion.version} (${prevVersion.filePath})`));
10248
10146
  const diff = computeDiff(prevVersion.content, finalSpec);
10249
- console.log(chalk19.cyan("\n \u2500\u2500 Changes vs previous version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10147
+ console.log(chalk18.cyan("\n \u2500\u2500 Changes vs previous version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10250
10148
  printDiffSummary(diff, `v${prevVersion.version} \u2192 v${prevVersion.version + 1}`);
10251
10149
  printDiff(diff);
10252
- console.log(chalk19.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10150
+ console.log(chalk18.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10253
10151
  }
10254
10152
  const gate = await select3({
10255
10153
  message: "Ready to proceed to code generation?",
@@ -10260,9 +10158,9 @@ program.command("create").description("Generate a feature spec and kick off code
10260
10158
  ]
10261
10159
  });
10262
10160
  if (gate === "view") {
10263
- console.log(chalk19.cyan("\n" + "\u2500".repeat(52)));
10161
+ console.log(chalk18.cyan("\n" + "\u2500".repeat(52)));
10264
10162
  console.log(finalSpec);
10265
- console.log(chalk19.cyan("\u2500".repeat(52) + "\n"));
10163
+ console.log(chalk18.cyan("\u2500".repeat(52) + "\n"));
10266
10164
  const confirm22 = await select3({
10267
10165
  message: "Proceed to code generation?",
10268
10166
  choices: [
@@ -10271,92 +10169,92 @@ program.command("create").description("Generate a feature spec and kick off code
10271
10169
  ]
10272
10170
  });
10273
10171
  if (confirm22 === "abort") {
10274
- console.log(chalk19.yellow(" Aborted. Spec was NOT saved."));
10172
+ console.log(chalk18.yellow(" Aborted. Spec was NOT saved."));
10275
10173
  process.exit(0);
10276
10174
  }
10277
10175
  } else if (gate === "abort") {
10278
- console.log(chalk19.yellow(" Aborted. Spec was NOT saved."));
10176
+ console.log(chalk18.yellow(" Aborted. Spec was NOT saved."));
10279
10177
  process.exit(0);
10280
10178
  }
10281
- console.log(chalk19.green(" \u2714 Approved \u2014 continuing to code generation."));
10179
+ console.log(chalk18.green(" \u2714 Approved \u2014 continuing to code generation."));
10282
10180
  } else {
10283
- console.log(chalk19.gray("[3.5/6] Approval Gate: skipped (--auto)."));
10181
+ console.log(chalk18.gray("[3.5/6] Approval Gate: skipped (--auto)."));
10284
10182
  }
10285
10183
  let extractedDsl = null;
10286
10184
  if (opts.skipDsl) {
10287
- console.log(chalk19.gray("\n[DSL] Skipped (--skip-dsl)."));
10185
+ console.log(chalk18.gray("\n[DSL] Skipped (--skip-dsl)."));
10288
10186
  } else {
10289
- console.log(chalk19.blue("\n[DSL] Extracting structured DSL from spec..."));
10290
- console.log(chalk19.gray(` Provider: ${specProviderName}/${specModelName}`));
10187
+ console.log(chalk18.blue("\n[DSL] Extracting structured DSL from spec..."));
10188
+ console.log(chalk18.gray(` Provider: ${specProviderName}/${specModelName}`));
10291
10189
  runLogger.stageStart("dsl_extract");
10292
10190
  try {
10293
10191
  const isFrontend = isFrontendDeps(context.dependencies);
10294
- if (isFrontend) console.log(chalk19.gray(" Frontend project detected \u2014 using ComponentSpec extractor"));
10192
+ if (isFrontend) console.log(chalk18.gray(" Frontend project detected \u2014 using ComponentSpec extractor"));
10295
10193
  const dslExtractor = new DslExtractor(specProvider);
10296
10194
  extractedDsl = await dslExtractor.extract(finalSpec, { auto: opts.auto, isFrontend });
10297
10195
  if (extractedDsl) {
10298
10196
  runLogger.stageEnd("dsl_extract", { endpoints: extractedDsl.endpoints?.length ?? 0, models: extractedDsl.models?.length ?? 0 });
10299
- console.log(chalk19.green(" \u2714 DSL extracted and validated."));
10197
+ console.log(chalk18.green(" \u2714 DSL extracted and validated."));
10300
10198
  } else {
10301
10199
  runLogger.stageEnd("dsl_extract", { skipped: true });
10302
- console.log(chalk19.yellow(" \u26A0 DSL skipped \u2014 codegen will use Spec + Tasks only."));
10200
+ console.log(chalk18.yellow(" \u26A0 DSL skipped \u2014 codegen will use Spec + Tasks only."));
10303
10201
  }
10304
10202
  } catch (err) {
10305
10203
  runLogger.stageFail("dsl_extract", err.message);
10306
- console.log(chalk19.yellow(` \u26A0 DSL extraction error: ${err.message} \u2014 continuing without DSL.`));
10204
+ console.log(chalk18.yellow(` \u26A0 DSL extraction error: ${err.message} \u2014 continuing without DSL.`));
10307
10205
  }
10308
10206
  }
10309
10207
  const isFrontendProject2 = isFrontendDeps(context.dependencies ?? []);
10310
10208
  const skipWorktree = opts.worktree ? false : opts.skipWorktree || isFrontendProject2;
10311
10209
  let workingDir = currentDir;
10312
10210
  if (!skipWorktree) {
10313
- console.log(chalk19.blue("\n[4/6] Setting up git worktree..."));
10211
+ console.log(chalk18.blue("\n[4/6] Setting up git worktree..."));
10314
10212
  const worktreeManager = new GitWorktreeManager(currentDir);
10315
10213
  const worktreePath = await worktreeManager.createWorktree(idea);
10316
10214
  if (worktreePath) workingDir = worktreePath;
10317
10215
  } else {
10318
10216
  const reason = opts.worktree ? "" : isFrontendProject2 ? " (frontend project \u2014 use --worktree to override)" : " (--skip-worktree)";
10319
- console.log(chalk19.gray(`[4/6] Skipping worktree${reason}.`));
10217
+ console.log(chalk18.gray(`[4/6] Skipping worktree${reason}.`));
10320
10218
  }
10321
- const specsDir = path23.join(workingDir, "specs");
10322
- await fs24.ensureDir(specsDir);
10219
+ const specsDir = path22.join(workingDir, "specs");
10220
+ await fs23.ensureDir(specsDir);
10323
10221
  const { filePath: specFile, version: specVersion } = await nextVersionPath(specsDir, featureSlug);
10324
- await fs24.writeFile(specFile, finalSpec, "utf-8");
10325
- console.log(chalk19.green(`
10326
- [5/6] \u2714 Spec saved: ${specFile}`) + chalk19.gray(` (v${specVersion})`));
10222
+ await fs23.writeFile(specFile, finalSpec, "utf-8");
10223
+ console.log(chalk18.green(`
10224
+ [5/6] \u2714 Spec saved: ${specFile}`) + chalk18.gray(` (v${specVersion})`));
10327
10225
  let savedDslFile = null;
10328
10226
  if (extractedDsl) {
10329
10227
  const dslExtractor = new DslExtractor(specProvider);
10330
10228
  savedDslFile = await dslExtractor.saveDsl(extractedDsl, specFile);
10331
- console.log(chalk19.green(` \u2714 DSL saved : ${savedDslFile}`));
10229
+ console.log(chalk18.green(` \u2714 DSL saved : ${savedDslFile}`));
10332
10230
  }
10333
10231
  if (!opts.skipTasks) {
10334
10232
  const taskGen = new TaskGenerator(specProvider);
10335
10233
  let tasksToSave = initialTasks;
10336
10234
  if (tasksToSave.length === 0) {
10337
- console.log(chalk19.blue(`
10235
+ console.log(chalk18.blue(`
10338
10236
  Generating tasks (separate call)...`));
10339
10237
  try {
10340
10238
  tasksToSave = await taskGen.generateTasks(finalSpec, context);
10341
10239
  } catch (err) {
10342
- console.log(chalk19.yellow(` \u26A0 Task generation failed: ${err.message}`));
10240
+ console.log(chalk18.yellow(` \u26A0 Task generation failed: ${err.message}`));
10343
10241
  }
10344
10242
  }
10345
10243
  if (tasksToSave.length > 0) {
10346
10244
  const sorted = taskGen.sortByLayer(tasksToSave);
10347
10245
  const tasksFile = await taskGen.saveTasks(sorted, specFile);
10348
10246
  printTasks(sorted);
10349
- console.log(chalk19.green(` \u2714 Tasks saved: ${tasksFile}`));
10247
+ console.log(chalk18.green(` \u2714 Tasks saved: ${tasksFile}`));
10350
10248
  } else {
10351
- console.log(chalk19.yellow(" \u26A0 No tasks generated \u2014 code generation will use fallback file planning."));
10249
+ console.log(chalk18.yellow(" \u26A0 No tasks generated \u2014 code generation will use fallback file planning."));
10352
10250
  }
10353
10251
  }
10354
- console.log(chalk19.blue(`
10252
+ console.log(chalk18.blue(`
10355
10253
  [6/6] Code generation (mode: ${codegenMode})...`));
10356
10254
  const codegenProvider = codegenProviderName === specProviderName && codegenApiKey === specApiKey ? specProvider : createProvider(codegenProviderName, codegenApiKey, codegenModelName);
10357
10255
  let generatedTestFiles = [];
10358
10256
  if (opts.tdd && extractedDsl) {
10359
- console.log(chalk19.cyan("\n[TDD] Generating pre-implementation tests (will fail until code is written)..."));
10257
+ console.log(chalk18.cyan("\n[TDD] Generating pre-implementation tests (will fail until code is written)..."));
10360
10258
  const testGen = new TestGenerator(codegenProvider);
10361
10259
  generatedTestFiles = await testGen.generateTdd(extractedDsl, workingDir);
10362
10260
  }
@@ -10370,13 +10268,13 @@ program.command("create").description("Generate a feature spec and kick off code
10370
10268
  });
10371
10269
  runLogger.stageEnd("codegen", { filesGenerated: generatedFiles.length });
10372
10270
  if (opts.tdd) {
10373
- console.log(chalk19.gray("\n[7/9] TDD mode \u2014 test files already written pre-implementation."));
10271
+ console.log(chalk18.gray("\n[7/9] TDD mode \u2014 test files already written pre-implementation."));
10374
10272
  } else if (opts.skipTests) {
10375
- console.log(chalk19.gray("\n[7/9] Skipping test generation (--skip-tests)."));
10273
+ console.log(chalk18.gray("\n[7/9] Skipping test generation (--skip-tests)."));
10376
10274
  } else if (!extractedDsl) {
10377
- console.log(chalk19.gray("\n[7/9] Skipping test generation (no DSL available)."));
10275
+ console.log(chalk18.gray("\n[7/9] Skipping test generation (no DSL available)."));
10378
10276
  } else {
10379
- console.log(chalk19.blue(`
10277
+ console.log(chalk18.blue(`
10380
10278
  [7/9] Test skeleton generation...`));
10381
10279
  runLogger.stageStart("test_gen");
10382
10280
  const testGen = new TestGenerator(codegenProvider);
@@ -10384,10 +10282,10 @@ program.command("create").description("Generate a feature spec and kick off code
10384
10282
  runLogger.stageEnd("test_gen", { filesGenerated: generatedTestFiles.length });
10385
10283
  }
10386
10284
  if (opts.skipErrorFeedback) {
10387
- console.log(chalk19.gray("[8/9] Skipping error feedback (--skip-error-feedback)."));
10285
+ console.log(chalk18.gray("[8/9] Skipping error feedback (--skip-error-feedback)."));
10388
10286
  } else {
10389
10287
  if (opts.tdd) {
10390
- console.log(chalk19.cyan("[8/9] TDD mode \u2014 error feedback loop driving implementation to pass tests..."));
10288
+ console.log(chalk18.cyan("[8/9] TDD mode \u2014 error feedback loop driving implementation to pass tests..."));
10391
10289
  }
10392
10290
  runLogger.stageStart("error_feedback");
10393
10291
  await runErrorFeedback(codegenProvider, workingDir, extractedDsl, {
@@ -10398,10 +10296,10 @@ program.command("create").description("Generate a feature spec and kick off code
10398
10296
  }
10399
10297
  let reviewResult = "";
10400
10298
  if (!opts.skipReview) {
10401
- console.log(chalk19.blue("\n[9/9] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
10299
+ console.log(chalk18.blue("\n[9/9] Automated code review (3-pass: architecture + implementation + impact/complexity)..."));
10402
10300
  runLogger.stageStart("review");
10403
10301
  const reviewer = new CodeReviewer(specProvider, currentDir);
10404
- const savedSpec = await fs24.readFile(specFile, "utf-8");
10302
+ const savedSpec = await fs23.readFile(specFile, "utf-8");
10405
10303
  if (codegenMode === "api" && generatedFiles.length > 0) {
10406
10304
  reviewResult = await reviewer.reviewFiles(savedSpec, generatedFiles, workingDir, specFile);
10407
10305
  } else {
@@ -10417,19 +10315,19 @@ program.command("create").description("Generate a feature spec and kick off code
10417
10315
  await accumulateReviewKnowledge(specProvider, currentDir, reviewResult);
10418
10316
  }
10419
10317
  runLogger.finish();
10420
- console.log(chalk19.bold.green("\n\u2714 All done!"));
10421
- console.log(chalk19.gray(` Spec : ${specFile}`));
10422
- if (savedDslFile) console.log(chalk19.gray(` DSL : ${savedDslFile}`));
10318
+ console.log(chalk18.bold.green("\n\u2714 All done!"));
10319
+ console.log(chalk18.gray(` Spec : ${specFile}`));
10320
+ if (savedDslFile) console.log(chalk18.gray(` DSL : ${savedDslFile}`));
10423
10321
  if (generatedTestFiles.length > 0) {
10424
- console.log(chalk19.gray(` Tests : ${generatedTestFiles.length} skeleton file(s) generated`));
10322
+ console.log(chalk18.gray(` Tests : ${generatedTestFiles.length} skeleton file(s) generated`));
10425
10323
  }
10426
- console.log(chalk19.gray(` Working dir : ${workingDir}`));
10324
+ console.log(chalk18.gray(` Working dir : ${workingDir}`));
10427
10325
  if (workingDir !== currentDir) {
10428
- console.log(chalk19.gray(` Run \`cd ${workingDir}\` to enter the worktree.`));
10326
+ console.log(chalk18.gray(` Run \`cd ${workingDir}\` to enter the worktree.`));
10429
10327
  }
10430
10328
  runLogger.printSummary();
10431
10329
  if (runSnapshot.fileCount > 0) {
10432
- console.log(chalk19.gray(` To undo changes: ai-spec restore ${runId}`));
10330
+ console.log(chalk18.gray(` To undo changes: ai-spec restore ${runId}`));
10433
10331
  }
10434
10332
  });
10435
10333
  program.command("review").description("Run AI code review on current git diff against a spec").argument("[specFile]", "Path to spec file (auto-detects latest in specs/ if omitted)").option(
@@ -10446,24 +10344,24 @@ program.command("review").description("Run AI code review on current git diff ag
10446
10344
  const reviewer = new CodeReviewer(provider, currentDir);
10447
10345
  let specContent = "";
10448
10346
  let resolvedSpecFile;
10449
- if (specFile && await fs24.pathExists(specFile)) {
10450
- specContent = await fs24.readFile(specFile, "utf-8");
10347
+ if (specFile && await fs23.pathExists(specFile)) {
10348
+ specContent = await fs23.readFile(specFile, "utf-8");
10451
10349
  resolvedSpecFile = specFile;
10452
- console.log(chalk19.gray(`Using spec: ${specFile}`));
10350
+ console.log(chalk18.gray(`Using spec: ${specFile}`));
10453
10351
  } else {
10454
- const specsDir = path23.join(currentDir, "specs");
10455
- if (await fs24.pathExists(specsDir)) {
10456
- const files = (await fs24.readdir(specsDir)).filter((f) => f.endsWith(".md")).sort().reverse();
10352
+ const specsDir = path22.join(currentDir, "specs");
10353
+ if (await fs23.pathExists(specsDir)) {
10354
+ const files = (await fs23.readdir(specsDir)).filter((f) => f.endsWith(".md")).sort().reverse();
10457
10355
  if (files.length > 0) {
10458
- const latest = path23.join(specsDir, files[0]);
10459
- specContent = await fs24.readFile(latest, "utf-8");
10356
+ const latest = path22.join(specsDir, files[0]);
10357
+ specContent = await fs23.readFile(latest, "utf-8");
10460
10358
  resolvedSpecFile = latest;
10461
- console.log(chalk19.gray(`Auto-detected spec: specs/${files[0]}`));
10359
+ console.log(chalk18.gray(`Auto-detected spec: specs/${files[0]}`));
10462
10360
  }
10463
10361
  }
10464
10362
  }
10465
10363
  if (!specContent) {
10466
- console.log(chalk19.yellow("No spec file found. Running review without spec context."));
10364
+ console.log(chalk18.yellow("No spec file found. Running review without spec context."));
10467
10365
  }
10468
10366
  await reviewer.reviewCode(specContent, resolvedSpecFile);
10469
10367
  await reviewer.printScoreTrend();
@@ -10490,15 +10388,15 @@ program.command("init").description(`Analyze codebase and generate Project Const
10490
10388
  auto: opts.auto
10491
10389
  });
10492
10390
  if (result.written) {
10493
- console.log(chalk19.blue("\n Summary:"));
10494
- console.log(chalk19.gray(` Lines : ${result.before.totalLines} \u2192 ${result.after.totalLines} (${result.before.totalLines - result.after.totalLines > 0 ? "-" : "+"}${Math.abs(result.before.totalLines - result.after.totalLines)})`));
10495
- console.log(chalk19.gray(` \xA79 : ${result.before.lessonCount} \u2192 ${result.after.lessonCount} lessons remaining`));
10391
+ console.log(chalk18.blue("\n Summary:"));
10392
+ console.log(chalk18.gray(` Lines : ${result.before.totalLines} \u2192 ${result.after.totalLines} (${result.before.totalLines - result.after.totalLines > 0 ? "-" : "+"}${Math.abs(result.before.totalLines - result.after.totalLines)})`));
10393
+ console.log(chalk18.gray(` \xA79 : ${result.before.lessonCount} \u2192 ${result.after.lessonCount} lessons remaining`));
10496
10394
  if (result.backupPath) {
10497
- console.log(chalk19.gray(` Backup: ${path23.basename(result.backupPath)}`));
10395
+ console.log(chalk18.gray(` Backup: ${path22.basename(result.backupPath)}`));
10498
10396
  }
10499
10397
  }
10500
10398
  } catch (err) {
10501
- console.error(chalk19.red(` \u2718 Consolidation failed: ${err.message}`));
10399
+ console.error(chalk18.red(` \u2718 Consolidation failed: ${err.message}`));
10502
10400
  process.exit(1);
10503
10401
  }
10504
10402
  return;
@@ -10506,75 +10404,75 @@ program.command("init").description(`Analyze codebase and generate Project Const
10506
10404
  if (opts.global) {
10507
10405
  const existing = await loadGlobalConstitution([currentDir]);
10508
10406
  if (existing && !opts.force) {
10509
- console.log(chalk19.yellow(`
10407
+ console.log(chalk18.yellow(`
10510
10408
  Global constitution already exists at: ${existing.source}`));
10511
- console.log(chalk19.gray(" Use --force to overwrite it."));
10409
+ console.log(chalk18.gray(" Use --force to overwrite it."));
10512
10410
  return;
10513
10411
  }
10514
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Generating Global Constitution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10515
- console.log(chalk19.gray(` Provider: ${providerName}/${modelName}`));
10516
- console.log(chalk19.gray(" Scanning repos in workspace..."));
10412
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Generating Global Constitution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10413
+ console.log(chalk18.gray(` Provider: ${providerName}/${modelName}`));
10414
+ console.log(chalk18.gray(" Scanning repos in workspace..."));
10517
10415
  const loader = new ContextLoader(currentDir);
10518
10416
  const ctx = await loader.loadProjectContext();
10519
10417
  const summary = [
10520
10418
  `Tech stack: ${ctx.techStack.join(", ") || "unknown"}`,
10521
10419
  `Dependencies: ${ctx.dependencies.slice(0, 20).join(", ")}`
10522
10420
  ].join("\n");
10523
- const prompt = buildGlobalConstitutionPrompt([{ name: path23.basename(currentDir), summary }]);
10421
+ const prompt = buildGlobalConstitutionPrompt([{ name: path22.basename(currentDir), summary }]);
10524
10422
  let globalConstitution;
10525
10423
  try {
10526
10424
  globalConstitution = await provider.generate(prompt, globalConstitutionSystemPrompt);
10527
10425
  } catch (err) {
10528
- console.error(chalk19.red(" \u2718 Failed to generate global constitution:"), err);
10426
+ console.error(chalk18.red(" \u2718 Failed to generate global constitution:"), err);
10529
10427
  process.exit(1);
10530
10428
  }
10531
10429
  const saved2 = await saveGlobalConstitution(globalConstitution, currentDir);
10532
- console.log(chalk19.green(`
10430
+ console.log(chalk18.green(`
10533
10431
  \u2714 Global constitution saved: ${saved2}`));
10534
- console.log(chalk19.gray(" This will be automatically merged into all project constitutions in this workspace."));
10535
- console.log(chalk19.gray(" Project-level rules always override global rules.\n"));
10536
- console.log(chalk19.bold(" Preview:"));
10537
- console.log(chalk19.gray(globalConstitution.split("\n").slice(0, 12).join("\n")));
10432
+ console.log(chalk18.gray(" This will be automatically merged into all project constitutions in this workspace."));
10433
+ console.log(chalk18.gray(" Project-level rules always override global rules.\n"));
10434
+ console.log(chalk18.bold(" Preview:"));
10435
+ console.log(chalk18.gray(globalConstitution.split("\n").slice(0, 12).join("\n")));
10538
10436
  if (globalConstitution.split("\n").length > 12) {
10539
- console.log(chalk19.gray(` ... (${globalConstitution.split("\n").length} lines total)`));
10437
+ console.log(chalk18.gray(` ... (${globalConstitution.split("\n").length} lines total)`));
10540
10438
  }
10541
10439
  return;
10542
10440
  }
10543
- const constitutionPath = path23.join(currentDir, CONSTITUTION_FILE);
10544
- if (!opts.force && await fs24.pathExists(constitutionPath)) {
10545
- console.log(chalk19.yellow(`
10441
+ const constitutionPath = path22.join(currentDir, CONSTITUTION_FILE);
10442
+ if (!opts.force && await fs23.pathExists(constitutionPath)) {
10443
+ console.log(chalk18.yellow(`
10546
10444
  ${CONSTITUTION_FILE} already exists.`));
10547
- console.log(chalk19.gray(" Use --force to overwrite it."));
10548
- console.log(chalk19.gray(` Or edit it directly: ${constitutionPath}`));
10445
+ console.log(chalk18.gray(" Use --force to overwrite it."));
10446
+ console.log(chalk18.gray(` Or edit it directly: ${constitutionPath}`));
10549
10447
  return;
10550
10448
  }
10551
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Generating Project Constitution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10552
- console.log(chalk19.gray(` Provider: ${providerName}/${modelName}`));
10553
- console.log(chalk19.gray(" Analyzing codebase..."));
10449
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Generating Project Constitution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10450
+ console.log(chalk18.gray(` Provider: ${providerName}/${modelName}`));
10451
+ console.log(chalk18.gray(" Analyzing codebase..."));
10554
10452
  const generator = new ConstitutionGenerator(provider);
10555
10453
  let constitution;
10556
10454
  try {
10557
10455
  constitution = await generator.generate(currentDir);
10558
10456
  } catch (err) {
10559
- console.error(chalk19.red(" \u2718 Failed to generate constitution:"), err);
10457
+ console.error(chalk18.red(" \u2718 Failed to generate constitution:"), err);
10560
10458
  process.exit(1);
10561
10459
  }
10562
10460
  const saved = await generator.saveConstitution(currentDir, constitution);
10563
- const globalResult = await loadGlobalConstitution([path23.dirname(currentDir)]);
10461
+ const globalResult = await loadGlobalConstitution([path22.dirname(currentDir)]);
10564
10462
  if (globalResult) {
10565
- console.log(chalk19.cyan(`
10463
+ console.log(chalk18.cyan(`
10566
10464
  \u2139 Global constitution detected: ${globalResult.source}`));
10567
- console.log(chalk19.gray(" It will be merged with this project constitution at runtime."));
10568
- console.log(chalk19.gray(" Project rules take priority over global rules."));
10465
+ console.log(chalk18.gray(" It will be merged with this project constitution at runtime."));
10466
+ console.log(chalk18.gray(" Project rules take priority over global rules."));
10569
10467
  }
10570
- console.log(chalk19.green(`
10468
+ console.log(chalk18.green(`
10571
10469
  \u2714 Constitution saved: ${saved}`));
10572
- console.log(chalk19.gray(" This file will be automatically used in all future `ai-spec create` runs."));
10573
- console.log(chalk19.gray(" Edit it to add custom rules or red lines for your project.\n"));
10574
- console.log(chalk19.bold(" Preview:"));
10575
- console.log(chalk19.gray(constitution.split("\n").slice(0, 15).join("\n")));
10470
+ console.log(chalk18.gray(" This file will be automatically used in all future `ai-spec create` runs."));
10471
+ console.log(chalk18.gray(" Edit it to add custom rules or red lines for your project.\n"));
10472
+ console.log(chalk18.bold(" Preview:"));
10473
+ console.log(chalk18.gray(constitution.split("\n").slice(0, 15).join("\n")));
10576
10474
  if (constitution.split("\n").length > 15) {
10577
- console.log(chalk19.gray(` ... (${constitution.split("\n").length} lines total)`));
10475
+ console.log(chalk18.gray(` ... (${constitution.split("\n").length} lines total)`));
10578
10476
  }
10579
10477
  });
10580
10478
  program.command("config").description(`Set default configuration for this project (saved to ${CONFIG_FILE})`).option("--provider <name>", "Default AI provider for spec generation").option("--model <name>", "Default model for spec generation").option(
@@ -10582,44 +10480,44 @@ program.command("config").description(`Set default configuration for this projec
10582
10480
  "Default code generation mode (claude-code|api|plan)"
10583
10481
  ).option("--codegen-provider <name>", "Default provider for code generation").option("--codegen-model <name>", "Default model for code generation").option("--min-spec-score <score>", "Minimum overall spec score (1-10) to pass Approval Gate (0 = disabled)").option("--show", "Print current configuration").option("--reset", "Reset configuration to empty").option("--clear-keys", "Delete all saved API keys from ~/.ai-spec-keys.json").option("--clear-key <provider>", "Delete saved API key for a specific provider").option("--list-keys", "Show which providers have a saved key").action(async (opts) => {
10584
10482
  const currentDir = process.cwd();
10585
- const configPath = path23.join(currentDir, CONFIG_FILE);
10483
+ const configPath = path22.join(currentDir, CONFIG_FILE);
10586
10484
  if (opts.clearKeys) {
10587
10485
  await clearAllKeys();
10588
- console.log(chalk19.green(`\u2714 All saved API keys cleared.`));
10486
+ console.log(chalk18.green(`\u2714 All saved API keys cleared.`));
10589
10487
  return;
10590
10488
  }
10591
10489
  if (opts.clearKey) {
10592
10490
  await clearKey(opts.clearKey);
10593
- console.log(chalk19.green(`\u2714 Saved key for "${opts.clearKey}" removed.`));
10491
+ console.log(chalk18.green(`\u2714 Saved key for "${opts.clearKey}" removed.`));
10594
10492
  return;
10595
10493
  }
10596
10494
  if (opts.listKeys) {
10597
- const store = await fs24.readJson(KEY_STORE_FILE).catch(() => ({}));
10495
+ const store = await fs23.readJson(KEY_STORE_FILE).catch(() => ({}));
10598
10496
  const providers = Object.keys(store);
10599
10497
  if (providers.length === 0) {
10600
- console.log(chalk19.gray("No saved API keys."));
10498
+ console.log(chalk18.gray("No saved API keys."));
10601
10499
  } else {
10602
- console.log(chalk19.bold("Saved API keys:"));
10500
+ console.log(chalk18.bold("Saved API keys:"));
10603
10501
  for (const p of providers) {
10604
10502
  const k2 = store[p];
10605
- console.log(chalk19.gray(` ${p}: ${k2.slice(0, 6)}...${k2.slice(-4)}`));
10503
+ console.log(chalk18.gray(` ${p}: ${k2.slice(0, 6)}...${k2.slice(-4)}`));
10606
10504
  }
10607
- console.log(chalk19.gray(`
10505
+ console.log(chalk18.gray(`
10608
10506
  File: ${KEY_STORE_FILE}`));
10609
10507
  }
10610
10508
  return;
10611
10509
  }
10612
10510
  if (opts.reset) {
10613
- await fs24.writeJson(configPath, {}, { spaces: 2 });
10614
- console.log(chalk19.green(`\u2714 Config reset: ${configPath}`));
10511
+ await fs23.writeJson(configPath, {}, { spaces: 2 });
10512
+ console.log(chalk18.green(`\u2714 Config reset: ${configPath}`));
10615
10513
  return;
10616
10514
  }
10617
10515
  const existing = await loadConfig(currentDir);
10618
10516
  if (opts.show) {
10619
10517
  if (Object.keys(existing).length === 0) {
10620
- console.log(chalk19.gray("No config file found. Using built-in defaults."));
10518
+ console.log(chalk18.gray("No config file found. Using built-in defaults."));
10621
10519
  } else {
10622
- console.log(chalk19.bold(`${configPath}:`));
10520
+ console.log(chalk18.bold(`${configPath}:`));
10623
10521
  console.log(JSON.stringify(existing, null, 2));
10624
10522
  }
10625
10523
  return;
@@ -10633,27 +10531,27 @@ File: ${KEY_STORE_FILE}`));
10633
10531
  if (opts.minSpecScore !== void 0) {
10634
10532
  const score = parseInt(opts.minSpecScore, 10);
10635
10533
  if (isNaN(score) || score < 0 || score > 10) {
10636
- console.error(chalk19.red(" --min-spec-score must be a number between 0 and 10"));
10534
+ console.error(chalk18.red(" --min-spec-score must be a number between 0 and 10"));
10637
10535
  process.exit(1);
10638
10536
  }
10639
10537
  updated.minSpecScore = score;
10640
10538
  }
10641
- await fs24.writeJson(configPath, updated, { spaces: 2 });
10642
- console.log(chalk19.green(`\u2714 Config saved to ${configPath}`));
10539
+ await fs23.writeJson(configPath, updated, { spaces: 2 });
10540
+ console.log(chalk18.green(`\u2714 Config saved to ${configPath}`));
10643
10541
  console.log(JSON.stringify(updated, null, 2));
10644
10542
  });
10645
10543
  program.command("model").description("Interactively switch the active AI provider/model and save to .ai-spec.json").option("--list", "List all available providers and models").action(async (opts) => {
10646
10544
  const currentDir = process.cwd();
10647
- const configPath = path23.join(currentDir, CONFIG_FILE);
10545
+ const configPath = path22.join(currentDir, CONFIG_FILE);
10648
10546
  if (opts.list) {
10649
- console.log(chalk19.bold("\nAvailable providers & models:\n"));
10547
+ console.log(chalk18.bold("\nAvailable providers & models:\n"));
10650
10548
  for (const [key, meta] of Object.entries(PROVIDER_CATALOG)) {
10651
10549
  console.log(
10652
- ` ${chalk19.bold.cyan(key.padEnd(10))} ${chalk19.white(meta.displayName)}`
10550
+ ` ${chalk18.bold.cyan(key.padEnd(10))} ${chalk18.white(meta.displayName)}`
10653
10551
  );
10654
- console.log(chalk19.gray(` ${meta.description}`));
10552
+ console.log(chalk18.gray(` ${meta.description}`));
10655
10553
  console.log(
10656
- chalk19.gray(
10554
+ chalk18.gray(
10657
10555
  ` env: ${meta.envKey} | models: ${meta.models.join(", ")}`
10658
10556
  )
10659
10557
  );
@@ -10662,10 +10560,10 @@ program.command("model").description("Interactively switch the active AI provide
10662
10560
  return;
10663
10561
  }
10664
10562
  const existing = await loadConfig(currentDir);
10665
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Model Switcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10563
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Model Switcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10666
10564
  if (Object.keys(existing).length > 0) {
10667
10565
  console.log(
10668
- chalk19.gray(
10566
+ chalk18.gray(
10669
10567
  ` Current: spec=${existing.provider ?? "gemini"}/${existing.model ?? DEFAULT_MODELS[existing.provider ?? "gemini"]}` + (existing.codegenProvider ? ` codegen=${existing.codegenProvider}/${existing.codegenModel ?? ""}` : "")
10670
10568
  )
10671
10569
  );
@@ -10683,7 +10581,7 @@ program.command("model").description("Interactively switch the active AI provide
10683
10581
  const providerKey = await select3({
10684
10582
  message: `${label} \u2014 select provider:`,
10685
10583
  choices: Object.entries(PROVIDER_CATALOG).map(([key, meta2]) => ({
10686
- name: `${meta2.displayName.padEnd(22)} ${chalk19.gray(meta2.description)}`,
10584
+ name: `${meta2.displayName.padEnd(22)} ${chalk18.gray(meta2.description)}`,
10687
10585
  value: key,
10688
10586
  short: meta2.displayName
10689
10587
  }))
@@ -10691,7 +10589,7 @@ program.command("model").description("Interactively switch the active AI provide
10691
10589
  const meta = PROVIDER_CATALOG[providerKey];
10692
10590
  const modelChoices = [
10693
10591
  ...meta.models.map((m) => ({ name: m, value: m })),
10694
- { name: chalk19.italic("\u270E Enter custom model name..."), value: "__custom__" }
10592
+ { name: chalk18.italic("\u270E Enter custom model name..."), value: "__custom__" }
10695
10593
  ];
10696
10594
  let chosenModel = await select3({
10697
10595
  message: `${label} \u2014 select model (${meta.displayName}):`,
@@ -10725,37 +10623,37 @@ program.command("model").description("Interactively switch the active AI provide
10725
10623
  if (!updated.codegen || updated.codegen === "claude-code") {
10726
10624
  updated.codegen = "api";
10727
10625
  console.log(
10728
- chalk19.yellow(
10626
+ chalk18.yellow(
10729
10627
  `
10730
10628
  \u26A0 provider "${effectiveCodegenProvider}" \u4E0D\u652F\u6301 "claude-code" \u6A21\u5F0F\u3002`
10731
10629
  )
10732
10630
  );
10733
- console.log(chalk19.gray(` \u5DF2\u81EA\u52A8\u5C06 codegen \u6A21\u5F0F\u8BBE\u4E3A "api"\u3002`));
10631
+ console.log(chalk18.gray(` \u5DF2\u81EA\u52A8\u5C06 codegen \u6A21\u5F0F\u8BBE\u4E3A "api"\u3002`));
10734
10632
  }
10735
10633
  }
10736
10634
  }
10737
- console.log(chalk19.blue("\n Preview:"));
10738
- console.log(chalk19.gray(` spec \u2192 ${updated.provider}/${updated.model}`));
10635
+ console.log(chalk18.blue("\n Preview:"));
10636
+ console.log(chalk18.gray(` spec \u2192 ${updated.provider}/${updated.model}`));
10739
10637
  if (updated.codegenProvider) {
10740
10638
  console.log(
10741
- chalk19.gray(
10639
+ chalk18.gray(
10742
10640
  ` codegen \u2192 ${updated.codegenProvider}/${updated.codegenModel} (mode: ${updated.codegen ?? "claude-code"})`
10743
10641
  )
10744
10642
  );
10745
10643
  }
10746
10644
  const ok = await confirm2({ message: "Save to .ai-spec.json?", default: true });
10747
10645
  if (!ok) {
10748
- console.log(chalk19.gray(" Cancelled."));
10646
+ console.log(chalk18.gray(" Cancelled."));
10749
10647
  return;
10750
10648
  }
10751
- await fs24.writeJson(configPath, updated, { spaces: 2 });
10752
- console.log(chalk19.green(`
10649
+ await fs23.writeJson(configPath, updated, { spaces: 2 });
10650
+ console.log(chalk18.green(`
10753
10651
  \u2714 Saved to ${configPath}`));
10754
10652
  const providerToCheck = updated.provider ?? "gemini";
10755
10653
  const envKey = ENV_KEY_MAP[providerToCheck];
10756
10654
  if (envKey && !process.env[envKey]) {
10757
10655
  console.log(
10758
- chalk19.yellow(
10656
+ chalk18.yellow(
10759
10657
  ` \u26A0 Remember to set ${envKey} in your environment or .env file.`
10760
10658
  )
10761
10659
  );
@@ -10774,29 +10672,29 @@ async function runSingleRepoPipelineInWorkspace(opts) {
10774
10672
  cliOpts,
10775
10673
  contractContextSection
10776
10674
  } = opts;
10777
- console.log(chalk19.blue(`
10675
+ console.log(chalk18.blue(`
10778
10676
  [${repoName}] Loading project context...`));
10779
10677
  const loader = new ContextLoader(repoAbsPath);
10780
10678
  let context = await loader.loadProjectContext();
10781
10679
  const { type: detectedRepoType } = await detectRepoType(repoAbsPath);
10782
- console.log(chalk19.gray(` Tech stack: ${context.techStack.join(", ") || "unknown"} [${detectedRepoType}]`));
10783
- console.log(chalk19.gray(` Dependencies: ${context.dependencies.length} packages`));
10680
+ console.log(chalk18.gray(` Tech stack: ${context.techStack.join(", ") || "unknown"} [${detectedRepoType}]`));
10681
+ console.log(chalk18.gray(` Dependencies: ${context.dependencies.length} packages`));
10784
10682
  if (context.constitution && context.constitution.length > 6e3) {
10785
- console.log(chalk19.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
10683
+ console.log(chalk18.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
10786
10684
  }
10787
10685
  if (!context.constitution) {
10788
- console.log(chalk19.yellow(` Constitution: not found \u2014 auto-generating...`));
10686
+ console.log(chalk18.yellow(` Constitution: not found \u2014 auto-generating...`));
10789
10687
  try {
10790
10688
  const constitutionGen = new ConstitutionGenerator(specProvider);
10791
10689
  const constitutionContent = await constitutionGen.generate(repoAbsPath);
10792
10690
  await constitutionGen.saveConstitution(repoAbsPath, constitutionContent);
10793
10691
  context.constitution = constitutionContent;
10794
- console.log(chalk19.green(` Constitution: generated`));
10692
+ console.log(chalk18.green(` Constitution: generated`));
10795
10693
  } catch (err) {
10796
- console.log(chalk19.yellow(` Constitution: auto-generation failed (${err.message}), continuing.`));
10694
+ console.log(chalk18.yellow(` Constitution: auto-generation failed (${err.message}), continuing.`));
10797
10695
  }
10798
10696
  } else {
10799
- console.log(chalk19.green(` Constitution: found`));
10697
+ console.log(chalk18.green(` Constitution: found`));
10800
10698
  }
10801
10699
  let fullIdea = idea;
10802
10700
  if (contractContextSection) {
@@ -10804,58 +10702,58 @@ async function runSingleRepoPipelineInWorkspace(opts) {
10804
10702
 
10805
10703
  ${contractContextSection}`;
10806
10704
  }
10807
- console.log(chalk19.blue(` [${repoName}] Generating spec...`));
10705
+ console.log(chalk18.blue(` [${repoName}] Generating spec...`));
10808
10706
  let finalSpec;
10809
10707
  try {
10810
10708
  const result = await generateSpecWithTasks(specProvider, fullIdea, context);
10811
10709
  finalSpec = result.spec;
10812
- console.log(chalk19.green(` Spec generated.`));
10710
+ console.log(chalk18.green(` Spec generated.`));
10813
10711
  } catch (err) {
10814
- console.error(chalk19.red(` Spec generation failed: ${err.message}`));
10712
+ console.error(chalk18.red(` Spec generation failed: ${err.message}`));
10815
10713
  return { dsl: null, specFile: null };
10816
10714
  }
10817
10715
  let extractedDsl = null;
10818
10716
  if (!cliOpts.skipDsl) {
10819
- console.log(chalk19.blue(` [${repoName}] Extracting DSL...`));
10717
+ console.log(chalk18.blue(` [${repoName}] Extracting DSL...`));
10820
10718
  try {
10821
10719
  const dslExtractor = new DslExtractor(specProvider);
10822
10720
  const repoIsFrontend = isFrontendDeps(context.dependencies);
10823
10721
  extractedDsl = await dslExtractor.extract(finalSpec, { auto: true, isFrontend: repoIsFrontend });
10824
10722
  if (extractedDsl) {
10825
- console.log(chalk19.green(` DSL extracted.`));
10723
+ console.log(chalk18.green(` DSL extracted.`));
10826
10724
  }
10827
10725
  } catch (err) {
10828
- console.log(chalk19.yellow(` DSL extraction failed: ${err.message}`));
10726
+ console.log(chalk18.yellow(` DSL extraction failed: ${err.message}`));
10829
10727
  }
10830
10728
  }
10831
10729
  const isFrontendRepo = isFrontendDeps(context.dependencies ?? []);
10832
10730
  const skipWorktreeForRepo = cliOpts.worktree ? false : cliOpts.skipWorktree || isFrontendRepo;
10833
10731
  let workingDir = repoAbsPath;
10834
10732
  if (!skipWorktreeForRepo) {
10835
- console.log(chalk19.blue(` [${repoName}] Setting up git worktree...`));
10733
+ console.log(chalk18.blue(` [${repoName}] Setting up git worktree...`));
10836
10734
  try {
10837
10735
  const worktreeManager = new GitWorktreeManager(repoAbsPath);
10838
10736
  const worktreePath = await worktreeManager.createWorktree(idea);
10839
10737
  if (worktreePath) workingDir = worktreePath;
10840
10738
  } catch (err) {
10841
- console.log(chalk19.yellow(` Worktree setup failed: ${err.message}. Using main branch.`));
10739
+ console.log(chalk18.yellow(` Worktree setup failed: ${err.message}. Using main branch.`));
10842
10740
  }
10843
10741
  } else {
10844
- console.log(chalk19.gray(` [${repoName}] Skipping worktree${isFrontendRepo ? " (frontend repo)" : ""}.`));
10742
+ console.log(chalk18.gray(` [${repoName}] Skipping worktree${isFrontendRepo ? " (frontend repo)" : ""}.`));
10845
10743
  }
10846
- const specsDir = path23.join(workingDir, "specs");
10847
- await fs24.ensureDir(specsDir);
10744
+ const specsDir = path22.join(workingDir, "specs");
10745
+ await fs23.ensureDir(specsDir);
10848
10746
  const featureSlug = slugify(idea);
10849
10747
  const { filePath: specFile } = await nextVersionPath(specsDir, featureSlug);
10850
- await fs24.writeFile(specFile, finalSpec, "utf-8");
10851
- console.log(chalk19.green(` Spec saved: ${path23.relative(repoAbsPath, specFile)}`));
10748
+ await fs23.writeFile(specFile, finalSpec, "utf-8");
10749
+ console.log(chalk18.green(` Spec saved: ${path22.relative(repoAbsPath, specFile)}`));
10852
10750
  let savedDslFile = null;
10853
10751
  if (extractedDsl) {
10854
10752
  const dslExtractorForSave = new DslExtractor(specProvider);
10855
10753
  savedDslFile = await dslExtractorForSave.saveDsl(extractedDsl, specFile);
10856
- console.log(chalk19.green(` DSL saved: ${path23.relative(repoAbsPath, savedDslFile)}`));
10754
+ console.log(chalk18.green(` DSL saved: ${path22.relative(repoAbsPath, savedDslFile)}`));
10857
10755
  }
10858
- console.log(chalk19.blue(` [${repoName}] Running code generation (mode: ${codegenMode})...`));
10756
+ console.log(chalk18.blue(` [${repoName}] Running code generation (mode: ${codegenMode})...`));
10859
10757
  try {
10860
10758
  const codegen = new CodeGenerator(codegenProvider, codegenMode);
10861
10759
  await codegen.generateCode(specFile, workingDir, context, {
@@ -10863,29 +10761,29 @@ ${contractContextSection}`;
10863
10761
  dslFilePath: savedDslFile ?? void 0,
10864
10762
  repoType: detectedRepoType
10865
10763
  });
10866
- console.log(chalk19.green(` Code generation complete.`));
10764
+ console.log(chalk18.green(` Code generation complete.`));
10867
10765
  } catch (err) {
10868
- console.log(chalk19.yellow(` Code generation failed: ${err.message}`));
10766
+ console.log(chalk18.yellow(` Code generation failed: ${err.message}`));
10869
10767
  }
10870
10768
  if (!cliOpts.skipTests && extractedDsl) {
10871
- console.log(chalk19.blue(` [${repoName}] Generating test skeletons...`));
10769
+ console.log(chalk18.blue(` [${repoName}] Generating test skeletons...`));
10872
10770
  try {
10873
10771
  const testGen = new TestGenerator(codegenProvider);
10874
10772
  const testFiles = await testGen.generate(extractedDsl, workingDir);
10875
- console.log(chalk19.green(` ${testFiles.length} test file(s) generated.`));
10773
+ console.log(chalk18.green(` ${testFiles.length} test file(s) generated.`));
10876
10774
  } catch (err) {
10877
- console.log(chalk19.yellow(` Test generation failed: ${err.message}`));
10775
+ console.log(chalk18.yellow(` Test generation failed: ${err.message}`));
10878
10776
  }
10879
10777
  }
10880
10778
  if (!cliOpts.skipErrorFeedback) {
10881
10779
  try {
10882
10780
  await runErrorFeedback(codegenProvider, workingDir, extractedDsl, { maxCycles: 1 });
10883
10781
  } catch (err) {
10884
- console.log(chalk19.yellow(` Error feedback failed: ${err.message}`));
10782
+ console.log(chalk18.yellow(` Error feedback failed: ${err.message}`));
10885
10783
  }
10886
10784
  }
10887
10785
  if (!cliOpts.skipReview) {
10888
- console.log(chalk19.blue(` [${repoName}] Running code review...`));
10786
+ console.log(chalk18.blue(` [${repoName}] Running code review...`));
10889
10787
  try {
10890
10788
  const reviewer = new CodeReviewer(specProvider);
10891
10789
  const originalDir = process.cwd();
@@ -10897,9 +10795,9 @@ ${contractContextSection}`;
10897
10795
  process.chdir(originalDir);
10898
10796
  }
10899
10797
  await accumulateReviewKnowledge(specProvider, repoAbsPath, reviewResult);
10900
- console.log(chalk19.green(` Code review complete.`));
10798
+ console.log(chalk18.green(` Code review complete.`));
10901
10799
  } catch (err) {
10902
- console.log(chalk19.yellow(` Code review failed: ${err.message}`));
10800
+ console.log(chalk18.yellow(` Code review failed: ${err.message}`));
10903
10801
  }
10904
10802
  }
10905
10803
  return { dsl: extractedDsl, specFile };
@@ -10922,7 +10820,7 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
10922
10820
  codegenModel: codegenModelName
10923
10821
  });
10924
10822
  const workspaceLoader = new WorkspaceLoader(currentDir);
10925
- console.log(chalk19.blue("\n[W1] Loading per-repo contexts..."));
10823
+ console.log(chalk18.blue("\n[W1] Loading per-repo contexts..."));
10926
10824
  const contexts = /* @__PURE__ */ new Map();
10927
10825
  const frontendContexts = /* @__PURE__ */ new Map();
10928
10826
  for (const repo of workspace.repos) {
@@ -10934,27 +10832,27 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
10934
10832
  if (repo.role === "frontend" || repo.role === "mobile") {
10935
10833
  const fctx = await loadFrontendContext(repoAbsPath);
10936
10834
  frontendContexts.set(repo.name, fctx);
10937
- console.log(chalk19.gray(` ${repo.name}: ${fctx.framework} / ${fctx.httpClient} / hooks:${fctx.hookFiles.length} stores:${fctx.storeFiles.length}`));
10835
+ console.log(chalk18.gray(` ${repo.name}: ${fctx.framework} / ${fctx.httpClient} / hooks:${fctx.hookFiles.length} stores:${fctx.storeFiles.length}`));
10938
10836
  } else {
10939
- console.log(chalk19.gray(` ${repo.name}: ${ctx.techStack.join(", ") || "unknown"} (${ctx.dependencies.length} deps)`));
10837
+ console.log(chalk18.gray(` ${repo.name}: ${ctx.techStack.join(", ") || "unknown"} (${ctx.dependencies.length} deps)`));
10940
10838
  }
10941
10839
  } catch (err) {
10942
- console.log(chalk19.yellow(` ${repo.name}: context load failed \u2014 ${err.message}`));
10840
+ console.log(chalk18.yellow(` ${repo.name}: context load failed \u2014 ${err.message}`));
10943
10841
  }
10944
10842
  }
10945
- console.log(chalk19.blue("\n[W2] Decomposing requirement across repos..."));
10843
+ console.log(chalk18.blue("\n[W2] Decomposing requirement across repos..."));
10946
10844
  const decomposer = new RequirementDecomposer(specProvider);
10947
10845
  let decomposition;
10948
10846
  try {
10949
10847
  decomposition = await decomposer.decompose(idea, workspace, contexts, frontendContexts);
10950
- console.log(chalk19.green(` Summary: ${decomposition.summary}`));
10951
- console.log(chalk19.gray(` Repos affected: ${decomposition.repos.map((r) => r.repoName).join(", ")}`));
10848
+ console.log(chalk18.green(` Summary: ${decomposition.summary}`));
10849
+ console.log(chalk18.gray(` Repos affected: ${decomposition.repos.map((r) => r.repoName).join(", ")}`));
10952
10850
  if (decomposition.coordinationNotes) {
10953
- console.log(chalk19.gray(` Coordination: ${decomposition.coordinationNotes}`));
10851
+ console.log(chalk18.gray(` Coordination: ${decomposition.coordinationNotes}`));
10954
10852
  }
10955
10853
  } catch (err) {
10956
- console.error(chalk19.red(` Decomposition failed: ${err.message}`));
10957
- console.log(chalk19.yellow(" Falling back to running all repos independently."));
10854
+ console.error(chalk18.red(` Decomposition failed: ${err.message}`));
10855
+ console.log(chalk18.yellow(" Falling back to running all repos independently."));
10958
10856
  decomposition = {
10959
10857
  originalRequirement: idea,
10960
10858
  summary: idea,
@@ -10970,11 +10868,11 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
10970
10868
  };
10971
10869
  }
10972
10870
  if (!opts.auto) {
10973
- console.log(chalk19.cyan("\n[W3] Decomposition Preview:"));
10974
- console.log(chalk19.cyan("\u2500".repeat(52)));
10871
+ console.log(chalk18.cyan("\n[W3] Decomposition Preview:"));
10872
+ console.log(chalk18.cyan("\u2500".repeat(52)));
10975
10873
  for (const r of decomposition.repos) {
10976
- console.log(chalk19.bold(` ${r.repoName} (${r.role})`));
10977
- console.log(chalk19.gray(` ${r.specIdea.slice(0, 150)}${r.specIdea.length > 150 ? "..." : ""}`));
10874
+ console.log(chalk18.bold(` ${r.repoName} (${r.role})`));
10875
+ console.log(chalk18.gray(` ${r.specIdea.slice(0, 150)}${r.specIdea.length > 150 ? "..." : ""}`));
10978
10876
  if (r.uxDecisions) {
10979
10877
  const ux = r.uxDecisions;
10980
10878
  const uxSummary = [
@@ -10983,13 +10881,13 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
10983
10881
  ux.optimisticUpdate ? "optimistic-update" : "",
10984
10882
  ux.errorRollback ? "rollback" : ""
10985
10883
  ].filter(Boolean).join(", ");
10986
- if (uxSummary) console.log(chalk19.cyan(` UX: ${uxSummary}`));
10884
+ if (uxSummary) console.log(chalk18.cyan(` UX: ${uxSummary}`));
10987
10885
  }
10988
10886
  if (r.dependsOnRepos.length > 0) {
10989
- console.log(chalk19.gray(` Depends on: ${r.dependsOnRepos.join(", ")}`));
10887
+ console.log(chalk18.gray(` Depends on: ${r.dependsOnRepos.join(", ")}`));
10990
10888
  }
10991
10889
  }
10992
- console.log(chalk19.cyan("\u2500".repeat(52)));
10890
+ console.log(chalk18.cyan("\u2500".repeat(52)));
10993
10891
  const gate = await select3({
10994
10892
  message: "Proceed with multi-repo pipeline?",
10995
10893
  choices: [
@@ -10998,24 +10896,24 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
10998
10896
  ]
10999
10897
  });
11000
10898
  if (gate === "abort") {
11001
- console.log(chalk19.yellow(" Aborted."));
10899
+ console.log(chalk18.yellow(" Aborted."));
11002
10900
  process.exit(0);
11003
10901
  }
11004
10902
  }
11005
10903
  const sortedRepoRequirements = RequirementDecomposer.sortByDependency(decomposition.repos);
11006
10904
  const contractDsls = /* @__PURE__ */ new Map();
11007
- console.log(chalk19.blue(`
10905
+ console.log(chalk18.blue(`
11008
10906
  [W4] Running pipeline for ${sortedRepoRequirements.length} repo(s)...`));
11009
10907
  const results = [];
11010
10908
  for (const repoReq of sortedRepoRequirements) {
11011
10909
  const repoConfig = workspace.repos.find((r) => r.name === repoReq.repoName);
11012
10910
  if (!repoConfig) {
11013
- console.log(chalk19.yellow(` Skipping ${repoReq.repoName} \u2014 not found in workspace config.`));
10911
+ console.log(chalk18.yellow(` Skipping ${repoReq.repoName} \u2014 not found in workspace config.`));
11014
10912
  results.push({ repoName: repoReq.repoName, status: "skipped", specFile: null, dsl: null, repoAbsPath: "", role: repoReq.role });
11015
10913
  continue;
11016
10914
  }
11017
10915
  const repoAbsPath = workspaceLoader.resolveAbsPath(repoConfig);
11018
- console.log(chalk19.bold.blue(`
10916
+ console.log(chalk18.bold.blue(`
11019
10917
  \u2500\u2500 ${repoReq.repoName} (${repoReq.role}) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
11020
10918
  let contractContextSection;
11021
10919
  if (repoReq.dependsOnRepos.length > 0) {
@@ -11023,7 +10921,7 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
11023
10921
  for (const depName of repoReq.dependsOnRepos) {
11024
10922
  const depDsl = contractDsls.get(depName);
11025
10923
  if (depDsl) {
11026
- console.log(chalk19.gray(` Using API contract from: ${depName}`));
10924
+ console.log(chalk18.gray(` Using API contract from: ${depName}`));
11027
10925
  const contract = buildFrontendApiContract(depDsl);
11028
10926
  contractParts.push(buildContractContextSection(contract));
11029
10927
  }
@@ -11043,7 +10941,7 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
11043
10941
  frontendContext: frontendCtx
11044
10942
  });
11045
10943
  contractContextSection = void 0;
11046
- console.log(chalk19.gray(` Frontend context: ${frontendCtx.framework} / ${frontendCtx.httpClient} / ${frontendCtx.uiLibrary}`));
10944
+ console.log(chalk18.gray(` Frontend context: ${frontendCtx.framework} / ${frontendCtx.httpClient} / ${frontendCtx.uiLibrary}`));
11047
10945
  }
11048
10946
  try {
11049
10947
  const { dsl, specFile } = await runSingleRepoPipelineInWorkspace({
@@ -11060,22 +10958,22 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
11060
10958
  });
11061
10959
  if (repoReq.isContractProvider && dsl) {
11062
10960
  contractDsls.set(repoReq.repoName, dsl);
11063
- console.log(chalk19.green(` Contract stored for downstream repos.`));
10961
+ console.log(chalk18.green(` Contract stored for downstream repos.`));
11064
10962
  }
11065
10963
  results.push({ repoName: repoReq.repoName, status: "success", specFile, dsl, repoAbsPath, role: repoReq.role });
11066
- console.log(chalk19.green(` \u2714 ${repoReq.repoName} complete`));
10964
+ console.log(chalk18.green(` \u2714 ${repoReq.repoName} complete`));
11067
10965
  } catch (err) {
11068
- console.error(chalk19.red(` \u2718 ${repoReq.repoName} failed: ${err.message}`));
10966
+ console.error(chalk18.red(` \u2718 ${repoReq.repoName} failed: ${err.message}`));
11069
10967
  results.push({ repoName: repoReq.repoName, status: "failed", specFile: null, dsl: null, repoAbsPath, role: repoReq.role });
11070
10968
  }
11071
10969
  }
11072
- console.log(chalk19.bold.green("\n\u2714 Multi-repo pipeline complete!"));
11073
- console.log(chalk19.gray(` Workspace: ${workspace.name}`));
11074
- console.log(chalk19.gray(` Requirement: ${idea}`));
10970
+ console.log(chalk18.bold.green("\n\u2714 Multi-repo pipeline complete!"));
10971
+ console.log(chalk18.gray(` Workspace: ${workspace.name}`));
10972
+ console.log(chalk18.gray(` Requirement: ${idea}`));
11075
10973
  console.log();
11076
10974
  for (const r of results) {
11077
- const icon = r.status === "success" ? chalk19.green("\u2714") : r.status === "failed" ? chalk19.red("\u2718") : chalk19.gray("\u2212");
11078
- const specInfo = r.specFile ? chalk19.gray(` \u2192 ${r.specFile}`) : "";
10975
+ const icon = r.status === "success" ? chalk18.green("\u2714") : r.status === "failed" ? chalk18.red("\u2718") : chalk18.gray("\u2212");
10976
+ const specInfo = r.specFile ? chalk18.gray(` \u2192 ${r.specFile}`) : "";
11079
10977
  console.log(` ${icon} ${r.repoName} (${r.status})${specInfo}`);
11080
10978
  }
11081
10979
  return results;
@@ -11083,18 +10981,18 @@ async function runMultiRepoPipeline(idea, workspace, opts, currentDir, config2)
11083
10981
  var workspaceCmd = program.command("workspace").description("Manage multi-repo workspace configuration");
11084
10982
  workspaceCmd.command("init").description(`Interactive workspace setup \u2014 creates ${WORKSPACE_CONFIG_FILE}`).action(async () => {
11085
10983
  const currentDir = process.cwd();
11086
- const configPath = path23.join(currentDir, WORKSPACE_CONFIG_FILE);
11087
- if (await fs24.pathExists(configPath)) {
10984
+ const configPath = path22.join(currentDir, WORKSPACE_CONFIG_FILE);
10985
+ if (await fs23.pathExists(configPath)) {
11088
10986
  const overwrite = await confirm2({
11089
10987
  message: `${WORKSPACE_CONFIG_FILE} already exists. Overwrite?`,
11090
10988
  default: false
11091
10989
  });
11092
10990
  if (!overwrite) {
11093
- console.log(chalk19.gray(" Cancelled."));
10991
+ console.log(chalk18.gray(" Cancelled."));
11094
10992
  return;
11095
10993
  }
11096
10994
  }
11097
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Workspace Setup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10995
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Workspace Setup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11098
10996
  const workspaceName = await input({
11099
10997
  message: "Workspace name:",
11100
10998
  validate: (v2) => v2.trim().length > 0 || "Name cannot be empty"
@@ -11108,11 +11006,11 @@ workspaceCmd.command("init").description(`Interactive workspace setup \u2014 cre
11108
11006
  const workspaceLoader = new WorkspaceLoader(currentDir);
11109
11007
  const detected = await workspaceLoader.autoDetect();
11110
11008
  if (detected.length === 0) {
11111
- console.log(chalk19.yellow(" No recognizable repos found in sibling directories."));
11009
+ console.log(chalk18.yellow(" No recognizable repos found in sibling directories."));
11112
11010
  } else {
11113
- console.log(chalk19.cyan("\n Detected repos:"));
11011
+ console.log(chalk18.cyan("\n Detected repos:"));
11114
11012
  for (const r of detected) {
11115
- console.log(chalk19.gray(` - ${r.name}: ${r.role} (${r.type}) at ${r.path}`));
11013
+ console.log(chalk18.gray(` - ${r.name}: ${r.role} (${r.type}) at ${r.path}`));
11116
11014
  }
11117
11015
  const keepAll = await confirm2({
11118
11016
  message: `Include all ${detected.length} detected repo(s)?`,
@@ -11129,7 +11027,7 @@ workspaceCmd.command("init").description(`Interactive workspace setup \u2014 cre
11129
11027
  if (keep) repos.push(r);
11130
11028
  }
11131
11029
  }
11132
- console.log(chalk19.green(` \u2714 ${repos.length} repo(s) added from auto-scan.`));
11030
+ console.log(chalk18.green(` \u2714 ${repos.length} repo(s) added from auto-scan.`));
11133
11031
  }
11134
11032
  }
11135
11033
  const repoTypeChoices = [
@@ -11151,7 +11049,7 @@ workspaceCmd.command("init").description(`Interactive workspace setup \u2014 cre
11151
11049
  default: repos.length === 0
11152
11050
  });
11153
11051
  while (addMore) {
11154
- console.log(chalk19.cyan(`
11052
+ console.log(chalk18.cyan(`
11155
11053
  Adding repo #${repos.length + 1}`));
11156
11054
  const repoName = await input({
11157
11055
  message: "Repo name (e.g. api, web, app):",
@@ -11165,16 +11063,16 @@ workspaceCmd.command("init").description(`Interactive workspace setup \u2014 cre
11165
11063
  message: `Relative path to "${repoName}" from here (default: ./${repoName}):`,
11166
11064
  default: `./${repoName}`
11167
11065
  });
11168
- const absPath = path23.resolve(currentDir, repoPath);
11066
+ const absPath = path22.resolve(currentDir, repoPath);
11169
11067
  let detectedType = "unknown";
11170
11068
  let detectedRole = "shared";
11171
- if (await fs24.pathExists(absPath)) {
11069
+ if (await fs23.pathExists(absPath)) {
11172
11070
  const { type, role } = await detectRepoType(absPath);
11173
11071
  detectedType = type;
11174
11072
  detectedRole = role;
11175
- console.log(chalk19.gray(` Auto-detected: type=${type}, role=${role}`));
11073
+ console.log(chalk18.gray(` Auto-detected: type=${type}, role=${role}`));
11176
11074
  } else {
11177
- console.log(chalk19.yellow(` Path "${absPath}" not found \u2014 type/role will be manual.`));
11075
+ console.log(chalk18.yellow(` Path "${absPath}" not found \u2014 type/role will be manual.`));
11178
11076
  }
11179
11077
  const repoType = await select3({
11180
11078
  message: `Repo type for "${repoName}":`,
@@ -11197,53 +11095,53 @@ workspaceCmd.command("init").description(`Interactive workspace setup \u2014 cre
11197
11095
  type: repoType,
11198
11096
  role: repoRole
11199
11097
  });
11200
- console.log(chalk19.green(` \u2714 Added: ${repoName} (${repoRole}, ${repoType})`));
11098
+ console.log(chalk18.green(` \u2714 Added: ${repoName} (${repoRole}, ${repoType})`));
11201
11099
  addMore = await confirm2({
11202
11100
  message: "Add another repo?",
11203
11101
  default: false
11204
11102
  });
11205
11103
  }
11206
11104
  const workspaceConfig = { name: workspaceName, repos };
11207
- console.log(chalk19.cyan("\n Workspace summary:"));
11208
- console.log(chalk19.gray(` Name: ${workspaceName}`));
11105
+ console.log(chalk18.cyan("\n Workspace summary:"));
11106
+ console.log(chalk18.gray(` Name: ${workspaceName}`));
11209
11107
  for (const r of repos) {
11210
- console.log(chalk19.gray(` - ${r.name}: ${r.role} (${r.type}) at ${r.path}`));
11108
+ console.log(chalk18.gray(` - ${r.name}: ${r.role} (${r.type}) at ${r.path}`));
11211
11109
  }
11212
11110
  const ok = await confirm2({ message: `Save to ${WORKSPACE_CONFIG_FILE}?`, default: true });
11213
11111
  if (!ok) {
11214
- console.log(chalk19.gray(" Cancelled."));
11112
+ console.log(chalk18.gray(" Cancelled."));
11215
11113
  return;
11216
11114
  }
11217
11115
  const loader = new WorkspaceLoader(currentDir);
11218
11116
  const saved = await loader.save(workspaceConfig);
11219
- console.log(chalk19.green(`
11117
+ console.log(chalk18.green(`
11220
11118
  \u2714 Workspace saved: ${saved}`));
11221
- console.log(chalk19.gray(` Run \`ai-spec create "your feature"\` \u2014 workspace mode will activate automatically.`));
11119
+ console.log(chalk18.gray(` Run \`ai-spec create "your feature"\` \u2014 workspace mode will activate automatically.`));
11222
11120
  });
11223
11121
  workspaceCmd.command("status").description("Show current workspace configuration").action(async () => {
11224
11122
  const currentDir = process.cwd();
11225
11123
  const loader = new WorkspaceLoader(currentDir);
11226
11124
  const config2 = await loader.load();
11227
11125
  if (!config2) {
11228
- console.log(chalk19.yellow(`No ${WORKSPACE_CONFIG_FILE} found in ${currentDir}`));
11229
- console.log(chalk19.gray(" Run `ai-spec workspace init` to create one."));
11126
+ console.log(chalk18.yellow(`No ${WORKSPACE_CONFIG_FILE} found in ${currentDir}`));
11127
+ console.log(chalk18.gray(" Run `ai-spec workspace init` to create one."));
11230
11128
  return;
11231
11129
  }
11232
- console.log(chalk19.bold(`
11130
+ console.log(chalk18.bold(`
11233
11131
  Workspace: ${config2.name}`));
11234
- console.log(chalk19.gray(` Config: ${path23.join(currentDir, WORKSPACE_CONFIG_FILE)}`));
11235
- console.log(chalk19.gray(` Repos (${config2.repos.length}):
11132
+ console.log(chalk18.gray(` Config: ${path22.join(currentDir, WORKSPACE_CONFIG_FILE)}`));
11133
+ console.log(chalk18.gray(` Repos (${config2.repos.length}):
11236
11134
  `));
11237
11135
  for (const repo of config2.repos) {
11238
11136
  const absPath = loader.resolveAbsPath(repo);
11239
- const exists = await fs24.pathExists(absPath);
11240
- const status = exists ? chalk19.green("found") : chalk19.red("not found");
11137
+ const exists = await fs23.pathExists(absPath);
11138
+ const status = exists ? chalk18.green("found") : chalk18.red("not found");
11241
11139
  console.log(
11242
- ` ${chalk19.bold(repo.name.padEnd(12))} ${repo.role.padEnd(10)} ${repo.type.padEnd(16)} ${status}`
11140
+ ` ${chalk18.bold(repo.name.padEnd(12))} ${repo.role.padEnd(10)} ${repo.type.padEnd(16)} ${status}`
11243
11141
  );
11244
- console.log(chalk19.gray(` path: ${absPath}`));
11142
+ console.log(chalk18.gray(` path: ${absPath}`));
11245
11143
  if (repo.constitution) {
11246
- console.log(chalk19.green(` constitution: found`));
11144
+ console.log(chalk18.green(` constitution: found`));
11247
11145
  }
11248
11146
  }
11249
11147
  });
@@ -11260,30 +11158,30 @@ program.command("update").description("Update an existing spec with a change req
11260
11158
  const modelName = opts.model || config2.model || DEFAULT_MODELS[providerName];
11261
11159
  const apiKey = await resolveApiKey(providerName, opts.key);
11262
11160
  const provider = createProvider(providerName, apiKey, modelName);
11263
- console.log(chalk19.blue("\n\u2500\u2500\u2500 ai-spec update \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11264
- console.log(chalk19.gray(` Provider: ${providerName}/${modelName}`));
11161
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 ai-spec update \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11162
+ console.log(chalk18.gray(` Provider: ${providerName}/${modelName}`));
11265
11163
  const updateRunId = generateRunId();
11266
11164
  const updateSnapshot = new RunSnapshot(currentDir, updateRunId);
11267
11165
  setActiveSnapshot(updateSnapshot);
11268
11166
  const updateLogger = new RunLogger(currentDir, updateRunId, { provider: providerName, model: modelName });
11269
11167
  setActiveLogger(updateLogger);
11270
- console.log(chalk19.gray(` Run ID: ${updateRunId}`));
11168
+ console.log(chalk18.gray(` Run ID: ${updateRunId}`));
11271
11169
  let specPath = opts.spec ?? null;
11272
11170
  if (!specPath) {
11273
- const specsDir = path23.join(currentDir, "specs");
11171
+ const specsDir = path22.join(currentDir, "specs");
11274
11172
  const latest = await SpecUpdater.findLatestSpec(specsDir);
11275
11173
  if (!latest) {
11276
- console.error(chalk19.red(" No spec files found in specs/. Run `ai-spec create` first or use --spec <path>."));
11174
+ console.error(chalk18.red(" No spec files found in specs/. Run `ai-spec create` first or use --spec <path>."));
11277
11175
  process.exit(1);
11278
11176
  }
11279
11177
  specPath = latest.filePath;
11280
- console.log(chalk19.gray(` Using spec: ${path23.relative(currentDir, specPath)} (v${latest.version})`));
11178
+ console.log(chalk18.gray(` Using spec: ${path22.relative(currentDir, specPath)} (v${latest.version})`));
11281
11179
  }
11282
- console.log(chalk19.gray(" Loading project context..."));
11180
+ console.log(chalk18.gray(" Loading project context..."));
11283
11181
  const loader = new ContextLoader(currentDir);
11284
11182
  const context = await loader.loadProjectContext();
11285
11183
  if (context.constitution && context.constitution.length > 6e3) {
11286
- console.log(chalk19.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
11184
+ console.log(chalk18.yellow(` \u26A0 Constitution is long (${context.constitution.length.toLocaleString()} chars). Consider running: ai-spec init --consolidate`));
11287
11185
  }
11288
11186
  const { detectRepoType: _detectRepoType } = await Promise.resolve().then(() => (init_workspace_loader(), workspace_loader_exports));
11289
11187
  const { type: repoType } = await _detectRepoType(currentDir);
@@ -11295,19 +11193,19 @@ program.command("update").description("Update an existing spec with a change req
11295
11193
  repoType
11296
11194
  });
11297
11195
  } catch (err) {
11298
- console.error(chalk19.red(` Update failed: ${err.message}`));
11196
+ console.error(chalk18.red(` Update failed: ${err.message}`));
11299
11197
  process.exit(1);
11300
11198
  }
11301
- console.log(chalk19.green(`
11302
- \u2714 Spec updated \u2192 v${result.newVersion}: ${path23.relative(currentDir, result.newSpecPath)}`));
11199
+ console.log(chalk18.green(`
11200
+ \u2714 Spec updated \u2192 v${result.newVersion}: ${path22.relative(currentDir, result.newSpecPath)}`));
11303
11201
  if (result.newDslPath) {
11304
- console.log(chalk19.green(` \u2714 DSL updated: ${path23.relative(currentDir, result.newDslPath)}`));
11202
+ console.log(chalk18.green(` \u2714 DSL updated: ${path22.relative(currentDir, result.newDslPath)}`));
11305
11203
  }
11306
11204
  if (result.affectedFiles.length > 0) {
11307
- console.log(chalk19.cyan("\n Affected files:"));
11205
+ console.log(chalk18.cyan("\n Affected files:"));
11308
11206
  for (const f of result.affectedFiles) {
11309
- const icon = f.action === "create" ? chalk19.green("+") : chalk19.yellow("~");
11310
- console.log(` ${icon} ${f.file}: ${chalk19.gray(f.description)}`);
11207
+ const icon = f.action === "create" ? chalk18.green("+") : chalk18.yellow("~");
11208
+ console.log(` ${icon} ${f.file}: ${chalk18.gray(f.description)}`);
11311
11209
  }
11312
11210
  }
11313
11211
  if (opts.codegen && result.affectedFiles.length > 0) {
@@ -11315,9 +11213,9 @@ program.command("update").description("Update an existing spec with a change req
11315
11213
  const codegenModelName = opts.codegenModel || config2.codegenModel || DEFAULT_MODELS[codegenProviderName];
11316
11214
  const codegenApiKey = opts.codegenKey ?? (codegenProviderName === providerName ? apiKey : await resolveApiKey(codegenProviderName, opts.codegenKey));
11317
11215
  const codegenProvider = createProvider(codegenProviderName, codegenApiKey, codegenModelName);
11318
- console.log(chalk19.blue("\n Regenerating affected files..."));
11216
+ console.log(chalk18.blue("\n Regenerating affected files..."));
11319
11217
  const codeGenerator = new CodeGenerator(codegenProvider, "api");
11320
- const specContent = await fs24.readFile(result.newSpecPath, "utf-8");
11218
+ const specContent = await fs23.readFile(result.newSpecPath, "utf-8");
11321
11219
  const constitutionSection = context.constitution ? `
11322
11220
  === Project Constitution (MUST follow) ===
11323
11221
  ${context.constitution}
@@ -11328,10 +11226,10 @@ ${JSON.stringify(result.updatedDsl, null, 2).slice(0, 3e3)}
11328
11226
  ` : "";
11329
11227
  updateLogger.stageStart("update_codegen");
11330
11228
  for (const affected of result.affectedFiles) {
11331
- const fullPath = path23.join(currentDir, affected.file);
11229
+ const fullPath = path22.join(currentDir, affected.file);
11332
11230
  let existing = "";
11333
11231
  try {
11334
- existing = await fs24.readFile(fullPath, "utf-8");
11232
+ existing = await fs23.readFile(fullPath, "utf-8");
11335
11233
  } catch {
11336
11234
  }
11337
11235
  const codePrompt = `Apply this change to the file.
@@ -11345,23 +11243,23 @@ ${specContent}
11345
11243
  ${constitutionSection}${dslSection}
11346
11244
  === ${existing ? "Current File (return the FULL updated content)" : "New File"} ===
11347
11245
  ${existing || "Create from scratch."}`;
11348
- process.stdout.write(` ${existing ? chalk19.yellow("~") : chalk19.green("+")} ${affected.file}... `);
11246
+ process.stdout.write(` ${existing ? chalk18.yellow("~") : chalk18.green("+")} ${affected.file}... `);
11349
11247
  try {
11350
11248
  const { getCodeGenSystemPrompt: _getPrompt } = await Promise.resolve().then(() => (init_codegen_prompt(), codegen_prompt_exports));
11351
11249
  const raw = await codegenProvider.generate(codePrompt, _getPrompt(repoType));
11352
11250
  const content = raw.replace(/^```\w*\n?/gm, "").replace(/\n?```$/gm, "").trim();
11353
- await fs24.ensureDir(path23.dirname(fullPath));
11251
+ await fs23.ensureDir(path22.dirname(fullPath));
11354
11252
  await updateSnapshot.snapshotFile(fullPath);
11355
- await fs24.writeFile(fullPath, content, "utf-8");
11253
+ await fs23.writeFile(fullPath, content, "utf-8");
11356
11254
  updateLogger.fileWritten(affected.file);
11357
- console.log(chalk19.green("\u2714"));
11255
+ console.log(chalk18.green("\u2714"));
11358
11256
  } catch (err) {
11359
11257
  updateLogger.stageFail("update_codegen", `${affected.file}: ${err.message}`);
11360
- console.log(chalk19.red(`\u2718 ${err.message}`));
11258
+ console.log(chalk18.red(`\u2718 ${err.message}`));
11361
11259
  }
11362
11260
  }
11363
11261
  updateLogger.stageEnd("update_codegen", { filesUpdated: result.affectedFiles.length });
11364
- const updatedSpecContent = await fs24.readFile(result.newSpecPath, "utf-8").catch(() => "");
11262
+ const updatedSpecContent = await fs23.readFile(result.newSpecPath, "utf-8").catch(() => "");
11365
11263
  if (updatedSpecContent) {
11366
11264
  const updateReviewer = new CodeReviewer(provider, currentDir);
11367
11265
  const reviewResult = await updateReviewer.reviewCode(updatedSpecContent, result.newSpecPath).catch(() => "");
@@ -11373,13 +11271,13 @@ ${existing || "Create from scratch."}`;
11373
11271
  updateLogger.finish();
11374
11272
  updateLogger.printSummary();
11375
11273
  if (updateSnapshot.fileCount > 0) {
11376
- console.log(chalk19.gray(` To undo changes: ai-spec restore ${updateRunId}`));
11274
+ console.log(chalk18.gray(` To undo changes: ai-spec restore ${updateRunId}`));
11377
11275
  }
11378
11276
  if (!opts.codegen && result.affectedFiles.length > 0) {
11379
- console.log(chalk19.blue("\n Next steps:"));
11380
- console.log(chalk19.gray(` \u2022 Re-run with --codegen to regenerate affected files automatically`));
11381
- console.log(chalk19.gray(` \u2022 Or update files manually based on the affected files list above`));
11382
- console.log(chalk19.gray(` \u2022 Run \`ai-spec mock\` to refresh the mock server with the new DSL`));
11277
+ console.log(chalk18.blue("\n Next steps:"));
11278
+ console.log(chalk18.gray(` \u2022 Re-run with --codegen to regenerate affected files automatically`));
11279
+ console.log(chalk18.gray(` \u2022 Or update files manually based on the affected files list above`));
11280
+ console.log(chalk18.gray(` \u2022 Run \`ai-spec mock\` to refresh the mock server with the new DSL`));
11383
11281
  }
11384
11282
  });
11385
11283
  program.command("export").description("Export the latest DSL to OpenAPI 3.1.0 (YAML or JSON)").option("--openapi", "Export as OpenAPI 3.1.0 (default behaviour)").option("--format <fmt>", "Output format: yaml | json (default: yaml)", "yaml").option("--output <path>", "Output file path (default: openapi.yaml)").option("--server <url>", "API server URL in the OpenAPI document (default: http://localhost:3000)").option("--dsl <path>", "Path to a specific .dsl.json file (auto-detected if omitted)").action(async (opts) => {
@@ -11388,19 +11286,19 @@ program.command("export").description("Export the latest DSL to OpenAPI 3.1.0 (Y
11388
11286
  if (!dslPath) {
11389
11287
  dslPath = await findLatestDslFile(currentDir);
11390
11288
  if (!dslPath) {
11391
- console.error(chalk19.red(" No .dsl.json file found. Run `ai-spec create` first or use --dsl <path>."));
11289
+ console.error(chalk18.red(" No .dsl.json file found. Run `ai-spec create` first or use --dsl <path>."));
11392
11290
  process.exit(1);
11393
11291
  }
11394
- console.log(chalk19.gray(` Using DSL: ${path23.relative(currentDir, dslPath)}`));
11292
+ console.log(chalk18.gray(` Using DSL: ${path22.relative(currentDir, dslPath)}`));
11395
11293
  }
11396
11294
  let dsl;
11397
11295
  try {
11398
- dsl = await fs24.readJson(dslPath);
11296
+ dsl = await fs23.readJson(dslPath);
11399
11297
  } catch (err) {
11400
- console.error(chalk19.red(` Failed to read DSL: ${err.message}`));
11298
+ console.error(chalk18.red(` Failed to read DSL: ${err.message}`));
11401
11299
  process.exit(1);
11402
11300
  }
11403
- console.log(chalk19.blue("\n\u2500\u2500\u2500 ai-spec export \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11301
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 ai-spec export \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11404
11302
  const format = opts.format === "json" ? "json" : "yaml";
11405
11303
  const serverUrl = opts.server || "http://localhost:3000";
11406
11304
  try {
@@ -11409,31 +11307,31 @@ program.command("export").description("Export the latest DSL to OpenAPI 3.1.0 (Y
11409
11307
  serverUrl,
11410
11308
  outputPath: opts.output
11411
11309
  });
11412
- const rel = path23.relative(currentDir, outputPath);
11413
- console.log(chalk19.green(` \u2714 OpenAPI ${format.toUpperCase()} exported: ${rel}`));
11414
- console.log(chalk19.gray(` Feature : ${dsl.feature.title}`));
11415
- console.log(chalk19.gray(` Endpoints: ${dsl.endpoints.length}`));
11416
- console.log(chalk19.gray(` Models : ${dsl.models.length}`));
11417
- console.log(chalk19.gray(` Server : ${serverUrl}`));
11418
- console.log(chalk19.blue("\n Next steps:"));
11419
- console.log(chalk19.gray(` \u2022 Import ${rel} into Postman / Insomnia / Swagger UI`));
11420
- console.log(chalk19.gray(` \u2022 Use openapi-generator to generate client SDKs`));
11310
+ const rel = path22.relative(currentDir, outputPath);
11311
+ console.log(chalk18.green(` \u2714 OpenAPI ${format.toUpperCase()} exported: ${rel}`));
11312
+ console.log(chalk18.gray(` Feature : ${dsl.feature.title}`));
11313
+ console.log(chalk18.gray(` Endpoints: ${dsl.endpoints.length}`));
11314
+ console.log(chalk18.gray(` Models : ${dsl.models.length}`));
11315
+ console.log(chalk18.gray(` Server : ${serverUrl}`));
11316
+ console.log(chalk18.blue("\n Next steps:"));
11317
+ console.log(chalk18.gray(` \u2022 Import ${rel} into Postman / Insomnia / Swagger UI`));
11318
+ console.log(chalk18.gray(` \u2022 Use openapi-generator to generate client SDKs`));
11421
11319
  } catch (err) {
11422
- console.error(chalk19.red(` Export failed: ${err.message}`));
11320
+ console.error(chalk18.red(` Export failed: ${err.message}`));
11423
11321
  process.exit(1);
11424
11322
  }
11425
11323
  });
11426
11324
  program.command("mock").description("Generate a standalone mock server + proxy config from the latest DSL").option("--port <n>", "Mock server port (default: 3001)", "3001").option("--msw", "Also generate MSW (Mock Service Worker) handlers at src/mocks/").option("--proxy", "Also generate frontend proxy config snippet").option("--dsl <path>", "Path to a specific .dsl.json file (auto-detected if omitted)").option("--workspace", "Generate mock assets for all backend repos in the workspace").option("--serve", "Start mock server in background + patch frontend proxy (use with --frontend)").option("--frontend <path>", "Path to frontend project for proxy patching (used with --serve/--restore)").option("--restore", "Undo proxy changes and stop mock server (requires --frontend or auto-detects)").action(async (opts) => {
11427
11325
  const currentDir = process.cwd();
11428
11326
  const port = parseInt(opts.port, 10) || 3001;
11429
- console.log(chalk19.blue("\n\u2500\u2500\u2500 ai-spec mock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11327
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 ai-spec mock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11430
11328
  if (opts.restore) {
11431
- const frontendDir = opts.frontend ? path23.resolve(opts.frontend) : currentDir;
11329
+ const frontendDir = opts.frontend ? path22.resolve(opts.frontend) : currentDir;
11432
11330
  const r = await restoreMockProxy(frontendDir);
11433
11331
  if (r.restored) {
11434
- console.log(chalk19.green(" \u2714 Proxy restored and mock server stopped."));
11332
+ console.log(chalk18.green(" \u2714 Proxy restored and mock server stopped."));
11435
11333
  } else {
11436
- console.log(chalk19.yellow(` ${r.note ?? "Nothing to restore."}`));
11334
+ console.log(chalk18.yellow(` ${r.note ?? "Nothing to restore."}`));
11437
11335
  }
11438
11336
  return;
11439
11337
  }
@@ -11441,32 +11339,32 @@ program.command("mock").description("Generate a standalone mock server + proxy c
11441
11339
  const workspaceLoader = new WorkspaceLoader(currentDir);
11442
11340
  const workspaceConfig = await workspaceLoader.load();
11443
11341
  if (!workspaceConfig) {
11444
- console.error(chalk19.red(` No ${WORKSPACE_CONFIG_FILE} found. Run \`ai-spec workspace init\` first.`));
11342
+ console.error(chalk18.red(` No ${WORKSPACE_CONFIG_FILE} found. Run \`ai-spec workspace init\` first.`));
11445
11343
  process.exit(1);
11446
11344
  }
11447
11345
  const backendRepos = workspaceConfig.repos.filter((r) => r.role === "backend");
11448
11346
  if (backendRepos.length === 0) {
11449
- console.log(chalk19.yellow(" No backend repos found in workspace."));
11347
+ console.log(chalk18.yellow(" No backend repos found in workspace."));
11450
11348
  return;
11451
11349
  }
11452
11350
  for (const repo of backendRepos) {
11453
11351
  const repoAbsPath = workspaceLoader.resolveAbsPath(repo);
11454
- console.log(chalk19.cyan(`
11352
+ console.log(chalk18.cyan(`
11455
11353
  Repo: ${repo.name} (${repoAbsPath})`));
11456
11354
  const dslFile = await findLatestDslFile(repoAbsPath);
11457
11355
  if (!dslFile) {
11458
- console.log(chalk19.yellow(` No DSL file found \u2014 skipping.`));
11356
+ console.log(chalk18.yellow(` No DSL file found \u2014 skipping.`));
11459
11357
  continue;
11460
11358
  }
11461
- const dsl2 = await fs24.readJson(dslFile);
11359
+ const dsl2 = await fs23.readJson(dslFile);
11462
11360
  const result2 = await generateMockAssets(dsl2, repoAbsPath, {
11463
11361
  port,
11464
11362
  msw: opts.msw,
11465
11363
  proxy: opts.proxy
11466
11364
  });
11467
11365
  for (const f of result2.files) {
11468
- console.log(chalk19.green(` \u2714 ${f.path}`));
11469
- console.log(chalk19.gray(` ${f.description}`));
11366
+ console.log(chalk18.green(` \u2714 ${f.path}`));
11367
+ console.log(chalk18.gray(` ${f.description}`));
11470
11368
  }
11471
11369
  }
11472
11370
  return;
@@ -11476,19 +11374,19 @@ program.command("mock").description("Generate a standalone mock server + proxy c
11476
11374
  dslPath = await findLatestDslFile(currentDir);
11477
11375
  if (!dslPath) {
11478
11376
  console.error(
11479
- chalk19.red(
11377
+ chalk18.red(
11480
11378
  " No .dsl.json file found in .ai-spec/. Run `ai-spec create` first or use --dsl <path>."
11481
11379
  )
11482
11380
  );
11483
11381
  process.exit(1);
11484
11382
  }
11485
- console.log(chalk19.gray(` Using DSL: ${path23.relative(currentDir, dslPath)}`));
11383
+ console.log(chalk18.gray(` Using DSL: ${path22.relative(currentDir, dslPath)}`));
11486
11384
  }
11487
11385
  let dsl;
11488
11386
  try {
11489
- dsl = await fs24.readJson(dslPath);
11387
+ dsl = await fs23.readJson(dslPath);
11490
11388
  } catch (err) {
11491
- console.error(chalk19.red(` Failed to read DSL file: ${err.message}`));
11389
+ console.error(chalk18.red(` Failed to read DSL file: ${err.message}`));
11492
11390
  process.exit(1);
11493
11391
  }
11494
11392
  const result = await generateMockAssets(dsl, currentDir, {
@@ -11496,58 +11394,58 @@ program.command("mock").description("Generate a standalone mock server + proxy c
11496
11394
  msw: opts.msw,
11497
11395
  proxy: opts.proxy
11498
11396
  });
11499
- console.log(chalk19.green(`
11397
+ console.log(chalk18.green(`
11500
11398
  \u2714 Mock assets generated (${result.files.length} file(s)):`));
11501
11399
  for (const f of result.files) {
11502
- console.log(chalk19.green(` ${f.path}`));
11503
- console.log(chalk19.gray(` ${f.description}`));
11400
+ console.log(chalk18.green(` ${f.path}`));
11401
+ console.log(chalk18.gray(` ${f.description}`));
11504
11402
  }
11505
11403
  if (opts.serve) {
11506
- const serverJsPath = path23.join(currentDir, "mock", "server.js");
11507
- if (!await fs24.pathExists(serverJsPath)) {
11508
- console.error(chalk19.red(" mock/server.js not found \u2014 generation may have failed."));
11404
+ const serverJsPath = path22.join(currentDir, "mock", "server.js");
11405
+ if (!await fs23.pathExists(serverJsPath)) {
11406
+ console.error(chalk18.red(" mock/server.js not found \u2014 generation may have failed."));
11509
11407
  process.exit(1);
11510
11408
  }
11511
11409
  const pid = startMockServerBackground(serverJsPath, port);
11512
- console.log(chalk19.green(`
11410
+ console.log(chalk18.green(`
11513
11411
  \u2714 Mock server started (PID ${pid}) \u2192 http://localhost:${port}`));
11514
11412
  if (opts.frontend) {
11515
- const frontendDir = path23.resolve(opts.frontend);
11413
+ const frontendDir = path22.resolve(opts.frontend);
11516
11414
  const proxyResult = await applyMockProxy(frontendDir, port, dsl.endpoints);
11517
11415
  await saveMockServerPid(frontendDir, pid);
11518
11416
  if (proxyResult.applied) {
11519
- console.log(chalk19.green(` \u2714 Frontend proxy patched (${proxyResult.framework})`));
11520
- console.log(chalk19.bold.cyan(`
11417
+ console.log(chalk18.green(` \u2714 Frontend proxy patched (${proxyResult.framework})`));
11418
+ console.log(chalk18.bold.cyan(`
11521
11419
  Ready! Open a new terminal and run:`));
11522
- console.log(chalk19.white(` cd ${frontendDir}`));
11523
- console.log(chalk19.white(` ${proxyResult.devCommand}`));
11524
- console.log(chalk19.gray(`
11420
+ console.log(chalk18.white(` cd ${frontendDir}`));
11421
+ console.log(chalk18.white(` ${proxyResult.devCommand}`));
11422
+ console.log(chalk18.gray(`
11525
11423
  When done: ai-spec mock --restore --frontend ${frontendDir}`));
11526
11424
  } else {
11527
- console.log(chalk19.yellow(` \u26A0 Auto-patch not available for ${proxyResult.framework}.`));
11528
- if (proxyResult.note) console.log(chalk19.gray(` ${proxyResult.note}`));
11425
+ console.log(chalk18.yellow(` \u26A0 Auto-patch not available for ${proxyResult.framework}.`));
11426
+ if (proxyResult.note) console.log(chalk18.gray(` ${proxyResult.note}`));
11529
11427
  }
11530
11428
  } else {
11531
- console.log(chalk19.gray(` Tip: use --frontend <path> to also auto-patch your frontend proxy config.`));
11532
- console.log(chalk19.gray(` Mock server: http://localhost:${port}`));
11429
+ console.log(chalk18.gray(` Tip: use --frontend <path> to also auto-patch your frontend proxy config.`));
11430
+ console.log(chalk18.gray(` Mock server: http://localhost:${port}`));
11533
11431
  }
11534
11432
  return;
11535
11433
  }
11536
- console.log(chalk19.blue("\n\u2500\u2500\u2500 Quick start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11537
- console.log(chalk19.white(` 1. Install express (if not already):`));
11538
- console.log(chalk19.gray(` npm install --save-dev express`));
11539
- console.log(chalk19.white(` 2. Start mock server:`));
11540
- console.log(chalk19.gray(` node mock/server.js`));
11541
- console.log(chalk19.gray(` # or: ai-spec mock --serve --frontend <path-to-frontend>`));
11542
- console.log(chalk19.white(` 3. Configure your frontend to proxy API calls to:`));
11543
- console.log(chalk19.gray(` http://localhost:${port}`));
11434
+ console.log(chalk18.blue("\n\u2500\u2500\u2500 Quick start \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11435
+ console.log(chalk18.white(` 1. Install express (if not already):`));
11436
+ console.log(chalk18.gray(` npm install --save-dev express`));
11437
+ console.log(chalk18.white(` 2. Start mock server:`));
11438
+ console.log(chalk18.gray(` node mock/server.js`));
11439
+ console.log(chalk18.gray(` # or: ai-spec mock --serve --frontend <path-to-frontend>`));
11440
+ console.log(chalk18.white(` 3. Configure your frontend to proxy API calls to:`));
11441
+ console.log(chalk18.gray(` http://localhost:${port}`));
11544
11442
  if (opts.proxy) {
11545
- console.log(chalk19.gray(` (See the generated proxy config file for framework-specific instructions)`));
11443
+ console.log(chalk18.gray(` (See the generated proxy config file for framework-specific instructions)`));
11546
11444
  }
11547
11445
  if (opts.msw) {
11548
- console.log(chalk19.white(` 4. MSW: import and start the worker in your app entry:`));
11549
- console.log(chalk19.gray(` import { worker } from './mocks/browser';`));
11550
- console.log(chalk19.gray(` if (process.env.NODE_ENV === 'development') worker.start();`));
11446
+ console.log(chalk18.white(` 4. MSW: import and start the worker in your app entry:`));
11447
+ console.log(chalk18.gray(` import { worker } from './mocks/browser';`));
11448
+ console.log(chalk18.gray(` if (process.env.NODE_ENV === 'development') worker.start();`));
11551
11449
  }
11552
11450
  });
11553
11451
  program.command("learn").description("Append a lesson or engineering decision directly to constitution \xA79").argument("[lesson]", "The lesson or decision to record (prompted if omitted)").action(async (lesson) => {
@@ -11563,34 +11461,26 @@ program.command("learn").description("Append a lesson or engineering decision di
11563
11461
  }
11564
11462
  const result = await appendDirectLesson(currentDir, lesson.trim());
11565
11463
  if (result.appended) {
11566
- console.log(chalk19.green(`
11464
+ console.log(chalk18.green(`
11567
11465
  \u2714 Lesson appended to constitution \xA79`));
11568
- console.log(chalk19.gray(` File: .ai-spec-constitution.md`));
11466
+ console.log(chalk18.gray(` File: .ai-spec-constitution.md`));
11569
11467
  } else {
11570
- console.log(chalk19.yellow(`
11468
+ console.log(chalk18.yellow(`
11571
11469
  \u26A0 Not appended: ${result.reason}`));
11572
11470
  }
11573
11471
  });
11574
11472
  program.command("restore").description("Restore files modified by a previous run").argument("<runId>", "Run ID shown at the end of a create / generate run").action(async (runId) => {
11575
11473
  const currentDir = process.cwd();
11576
11474
  const snapshot = new RunSnapshot(currentDir, runId);
11577
- console.log(chalk19.blue(`Restoring run: ${runId}...`));
11475
+ console.log(chalk18.blue(`Restoring run: ${runId}...`));
11578
11476
  const restored = await snapshot.restore();
11579
11477
  if (restored.length === 0) {
11580
- console.log(chalk19.yellow(" No backup found for this run ID."));
11478
+ console.log(chalk18.yellow(" No backup found for this run ID."));
11581
11479
  } else {
11582
- restored.forEach((f) => console.log(chalk19.green(` \u2714 restored: ${f}`)));
11583
- console.log(chalk19.bold.green(`
11480
+ restored.forEach((f) => console.log(chalk18.green(` \u2714 restored: ${f}`)));
11481
+ console.log(chalk18.bold.green(`
11584
11482
  \u2714 ${restored.length} file(s) restored.`));
11585
11483
  }
11586
11484
  });
11587
- if (process.argv.length <= 2) {
11588
- (async () => {
11589
- const currentDir = process.cwd();
11590
- const config2 = await loadConfig(currentDir);
11591
- await printWelcome(currentDir, config2);
11592
- })();
11593
- } else {
11594
- program.parse();
11595
- }
11485
+ program.parse();
11596
11486
  //# sourceMappingURL=index.mjs.map