specwf 0.2.1 → 0.2.2
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.js +72 -67
- package/dist/templates/commands/init.md +19 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/specwf-init.ts
|
|
7
|
-
import { join as
|
|
7
|
+
import { join as join8 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/core/config.ts
|
|
10
10
|
import { join } from "path";
|
|
@@ -468,66 +468,9 @@ async function runBrownfieldInit(rootDir, specwfDir, info) {
|
|
|
468
468
|
return domains;
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
// src/commands/specwf-init.ts
|
|
472
|
-
function register(program2) {
|
|
473
|
-
program2.command("init").description("\u521D\u59CB\u5316 specwf \u9879\u76EE\u7ED3\u6784").option("--dir <path>", "\u76EE\u6807\u76EE\u5F55", ".").option("--profile <profile>", "\u5DE5\u4F5C\u6D41\u4E25\u683C\u5EA6 (lite|standard|strict)", "standard").option("--brownfield", "\u5B58\u91CF\u9879\u76EE\u6A21\u5F0F\uFF08codebase mapping + spec bootstrap\uFF09").option("--yes", "\u8DF3\u8FC7\u786E\u8BA4\u4F7F\u7528\u9ED8\u8BA4\u503C").action(initHandler);
|
|
474
|
-
}
|
|
475
|
-
async function initHandler(options) {
|
|
476
|
-
const baseDir = options.dir.startsWith("/") ? options.dir : join5(process.cwd(), options.dir);
|
|
477
|
-
const specwfDir = join5(baseDir, "specwf");
|
|
478
|
-
if (isInitialized(specwfDir)) {
|
|
479
|
-
console.error("specwf \u5DF2\u521D\u59CB\u5316\u3002\u8FD0\u884C `specwf update` \u66F4\u65B0\u5E73\u53F0\u6587\u4EF6\u3002");
|
|
480
|
-
process.exit(1);
|
|
481
|
-
}
|
|
482
|
-
const wizard = await runInitWizard({ profile: options.profile, yes: options.yes });
|
|
483
|
-
const profile = wizard.profile;
|
|
484
|
-
const platform = wizard.platform;
|
|
485
|
-
const isBrownfield = options.brownfield || wizard.brownfield;
|
|
486
|
-
createSpecwfStructure(specwfDir);
|
|
487
|
-
console.log("\u2713 \u521B\u5EFA specwf/ \u76EE\u5F55\u7ED3\u6784");
|
|
488
|
-
saveConfig(specwfDir, {
|
|
489
|
-
version: 1,
|
|
490
|
-
platform,
|
|
491
|
-
profile,
|
|
492
|
-
context: wizard.context,
|
|
493
|
-
workflow: {},
|
|
494
|
-
review: {},
|
|
495
|
-
change: {},
|
|
496
|
-
git: { branching: "none", create_tag: true },
|
|
497
|
-
conventions: { inject: true },
|
|
498
|
-
models: {}
|
|
499
|
-
});
|
|
500
|
-
console.log("\u2713 \u521B\u5EFA project.yml (profile: " + profile + ")");
|
|
501
|
-
saveState(specwfDir, {
|
|
502
|
-
project: {
|
|
503
|
-
name: baseDir.split("/").pop() || "project",
|
|
504
|
-
status: "initialized",
|
|
505
|
-
current_milestone: null,
|
|
506
|
-
current_phase: null
|
|
507
|
-
},
|
|
508
|
-
active_context: {
|
|
509
|
-
type: "project",
|
|
510
|
-
ref: null,
|
|
511
|
-
step: "init"
|
|
512
|
-
},
|
|
513
|
-
changes: [],
|
|
514
|
-
adhoc: []
|
|
515
|
-
});
|
|
516
|
-
console.log("\u2713 \u521B\u5EFA state.md");
|
|
517
|
-
if (isBrownfield) {
|
|
518
|
-
const info = detectProjectInfo(process.cwd());
|
|
519
|
-
const domains = await runBrownfieldInit(process.cwd(), specwfDir, info);
|
|
520
|
-
console.log("\u2713 \u5B58\u91CF\u9879\u76EE codebase mapping \u5B8C\u6210 (" + domains.length + " \u4E2A spec \u57DF)");
|
|
521
|
-
}
|
|
522
|
-
console.log("specwf \u521D\u59CB\u5316\u5B8C\u6210\u3002\u8FD0\u884C `specwf update` \u751F\u6210\u5E73\u53F0\u6587\u4EF6\u3002");
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// src/commands/specwf-update.ts
|
|
526
|
-
import { join as join9 } from "path";
|
|
527
|
-
|
|
528
471
|
// src/generators/omp-commands.ts
|
|
529
472
|
import { readFileSync as readFileSync6 } from "fs";
|
|
530
|
-
import { join as
|
|
473
|
+
import { join as join5, dirname } from "path";
|
|
531
474
|
import { fileURLToPath } from "url";
|
|
532
475
|
var STEP_DEFS = [
|
|
533
476
|
{ step: "init", name: "specwf:init", description: "\u521D\u59CB\u5316 specwf \u9879\u76EE\u7ED3\u6784", usesAgent: true, agents: ["researcher"] },
|
|
@@ -548,9 +491,9 @@ var STEP_DEFS = [
|
|
|
548
491
|
{ step: "continue", name: "specwf:continue", description: "\u81EA\u52A8\u63A8\u8FDB \u2014 \u8BFB STATE \u786E\u5B9A\u4E0B\u4E00\u6B65", usesAgent: false, agents: [] }
|
|
549
492
|
];
|
|
550
493
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
551
|
-
var TEMPLATES_DIR =
|
|
494
|
+
var TEMPLATES_DIR = join5(__dirname, "templates", "commands");
|
|
552
495
|
function loadTemplate(step) {
|
|
553
|
-
return readFileSync6(
|
|
496
|
+
return readFileSync6(join5(TEMPLATES_DIR, `${step}.md`), "utf-8");
|
|
554
497
|
}
|
|
555
498
|
function renderTemplate(template, vars) {
|
|
556
499
|
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
|
|
@@ -634,12 +577,12 @@ function generateAllCommands(config) {
|
|
|
634
577
|
|
|
635
578
|
// src/generators/omp-agents.ts
|
|
636
579
|
import { readFileSync as readFileSync7 } from "fs";
|
|
637
|
-
import { join as
|
|
580
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
638
581
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
639
582
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
640
|
-
var TEMPLATES_DIR2 =
|
|
583
|
+
var TEMPLATES_DIR2 = join6(__dirname2, "templates", "agents");
|
|
641
584
|
function loadTemplate2(role) {
|
|
642
|
-
return readFileSync7(
|
|
585
|
+
return readFileSync7(join6(TEMPLATES_DIR2, `${role}.md`), "utf-8");
|
|
643
586
|
}
|
|
644
587
|
function renderTemplate2(template, vars) {
|
|
645
588
|
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
|
|
@@ -750,12 +693,12 @@ function generateAllAgents(config) {
|
|
|
750
693
|
|
|
751
694
|
// src/generators/skills.ts
|
|
752
695
|
import { readFileSync as readFileSync8 } from "fs";
|
|
753
|
-
import { join as
|
|
696
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
754
697
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
755
698
|
var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
756
|
-
var TEMPLATES_DIR3 =
|
|
699
|
+
var TEMPLATES_DIR3 = join7(__dirname3, "templates", "skills");
|
|
757
700
|
function loadTemplate3(step) {
|
|
758
|
-
return readFileSync8(
|
|
701
|
+
return readFileSync8(join7(TEMPLATES_DIR3, `${step}.md`), "utf-8");
|
|
759
702
|
}
|
|
760
703
|
function renderTemplate3(template, vars) {
|
|
761
704
|
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
|
|
@@ -828,7 +771,69 @@ function writeGeneratedFiles(files) {
|
|
|
828
771
|
}
|
|
829
772
|
}
|
|
830
773
|
|
|
774
|
+
// src/commands/specwf-init.ts
|
|
775
|
+
function register(program2) {
|
|
776
|
+
program2.command("init").description("\u521D\u59CB\u5316 specwf \u9879\u76EE\u7ED3\u6784").option("--dir <path>", "\u76EE\u6807\u76EE\u5F55", ".").option("--profile <profile>", "\u5DE5\u4F5C\u6D41\u4E25\u683C\u5EA6 (lite|standard|strict)", "standard").option("--brownfield", "\u5B58\u91CF\u9879\u76EE\u6A21\u5F0F\uFF08codebase mapping + spec bootstrap\uFF09").option("--yes", "\u8DF3\u8FC7\u786E\u8BA4\u4F7F\u7528\u9ED8\u8BA4\u503C").action(initHandler);
|
|
777
|
+
}
|
|
778
|
+
async function initHandler(options) {
|
|
779
|
+
const baseDir = options.dir.startsWith("/") ? options.dir : join8(process.cwd(), options.dir);
|
|
780
|
+
const specwfDir = join8(baseDir, "specwf");
|
|
781
|
+
if (isInitialized(specwfDir)) {
|
|
782
|
+
console.error("specwf \u5DF2\u521D\u59CB\u5316\u3002\u8FD0\u884C `specwf update` \u66F4\u65B0\u5E73\u53F0\u6587\u4EF6\u3002");
|
|
783
|
+
process.exit(1);
|
|
784
|
+
}
|
|
785
|
+
const wizard = await runInitWizard({ profile: options.profile, yes: options.yes });
|
|
786
|
+
const profile = wizard.profile;
|
|
787
|
+
const platform = wizard.platform;
|
|
788
|
+
const isBrownfield = options.brownfield || wizard.brownfield;
|
|
789
|
+
createSpecwfStructure(specwfDir);
|
|
790
|
+
console.log("\u2713 \u521B\u5EFA specwf/ \u76EE\u5F55\u7ED3\u6784");
|
|
791
|
+
saveConfig(specwfDir, {
|
|
792
|
+
version: 1,
|
|
793
|
+
platform,
|
|
794
|
+
profile,
|
|
795
|
+
context: wizard.context,
|
|
796
|
+
workflow: {},
|
|
797
|
+
review: {},
|
|
798
|
+
change: {},
|
|
799
|
+
git: { branching: "none", create_tag: true },
|
|
800
|
+
conventions: { inject: true },
|
|
801
|
+
models: {}
|
|
802
|
+
});
|
|
803
|
+
console.log("\u2713 \u521B\u5EFA project.yml (profile: " + profile + ")");
|
|
804
|
+
saveState(specwfDir, {
|
|
805
|
+
project: {
|
|
806
|
+
name: baseDir.split("/").pop() || "project",
|
|
807
|
+
status: "initialized",
|
|
808
|
+
current_milestone: null,
|
|
809
|
+
current_phase: null
|
|
810
|
+
},
|
|
811
|
+
active_context: {
|
|
812
|
+
type: "project",
|
|
813
|
+
ref: null,
|
|
814
|
+
step: "init"
|
|
815
|
+
},
|
|
816
|
+
changes: [],
|
|
817
|
+
adhoc: []
|
|
818
|
+
});
|
|
819
|
+
console.log("\u2713 \u521B\u5EFA state.md");
|
|
820
|
+
if (isBrownfield) {
|
|
821
|
+
const info = detectProjectInfo(process.cwd());
|
|
822
|
+
const domains = await runBrownfieldInit(process.cwd(), specwfDir, info);
|
|
823
|
+
console.log("\u2713 \u5B58\u91CF\u9879\u76EE codebase mapping \u5B8C\u6210 (" + domains.length + " \u4E2A spec \u57DF)");
|
|
824
|
+
}
|
|
825
|
+
console.log("specwf \u521D\u59CB\u5316\u5B8C\u6210\u3002");
|
|
826
|
+
try {
|
|
827
|
+
const files = generateAll({ version: 1, platform, profile, context: wizard.context, workflow: {}, review: {}, change: {}, git: { branching: "none", create_tag: true }, conventions: { inject: true }, models: {} });
|
|
828
|
+
writeGeneratedFiles(files);
|
|
829
|
+
console.log(`\u2713 \u5E73\u53F0\u6587\u4EF6\u5DF2\u751F\u6210 (${files.length} \u4E2A)`);
|
|
830
|
+
} catch {
|
|
831
|
+
console.log("\u26A0 \u5E73\u53F0\u6587\u4EF6\u751F\u6210\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u8FD0\u884C `specwf update` \u91CD\u8BD5");
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
831
835
|
// src/commands/specwf-update.ts
|
|
836
|
+
import { join as join9 } from "path";
|
|
832
837
|
function register2(program2) {
|
|
833
838
|
program2.command("update").description("\u66F4\u65B0\u5E73\u53F0\u6587\u4EF6\uFF08commands + agents\uFF09").option("--dir <path>", "specwf \u76EE\u5F55", "specwf").action(updateHandler);
|
|
834
839
|
}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
# 初始化项目
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
运行 `specwf init` 创建 specwf/ 目录结构 + platform.yml + state.md。
|
|
4
4
|
|
|
5
5
|
## 存量项目
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
已有代码的项目使用 brownfield 模式:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
specwf init --brownfield
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
CLI 创建目录结构后,**agent 需使用 specwf 子代理并行执行以下任务**:
|
|
14
|
+
|
|
15
|
+
1. **Codebase Mapping**(`specwf-researcher`)— 分析技术栈、模块结构、依赖关系
|
|
16
|
+
- 产出: `specwf/research/STACK.md` + `specwf/research/ARCHITECTURE.md`
|
|
17
|
+
2. **Spec Bootstrap**(`specwf-researcher`)— 从核心模块提取行为契约
|
|
18
|
+
- 产出: `specwf/specs/<domain>/spec.md`(标记 BOOTSTRAPPED)
|
|
19
|
+
|
|
20
|
+
使用 task 工具 fan-out 两个 specwf-researcher 并行执行。
|
|
8
21
|
|
|
9
22
|
## 上下文
|
|
10
23
|
|
|
@@ -13,8 +26,12 @@ specwf context init
|
|
|
13
26
|
specwf state
|
|
14
27
|
```
|
|
15
28
|
|
|
29
|
+
可参考 @specwf/conventions/coding.md。
|
|
30
|
+
|
|
16
31
|
## 下一步
|
|
17
32
|
|
|
33
|
+
完成后:
|
|
34
|
+
|
|
18
35
|
```bash
|
|
19
36
|
specwf continue
|
|
20
37
|
```
|