lee-spec-kit 0.2.0 → 0.2.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/index.js CHANGED
@@ -1,12 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import path7 from 'path';
3
+ import { fileURLToPath } from 'url';
2
4
  import { program } from 'commander';
3
5
  import prompts from 'prompts';
4
6
  import chalk from 'chalk';
5
- import path6 from 'path';
6
7
  import fs6 from 'fs-extra';
7
8
  import { glob } from 'glob';
8
- import { fileURLToPath } from 'url';
9
+ import { spawn, execSync } from 'child_process';
10
+ import os from 'os';
9
11
 
12
+ var getFilename = () => fileURLToPath(import.meta.url);
13
+ var getDirname = () => path7.dirname(getFilename());
14
+ var __dirname$1 = /* @__PURE__ */ getDirname();
10
15
  async function copyTemplates(src, dest) {
11
16
  await fs6.copy(src, dest, {
12
17
  overwrite: true,
@@ -32,10 +37,10 @@ async function replaceInFiles(dir, replacements) {
32
37
  }
33
38
  }
34
39
  var __filename2 = fileURLToPath(import.meta.url);
35
- var __dirname2 = path6.dirname(__filename2);
40
+ var __dirname2 = path7.dirname(__filename2);
36
41
  function getTemplatesDir() {
37
- const rootDir = path6.resolve(__dirname2, "..");
38
- return path6.join(rootDir, "templates");
42
+ const rootDir = path7.resolve(__dirname2, "..");
43
+ return path7.join(rootDir, "templates");
39
44
  }
40
45
 
41
46
  // src/utils/validation.ts
@@ -132,8 +137,17 @@ function assertValid(result, context) {
132
137
  throw new Error(message);
133
138
  }
134
139
  }
135
-
136
- // src/commands/init.ts
140
+ function checkGitRepo(cwd) {
141
+ try {
142
+ execSync("git rev-parse --is-inside-work-tree", {
143
+ cwd,
144
+ stdio: "ignore"
145
+ });
146
+ return true;
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
137
151
  function initCommand(program2) {
138
152
  program2.command("init").description("Initialize project documentation structure").option("-n, --name <name>", "Project name (default: current folder name)").option("-t, --type <type>", "Project type: single | fullstack").option("-l, --lang <lang>", "Language: ko | en (default: ko)").option("-d, --dir <dir>", "Target directory (default: ./docs)", "./docs").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
139
153
  try {
@@ -150,12 +164,36 @@ function initCommand(program2) {
150
164
  }
151
165
  async function runInit(options) {
152
166
  const cwd = process.cwd();
153
- const defaultName = path6.basename(cwd);
167
+ const defaultName = path7.basename(cwd);
154
168
  let projectName = options.name || defaultName;
155
169
  let projectType = options.type;
156
170
  let lang = options.lang || "ko";
157
- const targetDir = path6.resolve(cwd, options.dir || "./docs");
171
+ let docsRepo = "embedded";
172
+ let pushDocs;
173
+ let docsRemote;
174
+ const targetDir = path7.resolve(cwd, options.dir || "./docs");
175
+ const isInsideGitRepo = checkGitRepo(cwd);
158
176
  if (!options.yes) {
177
+ console.log();
178
+ console.log(chalk.blue(`\u{1F4CD} \uD604\uC7AC \uC704\uCE58: ${cwd}`));
179
+ if (isInsideGitRepo) {
180
+ console.log(chalk.green("\u2705 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0\uB428"));
181
+ console.log();
182
+ console.log(chalk.gray("\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uB0B4\uC5D0\uC11C \uC2E4\uD589\uD558\uACE0 \uACC4\uC2ED\uB2C8\uB2E4."));
183
+ console.log(
184
+ chalk.gray(
185
+ "\u2022 embedded: \uC5EC\uAE30\uC5D0 ./docs \uD3F4\uB354\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8\uC640 \uD568\uAED8 \uAD00\uB9AC\uB429\uB2C8\uB2E4."
186
+ )
187
+ );
188
+ console.log(
189
+ chalk.gray("\u2022 standalone: \uBCC4\uB3C4 \uD3F4\uB354\uC5D0\uC11C \uB3C5\uB9BD docs \uB808\uD3EC\uB85C \uAD00\uB9AC\uD558\uB824\uBA74,")
190
+ );
191
+ console.log(chalk.gray(" \uD574\uB2F9 \uD3F4\uB354\uB85C \uC774\uB3D9 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD574\uC8FC\uC138\uC694."));
192
+ } else {
193
+ console.log(chalk.yellow("\u26A0\uFE0F Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uAC10\uC9C0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
194
+ console.log(chalk.gray("\uC0C8\uB85C\uC6B4 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uC0DD\uC131\uB429\uB2C8\uB2E4."));
195
+ }
196
+ console.log();
159
197
  const response = await prompts(
160
198
  [
161
199
  {
@@ -191,6 +229,24 @@ async function runInit(options) {
191
229
  { title: "English (en)", value: "en" }
192
230
  ],
193
231
  initial: 0
232
+ },
233
+ {
234
+ type: "select",
235
+ name: "docsRepo",
236
+ message: "Docs \uAD00\uB9AC \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
237
+ choices: [
238
+ {
239
+ title: "embedded - \uD504\uB85C\uC81D\uD2B8 \uB0B4 \uD3EC\uD568 (./docs)",
240
+ value: "embedded",
241
+ description: "\uD504\uB85C\uC81D\uD2B8\uC640 \uD568\uAED8 push\uB429\uB2C8\uB2E4"
242
+ },
243
+ {
244
+ title: "standalone - \uBCC4\uB3C4 \uB3C5\uB9BD \uB808\uD3EC",
245
+ value: "standalone",
246
+ description: "push \uC5EC\uBD80\uB97C \uBCC4\uB3C4\uB85C \uC124\uC815\uD569\uB2C8\uB2E4"
247
+ }
248
+ ],
249
+ initial: 0
194
250
  }
195
251
  ],
196
252
  {
@@ -202,6 +258,53 @@ async function runInit(options) {
202
258
  projectName = response.projectName || projectName;
203
259
  projectType = response.projectType || projectType;
204
260
  lang = response.lang || lang;
261
+ docsRepo = response.docsRepo || "embedded";
262
+ if (docsRepo === "standalone") {
263
+ const standaloneResponse = await prompts(
264
+ [
265
+ {
266
+ type: "select",
267
+ name: "pushDocs",
268
+ message: "Docs push \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
269
+ choices: [
270
+ {
271
+ title: "local - \uB85C\uCEEC\uC5D0\uC11C\uB9CC \uAD00\uB9AC (push \uC548 \uD568)",
272
+ value: false
273
+ },
274
+ {
275
+ title: "remote - \uC6D0\uACA9\uC5D0\uB3C4 push",
276
+ value: true
277
+ }
278
+ ],
279
+ initial: 0
280
+ }
281
+ ],
282
+ {
283
+ onCancel: () => {
284
+ throw new Error("canceled");
285
+ }
286
+ }
287
+ );
288
+ pushDocs = standaloneResponse.pushDocs;
289
+ if (pushDocs === true) {
290
+ const remoteResponse = await prompts(
291
+ [
292
+ {
293
+ type: "text",
294
+ name: "docsRemote",
295
+ message: "\uC6D0\uACA9 \uB808\uD3EC URL\uC744 \uC785\uB825\uD558\uC138\uC694:",
296
+ validate: (value) => value.trim() ? true : "URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694"
297
+ }
298
+ ],
299
+ {
300
+ onCancel: () => {
301
+ throw new Error("canceled");
302
+ }
303
+ }
304
+ );
305
+ docsRemote = remoteResponse.docsRemote;
306
+ }
307
+ }
205
308
  }
206
309
  if (!projectType) {
207
310
  projectType = "single";
@@ -232,8 +335,8 @@ async function runInit(options) {
232
335
  console.log(chalk.gray(` \uACBD\uB85C: ${targetDir}`));
233
336
  console.log();
234
337
  const templatesDir = getTemplatesDir();
235
- const commonPath = path6.join(templatesDir, lang, "common");
236
- const typePath = path6.join(templatesDir, lang, projectType);
338
+ const commonPath = path7.join(templatesDir, lang, "common");
339
+ const typePath = path7.join(templatesDir, lang, projectType);
237
340
  if (await fs6.pathExists(commonPath)) {
238
341
  await copyTemplates(commonPath, targetDir);
239
342
  }
@@ -252,13 +355,20 @@ async function runInit(options) {
252
355
  projectName,
253
356
  projectType,
254
357
  lang,
255
- createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
358
+ createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
359
+ docsRepo
256
360
  };
257
- const configPath = path6.join(targetDir, ".lee-spec-kit.json");
361
+ if (docsRepo === "standalone") {
362
+ config.pushDocs = pushDocs;
363
+ if (pushDocs && docsRemote) {
364
+ config.docsRemote = docsRemote;
365
+ }
366
+ }
367
+ const configPath = path7.join(targetDir, ".lee-spec-kit.json");
258
368
  await fs6.writeJson(configPath, config, { spaces: 2 });
259
369
  console.log(chalk.green("\u2705 docs \uAD6C\uC870 \uC0DD\uC131 \uC644\uB8CC!"));
260
370
  console.log();
261
- await initGit(cwd, targetDir);
371
+ await initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote);
262
372
  console.log(chalk.blue("\uB2E4\uC74C \uB2E8\uACC4:"));
263
373
  console.log(chalk.gray(` 1. ${targetDir}/prd/README.md \uC791\uC131`));
264
374
  console.log(
@@ -266,8 +376,7 @@ async function runInit(options) {
266
376
  );
267
377
  console.log();
268
378
  }
269
- async function initGit(cwd, targetDir) {
270
- const { execSync } = await import('child_process');
379
+ async function initGit(cwd, targetDir, docsRepo, pushDocs, docsRemote) {
271
380
  try {
272
381
  try {
273
382
  execSync("git rev-parse --is-inside-work-tree", {
@@ -279,12 +388,23 @@ async function initGit(cwd, targetDir) {
279
388
  console.log(chalk.blue("\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911..."));
280
389
  execSync("git init", { cwd, stdio: "ignore" });
281
390
  }
282
- const relativePath = path6.relative(cwd, targetDir);
391
+ const relativePath = path7.relative(cwd, targetDir);
283
392
  execSync(`git add "${relativePath}"`, { cwd, stdio: "ignore" });
284
393
  execSync('git commit -m "init: docs \uAD6C\uC870 \uCD08\uAE30\uD654 (lee-spec-kit)"', {
285
394
  cwd,
286
395
  stdio: "ignore"
287
396
  });
397
+ if (docsRepo === "standalone" && pushDocs && docsRemote) {
398
+ try {
399
+ execSync(`git remote add origin "${docsRemote}"`, {
400
+ cwd,
401
+ stdio: "ignore"
402
+ });
403
+ console.log(chalk.green(`\u2705 Git remote \uC124\uC815 \uC644\uB8CC: ${docsRemote}`));
404
+ } catch {
405
+ console.log(chalk.yellow("\u26A0\uFE0F Git remote\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4."));
406
+ }
407
+ }
288
408
  console.log(chalk.green("\u2705 Git \uCD08\uAE30 \uCEE4\uBC0B \uC644\uB8CC!"));
289
409
  console.log();
290
410
  } catch (error) {
@@ -296,12 +416,12 @@ async function initGit(cwd, targetDir) {
296
416
  }
297
417
  async function getConfig(cwd) {
298
418
  const possibleDirs = [
299
- path6.join(cwd, "docs"),
419
+ path7.join(cwd, "docs"),
300
420
  cwd
301
421
  // 이미 docs 폴더 안에 있을 수 있음
302
422
  ];
303
423
  for (const docsDir of possibleDirs) {
304
- const configPath = path6.join(docsDir, ".lee-spec-kit.json");
424
+ const configPath = path7.join(docsDir, ".lee-spec-kit.json");
305
425
  if (await fs6.pathExists(configPath)) {
306
426
  try {
307
427
  const configFile = await fs6.readJson(configPath);
@@ -309,18 +429,21 @@ async function getConfig(cwd) {
309
429
  docsDir,
310
430
  projectName: configFile.projectName,
311
431
  projectType: configFile.projectType,
312
- lang: configFile.lang
432
+ lang: configFile.lang,
433
+ docsRepo: configFile.docsRepo,
434
+ pushDocs: configFile.pushDocs,
435
+ docsRemote: configFile.docsRemote
313
436
  };
314
437
  } catch {
315
438
  }
316
439
  }
317
- const agentsPath = path6.join(docsDir, "agents");
318
- const featuresPath = path6.join(docsDir, "features");
440
+ const agentsPath = path7.join(docsDir, "agents");
441
+ const featuresPath = path7.join(docsDir, "features");
319
442
  if (await fs6.pathExists(agentsPath) && await fs6.pathExists(featuresPath)) {
320
- const bePath = path6.join(featuresPath, "be");
321
- const fePath = path6.join(featuresPath, "fe");
443
+ const bePath = path7.join(featuresPath, "be");
444
+ const fePath = path7.join(featuresPath, "fe");
322
445
  const projectType = await fs6.pathExists(bePath) || await fs6.pathExists(fePath) ? "fullstack" : "single";
323
- const agentsMdPath = path6.join(agentsPath, "agents.md");
446
+ const agentsMdPath = path7.join(agentsPath, "agents.md");
324
447
  let lang = "ko";
325
448
  if (await fs6.pathExists(agentsMdPath)) {
326
449
  const content = await fs6.readFile(agentsMdPath, "utf-8");
@@ -392,17 +515,17 @@ async function runFeature(name, options) {
392
515
  }
393
516
  let featuresDir;
394
517
  if (projectType === "fullstack" && repo) {
395
- featuresDir = path6.join(docsDir, "features", repo);
518
+ featuresDir = path7.join(docsDir, "features", repo);
396
519
  } else {
397
- featuresDir = path6.join(docsDir, "features");
520
+ featuresDir = path7.join(docsDir, "features");
398
521
  }
399
522
  const featureFolderName = `${featureId}-${name}`;
400
- const featureDir = path6.join(featuresDir, featureFolderName);
523
+ const featureDir = path7.join(featuresDir, featureFolderName);
401
524
  if (await fs6.pathExists(featureDir)) {
402
525
  console.error(chalk.red(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: ${featureDir}`));
403
526
  process.exit(1);
404
527
  }
405
- const featureBasePath = path6.join(docsDir, "features", "feature-base");
528
+ const featureBasePath = path7.join(docsDir, "features", "feature-base");
406
529
  if (!await fs6.pathExists(featureBasePath)) {
407
530
  console.error(chalk.red("feature-base \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
408
531
  process.exit(1);
@@ -438,12 +561,12 @@ async function runFeature(name, options) {
438
561
  console.log();
439
562
  }
440
563
  async function getNextFeatureId(docsDir, projectType) {
441
- const featuresDir = path6.join(docsDir, "features");
564
+ const featuresDir = path7.join(docsDir, "features");
442
565
  let max = 0;
443
566
  const scanDirs = [];
444
567
  if (projectType === "fullstack") {
445
- scanDirs.push(path6.join(featuresDir, "be"));
446
- scanDirs.push(path6.join(featuresDir, "fe"));
568
+ scanDirs.push(path7.join(featuresDir, "be"));
569
+ scanDirs.push(path7.join(featuresDir, "fe"));
447
570
  } else {
448
571
  scanDirs.push(featuresDir);
449
572
  }
@@ -483,20 +606,20 @@ async function runStatus(options) {
483
606
  process.exit(1);
484
607
  }
485
608
  const { docsDir, projectType } = config;
486
- const featuresDir = path6.join(docsDir, "features");
609
+ const featuresDir = path7.join(docsDir, "features");
487
610
  const features = [];
488
611
  const idMap = /* @__PURE__ */ new Map();
489
612
  const scopes = projectType === "fullstack" ? ["be", "fe"] : [""];
490
613
  for (const scope of scopes) {
491
- const scanDir = scope ? path6.join(featuresDir, scope) : featuresDir;
614
+ const scanDir = scope ? path7.join(featuresDir, scope) : featuresDir;
492
615
  if (!await fs6.pathExists(scanDir)) continue;
493
616
  const entries = await fs6.readdir(scanDir, { withFileTypes: true });
494
617
  for (const entry of entries) {
495
618
  if (!entry.isDirectory()) continue;
496
619
  if (entry.name === "feature-base") continue;
497
- const featureDir = path6.join(scanDir, entry.name);
498
- const specPath = path6.join(featureDir, "spec.md");
499
- const tasksPath = path6.join(featureDir, "tasks.md");
620
+ const featureDir = path7.join(scanDir, entry.name);
621
+ const specPath = path7.join(featureDir, "spec.md");
622
+ const tasksPath = path7.join(featureDir, "tasks.md");
500
623
  if (!await fs6.pathExists(specPath)) continue;
501
624
  if (!await fs6.pathExists(tasksPath)) continue;
502
625
  const specContent = await fs6.readFile(specPath, "utf-8");
@@ -505,7 +628,7 @@ async function runStatus(options) {
505
628
  const name = extractSpecValue(specContent, "\uAE30\uB2A5\uBA85") || extractSpecValue(specContent, "Feature Name") || entry.name;
506
629
  const repo = extractSpecValue(specContent, "\uB300\uC0C1 \uB808\uD3EC") || extractSpecValue(specContent, "Target Repo") || (scope ? `{{projectName}}-${scope}` : "{{projectName}}");
507
630
  const issue = extractSpecValue(specContent, "\uC774\uC288 \uBC88\uD638") || extractSpecValue(specContent, "Issue Number") || "-";
508
- const relPath = path6.relative(docsDir, featureDir);
631
+ const relPath = path7.relative(docsDir, featureDir);
509
632
  if (!idMap.has(id)) {
510
633
  idMap.set(id, []);
511
634
  }
@@ -575,7 +698,7 @@ async function runStatus(options) {
575
698
  }
576
699
  console.log();
577
700
  if (options.write) {
578
- const outputPath = path6.join(featuresDir, "status.md");
701
+ const outputPath = path7.join(featuresDir, "status.md");
579
702
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
580
703
  const content = [
581
704
  "# Feature Status",
@@ -642,7 +765,7 @@ async function runUpdate(options) {
642
765
  }
643
766
  const { docsDir, projectType, lang } = config;
644
767
  const templatesDir = getTemplatesDir();
645
- const sourceDir = path6.join(templatesDir, lang, projectType);
768
+ const sourceDir = path7.join(templatesDir, lang, projectType);
646
769
  const updateAgents = options.agents || !options.agents && !options.templates;
647
770
  const updateTemplates = options.templates || !options.agents && !options.templates;
648
771
  console.log(chalk.blue("\u{1F4E6} \uD15C\uD50C\uB9BF \uC5C5\uB370\uC774\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4..."));
@@ -652,9 +775,9 @@ async function runUpdate(options) {
652
775
  let updatedCount = 0;
653
776
  if (updateAgents) {
654
777
  console.log(chalk.blue("\u{1F4C1} agents/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
655
- const commonAgents = path6.join(templatesDir, lang, "common", "agents");
656
- const typeAgents = path6.join(templatesDir, lang, projectType, "agents");
657
- const targetAgents = path6.join(docsDir, "agents");
778
+ const commonAgents = path7.join(templatesDir, lang, "common", "agents");
779
+ const typeAgents = path7.join(templatesDir, lang, projectType, "agents");
780
+ const targetAgents = path7.join(docsDir, "agents");
658
781
  const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
659
782
  const replacements = {
660
783
  "{{featurePath}}": featurePath
@@ -676,8 +799,8 @@ async function runUpdate(options) {
676
799
  }
677
800
  if (updateTemplates) {
678
801
  console.log(chalk.blue("\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
679
- const sourceFeatureBase = path6.join(sourceDir, "features", "feature-base");
680
- const targetFeatureBase = path6.join(docsDir, "features", "feature-base");
802
+ const sourceFeatureBase = path7.join(sourceDir, "features", "feature-base");
803
+ const targetFeatureBase = path7.join(docsDir, "features", "feature-base");
681
804
  if (await fs6.pathExists(sourceFeatureBase)) {
682
805
  const count = await updateFolder(
683
806
  sourceFeatureBase,
@@ -696,8 +819,8 @@ async function updateFolder(sourceDir, targetDir, force, replacements) {
696
819
  const files = await fs6.readdir(sourceDir);
697
820
  let updatedCount = 0;
698
821
  for (const file of files) {
699
- const sourcePath = path6.join(sourceDir, file);
700
- const targetPath = path6.join(targetDir, file);
822
+ const sourcePath = path7.join(sourceDir, file);
823
+ const targetPath = path7.join(targetDir, file);
701
824
  const stat = await fs6.stat(sourcePath);
702
825
  if (stat.isFile()) {
703
826
  if (file === "custom.md") {
@@ -739,11 +862,90 @@ async function updateFolder(sourceDir, targetDir, force, replacements) {
739
862
  }
740
863
  return updatedCount;
741
864
  }
865
+ var CACHE_FILE = path7.join(os.homedir(), ".lee-spec-kit-version-cache.json");
866
+ var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
867
+ function getCurrentVersion() {
868
+ try {
869
+ const packageJsonPath = path7.join(__dirname$1, "..", "package.json");
870
+ if (fs6.existsSync(packageJsonPath)) {
871
+ const pkg = fs6.readJsonSync(packageJsonPath);
872
+ return pkg.version;
873
+ }
874
+ } catch {
875
+ }
876
+ return "0.0.0";
877
+ }
878
+ function readCache() {
879
+ try {
880
+ if (fs6.existsSync(CACHE_FILE)) {
881
+ return fs6.readJsonSync(CACHE_FILE);
882
+ }
883
+ } catch {
884
+ }
885
+ return null;
886
+ }
887
+ function isNewerVersion(current, latest) {
888
+ const currentParts = current.split(".").map(Number);
889
+ const latestParts = latest.split(".").map(Number);
890
+ for (let i = 0; i < 3; i++) {
891
+ if ((latestParts[i] || 0) > (currentParts[i] || 0)) return true;
892
+ if ((latestParts[i] || 0) < (currentParts[i] || 0)) return false;
893
+ }
894
+ return false;
895
+ }
896
+ function printUpdateNotice(current, latest) {
897
+ console.log();
898
+ console.log(
899
+ chalk.yellow(`\u{1F4E6} lee-spec-kit v${latest} \uC0AC\uC6A9 \uAC00\uB2A5 (\uD604\uC7AC: v${current})`)
900
+ );
901
+ console.log(chalk.gray(" \uC5C5\uB370\uC774\uD2B8: npm update -g lee-spec-kit"));
902
+ console.log();
903
+ }
904
+ function spawnBackgroundVersionCheck() {
905
+ const script = `
906
+ const fs = require('fs');
907
+ const path = require('path');
908
+ const os = require('os');
909
+
910
+ const CACHE_FILE = path.join(os.homedir(), '.lee-spec-kit-version-cache.json');
911
+
912
+ fetch('https://registry.npmjs.org/lee-spec-kit/latest')
913
+ .then(res => res.json())
914
+ .then(data => {
915
+ const cache = { lastCheck: Date.now(), latestVersion: data.version };
916
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(cache));
917
+ })
918
+ .catch(() => {});
919
+ `;
920
+ const child = spawn("node", ["-e", script], {
921
+ detached: true,
922
+ stdio: "ignore"
923
+ });
924
+ child.unref();
925
+ }
926
+ function checkForUpdates() {
927
+ try {
928
+ const cache = readCache();
929
+ const now = Date.now();
930
+ if (cache && now - cache.lastCheck < CHECK_INTERVAL) {
931
+ if (cache.latestVersion) {
932
+ const currentVersion = getCurrentVersion();
933
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
934
+ printUpdateNotice(currentVersion, cache.latestVersion);
935
+ }
936
+ }
937
+ return;
938
+ }
939
+ spawnBackgroundVersionCheck();
940
+ } catch {
941
+ }
942
+ }
742
943
 
743
944
  // src/index.ts
945
+ checkForUpdates();
744
946
  program.name("lee-spec-kit").description(
745
947
  "Project documentation structure generator for AI-assisted development"
746
- ).version("0.1.0");
948
+ ).version("0.2.0");
747
949
  initCommand(program);
748
950
  featureCommand(program);
749
951
  statusCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -103,6 +103,24 @@ git checkout -b feat/{issue-number}-{feature-name}
103
103
 
104
104
  ---
105
105
 
106
+ ## Docs Push Rules
107
+
108
+ > Refer to the `docsRepo` setting in `.lee-spec-kit.json`.
109
+
110
+ | Setting | Behavior |
111
+ | -------------------------------------------- | ------------------------------- |
112
+ | `docsRepo: "embedded"` | docs included with project push |
113
+ | `docsRepo: "standalone"` + `pushDocs: false` | docs commit only, no push |
114
+ | `docsRepo: "standalone"` + `pushDocs: true` | push docs changes separately |
115
+
116
+ ### Standalone Mode Notes
117
+
118
+ - If `pushDocs: false`, docs changes are **committed locally only**
119
+ - If `pushDocs: true`, **push separately** after docs changes
120
+ - Project repo and docs repo are separate, **manage each independently**
121
+
122
+ ---
123
+
106
124
  ## GitHub Setup Requirements
107
125
 
108
126
  ### Required
@@ -103,6 +103,24 @@ git checkout -b feat/{issue-number}-{feature-name}
103
103
 
104
104
  ---
105
105
 
106
+ ## Docs Push 규칙
107
+
108
+ > `.lee-spec-kit.json`의 `docsRepo` 설정을 참조합니다.
109
+
110
+ | 설정 | 동작 |
111
+ | -------------------------------------------- | --------------------------------- |
112
+ | `docsRepo: "embedded"` | 프로젝트 push 시 docs도 함께 포함 |
113
+ | `docsRepo: "standalone"` + `pushDocs: false` | docs는 커밋만, push 안 함 |
114
+ | `docsRepo: "standalone"` + `pushDocs: true` | docs 변경 시 별도 push 진행 |
115
+
116
+ ### Standalone 모드 주의사항
117
+
118
+ - `pushDocs: false`인 경우 docs 변경사항은 **로컬에만 커밋**
119
+ - `pushDocs: true`인 경우 docs 변경 후 **별도로 push** 필요
120
+ - 프로젝트 레포와 docs 레포가 분리되어 있으므로 **각각 관리**
121
+
122
+ ---
123
+
106
124
  ## GitHub 설정 요구사항
107
125
 
108
126
  ### 필수