@superspec/cli 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
+ // src/commands/archive.ts
2
+ import { existsSync as existsSync3, readdirSync as readdirSync2, renameSync } from "fs";
3
+ import { join as join2 } from "path";
4
+
1
5
  // src/core/config.ts
2
- import { readFileSync, existsSync } from "fs";
6
+ import { existsSync, readFileSync } from "fs";
3
7
  import { join } from "path";
4
8
  var DEFAULT_CONFIG = {
5
9
  lang: "en",
@@ -61,120 +65,6 @@ function deepMerge(target, source) {
61
65
  return result;
62
66
  }
63
67
 
64
- // src/core/template.ts
65
- import { existsSync as existsSync4, copyFileSync, readFileSync as readFileSync2, writeFileSync } from "fs";
66
- import { join as join3, dirname as dirname2 } from "path";
67
-
68
- // src/utils/paths.ts
69
- import { dirname, join as join2 } from "path";
70
- import { existsSync as existsSync2 } from "fs";
71
- import { fileURLToPath } from "url";
72
- function getPackageRoot() {
73
- const __filename2 = fileURLToPath(import.meta.url);
74
- let dir = dirname(__filename2);
75
- while (dir !== dirname(dir)) {
76
- if (existsSync2(join2(dir, "package.json")) && existsSync2(join2(dir, "templates"))) {
77
- return dir;
78
- }
79
- dir = dirname(dir);
80
- }
81
- return join2(dirname(__filename2), "..", "..");
82
- }
83
-
84
- // src/utils/fs.ts
85
- import { mkdirSync, existsSync as existsSync3, readdirSync } from "fs";
86
- function ensureDir(dir) {
87
- if (!existsSync3(dir)) {
88
- mkdirSync(dir, { recursive: true });
89
- }
90
- }
91
-
92
- // src/core/template.ts
93
- function resolveTemplatePath(templateName, lang = "zh") {
94
- const root = getPackageRoot();
95
- const langPath = join3(root, "templates", lang, templateName);
96
- if (existsSync4(langPath)) return langPath;
97
- const fallbackLang = lang === "zh" ? "en" : "zh";
98
- const fallback = join3(root, "templates", fallbackLang, templateName);
99
- if (existsSync4(fallback)) return fallback;
100
- throw new Error(`Template not found: ${templateName} (lang: ${lang})`);
101
- }
102
- function copyTemplate(templateName, destPath, lang = "zh") {
103
- const srcPath = resolveTemplatePath(templateName, lang);
104
- ensureDir(dirname2(destPath));
105
- copyFileSync(srcPath, destPath);
106
- }
107
- function renderTemplate(templateName, vars = {}, lang = "zh") {
108
- const srcPath = resolveTemplatePath(templateName, lang);
109
- let content = readFileSync2(srcPath, "utf-8");
110
- content = processConditionals(content, vars);
111
- for (const [key, value] of Object.entries(vars)) {
112
- content = content.replaceAll(`{{${key}}}`, value);
113
- }
114
- return content;
115
- }
116
- function processConditionals(content, vars) {
117
- const ifRegex = /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
118
- return content.replace(ifRegex, (match, varName, innerContent) => {
119
- const value = vars[varName];
120
- if (value && value.trim() !== "") {
121
- return innerContent;
122
- }
123
- return "";
124
- });
125
- }
126
- function writeRenderedTemplate(templateName, destPath, vars = {}, lang = "zh") {
127
- const content = renderTemplate(templateName, vars, lang);
128
- ensureDir(dirname2(destPath));
129
- writeFileSync(destPath, content, "utf-8");
130
- }
131
-
132
- // src/core/lint.ts
133
- import { readFileSync as readFileSync3, existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
134
- import { join as join4, basename } from "path";
135
-
136
- // src/core/validate.ts
137
- import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
138
- import { join as join5 } from "path";
139
-
140
- // src/core/context.ts
141
- import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
142
- import { join as join6 } from "path";
143
-
144
- // src/utils/date.ts
145
- function getDateString() {
146
- const d = /* @__PURE__ */ new Date();
147
- return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
148
- }
149
-
150
- // src/utils/git.ts
151
- import { execSync } from "child_process";
152
- var GIT_TIMEOUT = 1e4;
153
- function isGitRepo() {
154
- try {
155
- execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore", timeout: GIT_TIMEOUT });
156
- return true;
157
- } catch {
158
- return false;
159
- }
160
- }
161
- var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
162
- function createBranch(branchName) {
163
- if (!SAFE_BRANCH_RE.test(branchName)) {
164
- throw new Error(`invalid branch name: ${branchName}`);
165
- }
166
- execSync(`git checkout -b ${branchName}`, { stdio: "inherit", timeout: GIT_TIMEOUT });
167
- }
168
-
169
- // src/commands/init.ts
170
- import { existsSync as existsSync9, writeFileSync as writeFileSync3, readdirSync as readdirSync5 } from "fs";
171
- import { join as join8 } from "path";
172
- import { execSync as execSync2 } from "child_process";
173
-
174
- // src/prompts/index.ts
175
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync2, readdirSync as readdirSync4 } from "fs";
176
- import { join as join7 } from "path";
177
-
178
68
  // src/ui/index.ts
179
69
  import chalk from "chalk";
180
70
  var theme = {
@@ -223,7 +113,9 @@ function boxText(text, width = 50) {
223
113
  }
224
114
  function createBox(lines, width = 52) {
225
115
  const top = theme.border(`${box.topLeft}${box.horizontal.repeat(width - 2)}${box.topRight}`);
226
- const bottom = theme.border(`${box.bottomLeft}${box.horizontal.repeat(width - 2)}${box.bottomRight}`);
116
+ const bottom = theme.border(
117
+ `${box.bottomLeft}${box.horizontal.repeat(width - 2)}${box.bottomRight}`
118
+ );
227
119
  const middle = lines.map((line) => boxText(line, width));
228
120
  return [top, ...middle, bottom].join("\n");
229
121
  }
@@ -279,7 +171,7 @@ function t(en, zh) {
279
171
  function printSummary(items) {
280
172
  const maxLabel = Math.max(...items.map((i) => i.label.length));
281
173
  const width = 50;
282
- console.log(theme.border("\u256D" + "\u2500".repeat(width - 2) + "\u256E"));
174
+ console.log(theme.border(`\u256D${"\u2500".repeat(width - 2)}\u256E`));
283
175
  for (const { label, value } of items) {
284
176
  const padding = " ".repeat(maxLabel - label.length);
285
177
  const line = `${theme.dim(label)}${padding} ${symbol.arrow} ${theme.highlight(value)}`;
@@ -287,10 +179,213 @@ function printSummary(items) {
287
179
  const rightPad = " ".repeat(Math.max(0, width - plainLine.length - 4));
288
180
  console.log(theme.border("\u2502 ") + line + rightPad + theme.border(" \u2502"));
289
181
  }
290
- console.log(theme.border("\u2570" + "\u2500".repeat(width - 2) + "\u256F"));
182
+ console.log(theme.border(`\u2570${"\u2500".repeat(width - 2)}\u256F`));
183
+ }
184
+
185
+ // src/utils/date.ts
186
+ function getDateString() {
187
+ const d = /* @__PURE__ */ new Date();
188
+ return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
189
+ }
190
+
191
+ // src/utils/fs.ts
192
+ import { existsSync as existsSync2, mkdirSync, readdirSync } from "fs";
193
+ function ensureDir(dir) {
194
+ if (!existsSync2(dir)) {
195
+ mkdirSync(dir, { recursive: true });
196
+ }
197
+ }
198
+
199
+ // src/commands/archive.ts
200
+ async function archiveCommand(name, options) {
201
+ const cwd = process.cwd();
202
+ const config = loadConfig(cwd);
203
+ const changesDir = join2(cwd, config.specDir, "changes");
204
+ const archiveDir = join2(cwd, config.specDir, "changes", config.archive.dir);
205
+ if (!existsSync3(changesDir)) {
206
+ log.warn(`${symbol.warn} ${t("no changes directory found", "\u672A\u627E\u5230 changes \u76EE\u5F55")}`);
207
+ return;
208
+ }
209
+ if (options.all) {
210
+ const entries = readdirSync2(changesDir, { withFileTypes: true }).filter(
211
+ (e) => e.isDirectory() && e.name !== config.archive.dir
212
+ );
213
+ if (entries.length === 0) {
214
+ log.warn(`${symbol.warn} ${t("no changes to archive", "\u6CA1\u6709\u53EF\u5F52\u6863\u7684\u53D8\u66F4")}`);
215
+ return;
216
+ }
217
+ log.info(`${symbol.start} ${t("archiving all changes...", "\u5F52\u6863\u6240\u6709\u53D8\u66F4...")}`);
218
+ for (const entry of entries) {
219
+ archiveOne(entry.name, changesDir, archiveDir, config);
220
+ }
221
+ } else if (name) {
222
+ const changePath = join2(changesDir, name);
223
+ if (!existsSync3(changePath)) {
224
+ log.warn(`${symbol.warn} ${t(`change "${name}" not found`, `\u53D8\u66F4 "${name}" \u4E0D\u5B58\u5728`)}`);
225
+ return;
226
+ }
227
+ log.info(`${symbol.start} ${t(`archiving: ${name}`, `\u5F52\u6863\u53D8\u66F4: ${name}`)}`);
228
+ archiveOne(name, changesDir, archiveDir, config);
229
+ } else {
230
+ log.warn(`${symbol.warn} ${t("specify a name or use --all", "\u8BF7\u6307\u5B9A\u53D8\u66F4\u540D\u79F0\u6216\u4F7F\u7528 --all")}`);
231
+ return;
232
+ }
233
+ log.info(`${symbol.start} ${t("archive done!", "\u5F52\u6863\u5B8C\u6210\uFF01")}`);
234
+ }
235
+ function archiveOne(name, changesDir, archiveDir, config) {
236
+ ensureDir(archiveDir);
237
+ const src = join2(changesDir, name);
238
+ const dateStr = config.archive.datePrefix ? `${getDateString()}-` : "";
239
+ const dest = join2(archiveDir, `${dateStr}${name}`);
240
+ if (existsSync3(dest)) {
241
+ log.warn(` ${symbol.warn} ${t("archive target exists", "\u5F52\u6863\u76EE\u6807\u5DF2\u5B58\u5728")}: ${dest}`);
242
+ return;
243
+ }
244
+ renameSync(src, dest);
245
+ log.success(` ${symbol.ok} ${name} \u2192 archive/${dateStr}${name}`);
246
+ }
247
+
248
+ // src/commands/create.ts
249
+ import { existsSync as existsSync5 } from "fs";
250
+ import { join as join4 } from "path";
251
+
252
+ // src/utils/git.ts
253
+ import { execSync } from "child_process";
254
+ var GIT_TIMEOUT = 1e4;
255
+ function isGitRepo() {
256
+ try {
257
+ execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore", timeout: GIT_TIMEOUT });
258
+ return true;
259
+ } catch {
260
+ return false;
261
+ }
262
+ }
263
+ var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
264
+ function createBranch(branchName) {
265
+ if (!SAFE_BRANCH_RE.test(branchName)) {
266
+ throw new Error(`invalid branch name: ${branchName}`);
267
+ }
268
+ execSync(`git checkout -b ${branchName}`, { stdio: "inherit", timeout: GIT_TIMEOUT });
269
+ }
270
+
271
+ // src/utils/paths.ts
272
+ import { existsSync as existsSync4 } from "fs";
273
+ import { dirname, join as join3 } from "path";
274
+ import { fileURLToPath } from "url";
275
+ function getPackageRoot() {
276
+ const __filename2 = fileURLToPath(import.meta.url);
277
+ let dir = dirname(__filename2);
278
+ while (dir !== dirname(dir)) {
279
+ if (existsSync4(join3(dir, "package.json")) && existsSync4(join3(dir, "templates"))) {
280
+ return dir;
281
+ }
282
+ dir = dirname(dir);
283
+ }
284
+ return join3(dirname(__filename2), "..", "..");
285
+ }
286
+
287
+ // src/utils/template.ts
288
+ function renderNameTemplate(template, vars, strict = false) {
289
+ let result = template;
290
+ for (const [key, value] of Object.entries(vars)) {
291
+ if (value !== void 0) {
292
+ result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
293
+ }
294
+ }
295
+ result = result.replace(/\{[^}]+\}/g, "");
296
+ if (strict) {
297
+ return result.replace(/[^a-zA-Z0-9._\-/]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
298
+ }
299
+ return result.replace(/[^\p{L}\p{N}._\-/]/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
300
+ }
301
+
302
+ // src/commands/create.ts
303
+ async function createCommand(feature, options) {
304
+ const cwd = process.cwd();
305
+ const config = loadConfig(cwd);
306
+ const specDir = options.specDir || config.specDir;
307
+ const branchPrefix = options.branchPrefix || config.branchPrefix;
308
+ const boost = options.boost || config.boost;
309
+ const strategy = options.creative ? "create" : config.strategy;
310
+ const templateVars = {
311
+ prefix: branchPrefix,
312
+ intentType: options.intentType,
313
+ feature,
314
+ date: getDateString(),
315
+ user: options.user
316
+ };
317
+ const changeNameTemplate = options.changeNameTemplate || config.changeNameTemplate || "{date}-{feature}";
318
+ const changeFolderName = renderNameTemplate(changeNameTemplate, templateVars, false);
319
+ const changePath = join4(cwd, specDir, "changes", changeFolderName);
320
+ if (existsSync5(changePath)) {
321
+ log.warn(
322
+ `${symbol.warn} ${t(`change "${changeFolderName}" already exists`, `\u53D8\u66F4 "${changeFolderName}" \u5DF2\u5B58\u5728`)}: ${changePath}`
323
+ );
324
+ return;
325
+ }
326
+ log.title(`${t("Creating Change", "\u521B\u5EFA\u53D8\u66F4")}: ${changeFolderName}`);
327
+ if (options.intentType) {
328
+ log.info(`${t("Intent Type", "\u610F\u56FE\u7C7B\u578B")}: ${options.intentType}`);
329
+ }
330
+ if (boost) {
331
+ log.boost(`${symbol.bolt} ${t("Boost mode enabled", "\u589E\u5F3A\u6A21\u5F0F\u5DF2\u542F\u7528")}`);
332
+ }
333
+ if (strategy === "create") {
334
+ log.boost(
335
+ `${symbol.bolt} ${t("Creative mode: exploring new solutions", "\u521B\u9020\u6A21\u5F0F\uFF1A\u63A2\u7D22\u65B0\u65B9\u6848")}`
336
+ );
337
+ }
338
+ ensureDir(changePath);
339
+ if (options.branch !== false && isGitRepo()) {
340
+ const branchTemplate = options.branchTemplate || config.branchTemplate || "{prefix}{date}-{feature}";
341
+ const branchName = renderNameTemplate(branchTemplate, templateVars, true);
342
+ try {
343
+ createBranch(branchName);
344
+ log.success(`${symbol.ok} Branch: ${branchName}`);
345
+ } catch (e) {
346
+ log.warn(`${symbol.warn} ${t("branch creation failed", "\u5206\u652F\u521B\u5EFA\u5931\u8D25")}: ${e.message}`);
347
+ }
348
+ }
349
+ log.done(t("Change created successfully!", "\u53D8\u66F4\u521B\u5EFA\u6210\u529F\uFF01"));
350
+ log.dim(`${t("Path", "\u8DEF\u5F84")}: ${specDir}/changes/${changeFolderName}/`);
351
+ log.dim(`${t("Templates", "\u6A21\u677F\u53C2\u8003")}: ${specDir}/templates/`);
352
+ const expectedArtifacts = boost ? config.boostArtifacts : config.artifacts;
353
+ log.dim(`${t("Expected artifacts", "\u9884\u671F Artifacts")}: ${expectedArtifacts.join(", ")}`);
354
+ log.dim(
355
+ `${t("Next", "\u4E0B\u4E00\u6B65")}: ${t("AI generates artifacts on demand via /ss-create", "AI \u6309\u9700\u901A\u8FC7 /ss-create \u751F\u6210 artifacts")}`
356
+ );
357
+ }
358
+
359
+ // src/commands/deps.ts
360
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
361
+ import { join as join5 } from "path";
362
+
363
+ // src/commands/init.ts
364
+ import { execSync as execSync2 } from "child_process";
365
+ import { existsSync as existsSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "fs";
366
+ import { join as join8 } from "path";
367
+
368
+ // src/core/template.ts
369
+ import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
370
+ import { dirname as dirname2, join as join6 } from "path";
371
+ function resolveTemplatePath(templateName, lang = "zh") {
372
+ const root = getPackageRoot();
373
+ const langPath = join6(root, "templates", lang, templateName);
374
+ if (existsSync7(langPath)) return langPath;
375
+ const fallbackLang = lang === "zh" ? "en" : "zh";
376
+ const fallback = join6(root, "templates", fallbackLang, templateName);
377
+ if (existsSync7(fallback)) return fallback;
378
+ throw new Error(`Template not found: ${templateName} (lang: ${lang})`);
379
+ }
380
+ function copyTemplate(templateName, destPath, lang = "zh") {
381
+ const srcPath = resolveTemplatePath(templateName, lang);
382
+ ensureDir(dirname2(destPath));
383
+ copyFileSync(srcPath, destPath);
291
384
  }
292
385
 
293
386
  // src/prompts/index.ts
387
+ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
388
+ import { join as join7 } from "path";
294
389
  var AI_EDITORS = {
295
390
  claude: {
296
391
  commands: ".claude/commands",
@@ -335,10 +430,10 @@ function installRules(cwd, editor) {
335
430
  ensureDir(rulesDir);
336
431
  const promptSrc = join7(getPackageRoot(), "prompts", "rules.md");
337
432
  if (existsSync8(promptSrc)) {
338
- const content = readFileSync6(promptSrc, "utf-8");
433
+ const content = readFileSync4(promptSrc, "utf-8");
339
434
  const rulesFile = "rulesFile" in config ? config.rulesFile : "superspec.md";
340
435
  const destPath = join7(rulesDir, rulesFile);
341
- writeFileSync2(destPath, content, "utf-8");
436
+ writeFileSync3(destPath, content, "utf-8");
342
437
  log.success(`${symbol.ok} ${config.rules}/${rulesFile}`);
343
438
  }
344
439
  }
@@ -348,25 +443,27 @@ function installAgentsMd(cwd) {
348
443
  const agentsMdPath = join7(cwd, "AGENTS.md");
349
444
  const agentPromptSrc = join7(getPackageRoot(), "prompts", "agents.md");
350
445
  if (!existsSync8(agentPromptSrc)) return;
351
- const newContent = readFileSync6(agentPromptSrc, "utf-8");
446
+ const newContent = readFileSync4(agentPromptSrc, "utf-8");
352
447
  const wrapped = `${SS_START}
353
448
  ${newContent}
354
449
  ${SS_END}`;
355
450
  if (existsSync8(agentsMdPath)) {
356
- const existing = readFileSync6(agentsMdPath, "utf-8");
451
+ const existing = readFileSync4(agentsMdPath, "utf-8");
357
452
  const startIdx = existing.indexOf(SS_START);
358
453
  const endIdx = existing.indexOf(SS_END);
359
454
  if (startIdx !== -1 && endIdx !== -1) {
360
455
  const before = existing.slice(0, startIdx);
361
456
  const after = existing.slice(endIdx + SS_END.length);
362
- writeFileSync2(agentsMdPath, before + wrapped + after, "utf-8");
457
+ writeFileSync3(agentsMdPath, before + wrapped + after, "utf-8");
363
458
  } else if (existing.includes("SuperSpec")) {
364
- writeFileSync2(agentsMdPath, existing, "utf-8");
459
+ writeFileSync3(agentsMdPath, existing, "utf-8");
365
460
  } else {
366
- writeFileSync2(agentsMdPath, existing + "\n\n" + wrapped, "utf-8");
461
+ writeFileSync3(agentsMdPath, `${existing}
462
+
463
+ ${wrapped}`, "utf-8");
367
464
  }
368
465
  } else {
369
- writeFileSync2(agentsMdPath, wrapped, "utf-8");
466
+ writeFileSync3(agentsMdPath, wrapped, "utf-8");
370
467
  }
371
468
  log.success(`${symbol.ok} AGENTS.md`);
372
469
  }
@@ -385,8 +482,8 @@ function installCommands(cwd, editor, lang = "zh") {
385
482
  for (const file of commandFiles) {
386
483
  const srcPath = join7(sourceDir, file);
387
484
  const destPath = join7(commandsDir, file);
388
- const content = readFileSync6(srcPath, "utf-8");
389
- writeFileSync2(destPath, content, "utf-8");
485
+ const content = readFileSync4(srcPath, "utf-8");
486
+ writeFileSync3(destPath, content, "utf-8");
390
487
  }
391
488
  log.success(`${symbol.ok} ${config.commands}/ (${commandFiles.length} commands)`);
392
489
  }
@@ -396,7 +493,9 @@ async function initCommand(options) {
396
493
  const cwd = process.cwd();
397
494
  const configPath = join8(cwd, "superspec.config.json");
398
495
  if (existsSync9(configPath) && !options.force) {
399
- log.warn(`${symbol.warn} ${t("superspec.config.json already exists, use --force to overwrite", "superspec.config.json \u5DF2\u5B58\u5728\uFF0C\u4F7F\u7528 --force \u8986\u76D6")}`);
496
+ log.warn(
497
+ `${symbol.warn} ${t("superspec.config.json already exists, use --force to overwrite", "superspec.config.json \u5DF2\u5B58\u5728\uFF0C\u4F7F\u7528 --force \u8986\u76D6")}`
498
+ );
400
499
  return;
401
500
  }
402
501
  const lang = options.lang || "zh";
@@ -412,12 +511,17 @@ async function initCommand(options) {
412
511
  const specDir = join8(cwd, config.specDir);
413
512
  const existingFiles = readdirSync5(cwd).filter((f) => !f.startsWith(".") && f !== "node_modules");
414
513
  if (existingFiles.length > 0 && !options.force) {
415
- log.warn(`${symbol.warn} ${t(`current directory is not empty (${existingFiles.length} items)`, `\u5F53\u524D\u76EE\u5F55\u975E\u7A7A\uFF08${existingFiles.length} \u9879\uFF09`)}`);
416
- log.dim(` ${t("template files will be merged with existing content", "\u6A21\u677F\u6587\u4EF6\u5C06\u4E0E\u73B0\u6709\u5185\u5BB9\u5408\u5E76")}`);
514
+ log.warn(
515
+ `${symbol.warn} ${t(`current directory is not empty (${existingFiles.length} items)`, `\u5F53\u524D\u76EE\u5F55\u975E\u7A7A\uFF08${existingFiles.length} \u9879\uFF09`)}`
516
+ );
517
+ log.dim(
518
+ ` ${t("template files will be merged with existing content", "\u6A21\u677F\u6587\u4EF6\u5C06\u4E0E\u73B0\u6709\u5185\u5BB9\u5408\u5E76")}`
519
+ );
417
520
  console.log();
418
521
  }
419
522
  log.section(t("Creating Configuration", "\u521B\u5EFA\u914D\u7F6E"));
420
- writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
523
+ writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
524
+ `, "utf-8");
421
525
  log.success(`${symbol.file} superspec.config.json`);
422
526
  log.section(t("Creating Directory Structure", "\u521B\u5EFA\u76EE\u5F55\u7ED3\u6784"));
423
527
  ensureDir(join8(specDir, "changes"));
@@ -425,7 +529,9 @@ async function initCommand(options) {
425
529
  log.success(`${symbol.folder} ${config.specDir}/changes/`);
426
530
  log.success(`${symbol.folder} ${config.specDir}/templates/`);
427
531
  log.section(t("Installing Templates", "\u5B89\u88C5\u6A21\u677F"));
428
- const templateNames = Object.values(config.templates).map((v) => v.endsWith(".md") ? v : `${v}.md`);
532
+ const templateNames = Object.values(config.templates).map(
533
+ (v) => v.endsWith(".md") ? v : `${v}.md`
534
+ );
429
535
  for (const tpl of templateNames) {
430
536
  try {
431
537
  copyTemplate(tpl, join8(specDir, "templates", tpl), lang);
@@ -454,168 +560,52 @@ async function initCommand(options) {
454
560
  log.dim(`${t("Next", "\u4E0B\u4E00\u6B65")}: superspec create <feature>`);
455
561
  }
456
562
 
457
- // src/commands/create.ts
458
- import { existsSync as existsSync10 } from "fs";
459
- import { join as join9 } from "path";
563
+ // src/commands/lint.ts
564
+ import { existsSync as existsSync11 } from "fs";
565
+ import { join as join10 } from "path";
460
566
 
461
- // src/utils/template.ts
462
- function renderNameTemplate(template, vars, strict = false) {
463
- let result = template;
464
- for (const [key, value] of Object.entries(vars)) {
465
- if (value !== void 0) {
466
- result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
467
- }
468
- }
469
- result = result.replace(/\{[^}]+\}/g, "");
470
- if (strict) {
471
- return result.replace(/[^a-zA-Z0-9._\-/]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
472
- }
473
- return result.replace(/[^\p{L}\p{N}._\-/]/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
474
- }
567
+ // src/core/lint.ts
568
+ import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync5 } from "fs";
569
+ import { basename, join as join9 } from "path";
475
570
 
476
- // src/commands/create.ts
477
- async function createCommand(feature, options) {
478
- const cwd = process.cwd();
479
- const config = loadConfig(cwd);
480
- const specDir = options.specDir || config.specDir;
481
- const branchPrefix = options.branchPrefix || config.branchPrefix;
482
- const boost = options.boost || config.boost;
483
- const strategy = options.creative ? "create" : config.strategy;
484
- const description = options.description || "";
485
- const lang = options.lang || config.lang || "en";
486
- const templateVars = {
487
- prefix: branchPrefix,
488
- intentType: options.intentType,
489
- feature,
490
- date: getDateString(),
491
- user: options.user
492
- };
493
- const changeNameTemplate = options.changeNameTemplate || config.changeNameTemplate || "{date}-{feature}";
494
- const changeFolderName = renderNameTemplate(changeNameTemplate, templateVars, false);
495
- const changePath = join9(cwd, specDir, "changes", changeFolderName);
496
- if (existsSync10(changePath)) {
497
- log.warn(`${symbol.warn} ${t(`change "${changeFolderName}" already exists`, `\u53D8\u66F4 "${changeFolderName}" \u5DF2\u5B58\u5728`)}: ${changePath}`);
498
- return;
499
- }
500
- log.title(`${t("Creating Change", "\u521B\u5EFA\u53D8\u66F4")}: ${changeFolderName}`);
501
- if (options.intentType) {
502
- log.info(`${t("Intent Type", "\u610F\u56FE\u7C7B\u578B")}: ${options.intentType}`);
503
- }
504
- if (boost) {
505
- log.boost(`${symbol.bolt} ${t("Boost mode enabled", "\u589E\u5F3A\u6A21\u5F0F\u5DF2\u542F\u7528")}`);
506
- }
507
- if (strategy === "create") {
508
- log.boost(`${symbol.bolt} ${t("Creative mode: exploring new solutions", "\u521B\u9020\u6A21\u5F0F\uFF1A\u63A2\u7D22\u65B0\u65B9\u6848")}`);
509
- }
510
- ensureDir(changePath);
511
- const vars = {
512
- name: changeFolderName,
513
- date: templateVars.date,
514
- boost: boost ? "true" : "false",
515
- strategy,
516
- description
517
- };
518
- const artifacts = boost ? config.boostArtifacts : config.artifacts;
519
- log.section(t("Generating Artifacts", "\u751F\u6210 Artifacts"));
520
- for (const artifact of artifacts) {
521
- const templateFile = config.templates[artifact] || `${artifact}.md`;
522
- const destPath = join9(changePath, `${artifact}.md`);
523
- try {
524
- writeRenderedTemplate(templateFile, destPath, vars, lang);
525
- log.success(`${symbol.ok} ${artifact}.md`);
526
- } catch (e) {
527
- log.error(`${symbol.fail} ${artifact}.md: ${e.message}`);
528
- }
529
- }
530
- if (options.branch !== false && isGitRepo()) {
531
- const branchTemplate = options.branchTemplate || config.branchTemplate || "{prefix}{date}-{feature}";
532
- const branchName = renderNameTemplate(branchTemplate, templateVars, true);
533
- try {
534
- createBranch(branchName);
535
- log.success(`${symbol.ok} Branch: ${branchName}`);
536
- } catch (e) {
537
- log.warn(`${symbol.warn} ${t("branch creation failed", "\u5206\u652F\u521B\u5EFA\u5931\u8D25")}: ${e.message}`);
538
- }
539
- }
540
- log.done(t("Change created successfully!", "\u53D8\u66F4\u521B\u5EFA\u6210\u529F\uFF01"));
541
- log.dim(`${t("Path", "\u8DEF\u5F84")}: ${specDir}/changes/${changeFolderName}/`);
542
- if (boost) {
543
- log.dim(`${t("Workflow", "\u5DE5\u4F5C\u6D41")}: /ss-create \u2192 /ss-tasks \u2192 /ss-apply (boost)`);
544
- } else {
545
- log.dim(`${t("Workflow", "\u5DE5\u4F5C\u6D41")}: /ss-tasks \u2192 /ss-apply`);
546
- }
547
- log.dim(`${t("Next", "\u4E0B\u4E00\u6B65")}: superspec lint ${changeFolderName}`);
548
- }
571
+ // src/commands/search.ts
572
+ import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync6 } from "fs";
573
+ import { basename as basename2, join as join11 } from "path";
549
574
 
550
- // src/commands/archive.ts
551
- import { existsSync as existsSync11, readdirSync as readdirSync6, renameSync } from "fs";
552
- import { join as join10 } from "path";
553
- async function archiveCommand(name, options) {
554
- const cwd = process.cwd();
555
- const config = loadConfig(cwd);
556
- const changesDir = join10(cwd, config.specDir, "changes");
557
- const archiveDir = join10(cwd, config.specDir, "changes", config.archive.dir);
558
- if (!existsSync11(changesDir)) {
559
- log.warn(`${symbol.warn} ${t("no changes directory found", "\u672A\u627E\u5230 changes \u76EE\u5F55")}`);
560
- return;
561
- }
562
- if (options.all) {
563
- const entries = readdirSync6(changesDir, { withFileTypes: true }).filter(
564
- (e) => e.isDirectory() && e.name !== config.archive.dir
565
- );
566
- if (entries.length === 0) {
567
- log.warn(`${symbol.warn} ${t("no changes to archive", "\u6CA1\u6709\u53EF\u5F52\u6863\u7684\u53D8\u66F4")}`);
568
- return;
569
- }
570
- log.info(`${symbol.start} ${t("archiving all changes...", "\u5F52\u6863\u6240\u6709\u53D8\u66F4...")}`);
571
- for (const entry of entries) {
572
- archiveOne(entry.name, changesDir, archiveDir, config);
573
- }
574
- } else if (name) {
575
- const changePath = join10(changesDir, name);
576
- if (!existsSync11(changePath)) {
577
- log.warn(`${symbol.warn} ${t(`change "${name}" not found`, `\u53D8\u66F4 "${name}" \u4E0D\u5B58\u5728`)}`);
578
- return;
579
- }
580
- log.info(`${symbol.start} ${t(`archiving: ${name}`, `\u5F52\u6863\u53D8\u66F4: ${name}`)}`);
581
- archiveOne(name, changesDir, archiveDir, config);
582
- } else {
583
- log.warn(`${symbol.warn} ${t("specify a name or use --all", "\u8BF7\u6307\u5B9A\u53D8\u66F4\u540D\u79F0\u6216\u4F7F\u7528 --all")}`);
584
- return;
585
- }
586
- log.info(`${symbol.start} ${t("archive done!", "\u5F52\u6863\u5B8C\u6210\uFF01")}`);
587
- }
588
- function archiveOne(name, changesDir, archiveDir, config) {
589
- ensureDir(archiveDir);
590
- const src = join10(changesDir, name);
591
- const dateStr = config.archive.datePrefix ? `${getDateString()}-` : "";
592
- const dest = join10(archiveDir, `${dateStr}${name}`);
593
- if (existsSync11(dest)) {
594
- log.warn(` ${symbol.warn} ${t("archive target exists", "\u5F52\u6863\u76EE\u6807\u5DF2\u5B58\u5728")}: ${dest}`);
595
- return;
596
- }
597
- renameSync(src, dest);
598
- log.success(` ${symbol.ok} ${name} \u2192 archive/${dateStr}${name}`);
599
- }
575
+ // src/commands/status.ts
576
+ import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync7 } from "fs";
577
+ import { join as join12 } from "path";
578
+
579
+ // src/commands/sync.ts
580
+ import { existsSync as existsSync15, writeFileSync as writeFileSync5 } from "fs";
581
+ import { join as join14 } from "path";
582
+
583
+ // src/core/context.ts
584
+ import { existsSync as existsSync14, readFileSync as readFileSync8 } from "fs";
585
+ import { join as join13 } from "path";
600
586
 
601
587
  // src/commands/update.ts
602
- import { existsSync as existsSync12 } from "fs";
603
- import { join as join11 } from "path";
588
+ import { existsSync as existsSync16 } from "fs";
589
+ import { join as join15 } from "path";
604
590
  async function updateCommand() {
605
591
  const cwd = process.cwd();
606
592
  const config = loadConfig(cwd);
607
- const specDir = join11(cwd, config.specDir);
593
+ const specDir = join15(cwd, config.specDir);
608
594
  const lang = config.lang || "zh";
609
- if (!existsSync12(join11(cwd, "superspec.config.json"))) {
610
- log.warn(`${symbol.warn} ${t("not initialized, run superspec init first", "\u5F53\u524D\u76EE\u5F55\u672A\u521D\u59CB\u5316 SuperSpec\uFF0C\u8BF7\u5148\u8FD0\u884C superspec init")}`);
595
+ if (!existsSync16(join15(cwd, "superspec.config.json"))) {
596
+ log.warn(
597
+ `${symbol.warn} ${t("not initialized, run superspec init first", "\u5F53\u524D\u76EE\u5F55\u672A\u521D\u59CB\u5316 SuperSpec\uFF0C\u8BF7\u5148\u8FD0\u884C superspec init")}`
598
+ );
611
599
  return;
612
600
  }
613
601
  log.info(`${symbol.start} ${t("updating SuperSpec...", "\u66F4\u65B0 SuperSpec...")}`);
614
- const templateNames = Object.values(config.templates).map((v) => v.endsWith(".md") ? v : `${v}.md`);
615
- ensureDir(join11(specDir, "templates"));
602
+ const templateNames = Object.values(config.templates).map(
603
+ (v) => v.endsWith(".md") ? v : `${v}.md`
604
+ );
605
+ ensureDir(join15(specDir, "templates"));
616
606
  for (const tpl of templateNames) {
617
607
  try {
618
- copyTemplate(tpl, join11(specDir, "templates", tpl), lang);
608
+ copyTemplate(tpl, join15(specDir, "templates", tpl), lang);
619
609
  } catch {
620
610
  }
621
611
  }
@@ -629,29 +619,13 @@ async function updateCommand() {
629
619
  log.info(`${symbol.start} ${t("update done!", "\u66F4\u65B0\u5B8C\u6210\uFF01")}`);
630
620
  }
631
621
 
632
- // src/commands/lint.ts
633
- import { existsSync as existsSync13 } from "fs";
634
- import { join as join12 } from "path";
635
-
636
622
  // src/commands/validate.ts
637
- import { existsSync as existsSync14 } from "fs";
638
- import { join as join13 } from "path";
639
-
640
- // src/commands/search.ts
641
- import { existsSync as existsSync15, readFileSync as readFileSync7, readdirSync as readdirSync7 } from "fs";
642
- import { join as join14, basename as basename3 } from "path";
643
-
644
- // src/commands/deps.ts
645
- import { existsSync as existsSync16, readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync8 } from "fs";
646
- import { join as join15 } from "path";
623
+ import { existsSync as existsSync18 } from "fs";
624
+ import { join as join17 } from "path";
647
625
 
648
- // src/commands/status.ts
649
- import { existsSync as existsSync17, readFileSync as readFileSync9, readdirSync as readdirSync9 } from "fs";
626
+ // src/core/validate.ts
627
+ import { existsSync as existsSync17, readFileSync as readFileSync9 } from "fs";
650
628
  import { join as join16 } from "path";
651
-
652
- // src/commands/sync.ts
653
- import { existsSync as existsSync18, writeFileSync as writeFileSync5 } from "fs";
654
- import { join as join17 } from "path";
655
629
  export {
656
630
  archiveCommand,
657
631
  createCommand,