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