@pixel613/spec 1.0.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/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/bin/pxs.d.ts +2 -0
- package/dist/bin/pxs.js +5 -0
- package/dist/bin/pxs.js.map +1 -0
- package/dist/src/backends/claude.d.ts +9 -0
- package/dist/src/backends/claude.js +80 -0
- package/dist/src/backends/claude.js.map +1 -0
- package/dist/src/backends/codex.d.ts +9 -0
- package/dist/src/backends/codex.js +72 -0
- package/dist/src/backends/codex.js.map +1 -0
- package/dist/src/backends/factory.d.ts +2 -0
- package/dist/src/backends/factory.js +14 -0
- package/dist/src/backends/factory.js.map +1 -0
- package/dist/src/backends/interface.d.ts +15 -0
- package/dist/src/backends/interface.js +2 -0
- package/dist/src/backends/interface.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +79 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/clarify.d.ts +5 -0
- package/dist/src/commands/clarify.js +46 -0
- package/dist/src/commands/clarify.js.map +1 -0
- package/dist/src/commands/implement.d.ts +9 -0
- package/dist/src/commands/implement.js +244 -0
- package/dist/src/commands/implement.js.map +1 -0
- package/dist/src/commands/init.d.ts +6 -0
- package/dist/src/commands/init.js +177 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/new.d.ts +5 -0
- package/dist/src/commands/new.js +143 -0
- package/dist/src/commands/new.js.map +1 -0
- package/dist/src/commands/refine.d.ts +8 -0
- package/dist/src/commands/refine.js +158 -0
- package/dist/src/commands/refine.js.map +1 -0
- package/dist/src/commands/review.d.ts +4 -0
- package/dist/src/commands/review.js +70 -0
- package/dist/src/commands/review.js.map +1 -0
- package/dist/src/commands/status.d.ts +1 -0
- package/dist/src/commands/status.js +53 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/discovery/project.d.ts +7 -0
- package/dist/src/discovery/project.js +90 -0
- package/dist/src/discovery/project.js.map +1 -0
- package/dist/src/git/operations.d.ts +10 -0
- package/dist/src/git/operations.js +56 -0
- package/dist/src/git/operations.js.map +1 -0
- package/dist/src/parsers/arguments.d.ts +18 -0
- package/dist/src/parsers/arguments.js +43 -0
- package/dist/src/parsers/arguments.js.map +1 -0
- package/dist/src/parsers/plan.d.ts +23 -0
- package/dist/src/parsers/plan.js +117 -0
- package/dist/src/parsers/plan.js.map +1 -0
- package/dist/src/parsers/spec.d.ts +10 -0
- package/dist/src/parsers/spec.js +46 -0
- package/dist/src/parsers/spec.js.map +1 -0
- package/dist/src/state/manager.d.ts +24 -0
- package/dist/src/state/manager.js +103 -0
- package/dist/src/state/manager.js.map +1 -0
- package/dist/src/state/types.d.ts +47 -0
- package/dist/src/state/types.js +21 -0
- package/dist/src/state/types.js.map +1 -0
- package/dist/src/utils/display.d.ts +7 -0
- package/dist/src/utils/display.js +42 -0
- package/dist/src/utils/display.js.map +1 -0
- package/dist/src/utils/prompt.d.ts +31 -0
- package/dist/src/utils/prompt.js +81 -0
- package/dist/src/utils/prompt.js.map +1 -0
- package/package.json +52 -0
- package/templates/agents-md-snippet.md +20 -0
- package/templates/architectures/clean/csharp-aspnet.md +56 -0
- package/templates/architectures/clean/go-gin.md +50 -0
- package/templates/architectures/clean/go-std.md +49 -0
- package/templates/architectures/clean/python-fastapi.md +49 -0
- package/templates/architectures/clean/typescript-express.md +60 -0
- package/templates/architectures/clean/typescript-nestjs.md +61 -0
- package/templates/architectures/ddd/csharp-aspnet.md +55 -0
- package/templates/architectures/ddd/go-gin.md +53 -0
- package/templates/architectures/ddd/python-fastapi.md +52 -0
- package/templates/architectures/ddd/typescript-nestjs.md +62 -0
- package/templates/architectures/hexagonal/csharp-aspnet.md +45 -0
- package/templates/architectures/hexagonal/go-gin.md +47 -0
- package/templates/architectures/hexagonal/python-fastapi.md +43 -0
- package/templates/architectures/hexagonal/typescript-nestjs.md +44 -0
- package/templates/architectures/layered/csharp-aspnet.md +45 -0
- package/templates/architectures/layered/go-gin.md +41 -0
- package/templates/architectures/layered/python-fastapi.md +42 -0
- package/templates/architectures/layered/typescript-nestjs.md +48 -0
- package/templates/architectures/modular/csharp-aspnet.md +45 -0
- package/templates/architectures/modular/go-gin.md +47 -0
- package/templates/architectures/modular/python-fastapi.md +45 -0
- package/templates/architectures/modular/typescript-nestjs.md +48 -0
- package/templates/claude-commands/sf.clarify.md +18 -0
- package/templates/claude-commands/sf.implement.md +79 -0
- package/templates/claude-commands/sf.new.md +22 -0
- package/templates/claude-commands/sf.refine.md +47 -0
- package/templates/claude-commands/sf.review.md +17 -0
- package/templates/claude-commands/sf.status.md +12 -0
- package/templates/workflow/config.yaml +17 -0
- package/templates/workflow/prompts/clarify.md +39 -0
- package/templates/workflow/prompts/final-review.md +30 -0
- package/templates/workflow/prompts/implement-tdd.md +35 -0
- package/templates/workflow/prompts/implement.md +43 -0
- package/templates/workflow/prompts/refine.md +52 -0
- package/templates/workflow/prompts/review.md +34 -0
- package/templates/workflow/prompts/test.md +14 -0
- package/templates/workflow/templates/spec-template.md +13 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a plan markdown file into structured tasks.
|
|
3
|
+
*/
|
|
4
|
+
export function parsePlan(content) {
|
|
5
|
+
const result = {
|
|
6
|
+
name: '',
|
|
7
|
+
type: 'feat',
|
|
8
|
+
branch: '',
|
|
9
|
+
totalTasks: 0,
|
|
10
|
+
tasks: [],
|
|
11
|
+
raw: content,
|
|
12
|
+
};
|
|
13
|
+
const lines = content.split('\n');
|
|
14
|
+
// Extract metadata from blockquotes at top
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const typeMatch = line.match(/^>\s*type:\s*(.+)/);
|
|
17
|
+
if (typeMatch) {
|
|
18
|
+
result.type = typeMatch[1].trim();
|
|
19
|
+
}
|
|
20
|
+
const branchMatch = line.match(/^>\s*branch:\s*(.+)/);
|
|
21
|
+
if (branchMatch) {
|
|
22
|
+
result.branch = branchMatch[1].trim();
|
|
23
|
+
}
|
|
24
|
+
const totalMatch = line.match(/^>\s*total_tasks:\s*(\d+)/);
|
|
25
|
+
if (totalMatch) {
|
|
26
|
+
result.totalTasks = parseInt(totalMatch[1], 10);
|
|
27
|
+
}
|
|
28
|
+
const titleMatch = line.match(/^#\s+Implementation Plan:\s*(.+)/);
|
|
29
|
+
if (titleMatch) {
|
|
30
|
+
result.name = titleMatch[1].trim();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Split into task sections by H2
|
|
34
|
+
const taskRegex = /^##\s+Task\s+(\d+):\s*(.+)/;
|
|
35
|
+
let currentTask = null;
|
|
36
|
+
const taskLines = [];
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const taskMatch = line.match(taskRegex);
|
|
39
|
+
if (taskMatch) {
|
|
40
|
+
if (currentTask) {
|
|
41
|
+
currentTask.raw = taskLines.join('\n').trim();
|
|
42
|
+
parseTaskFields(currentTask, taskLines);
|
|
43
|
+
result.tasks.push(currentTask);
|
|
44
|
+
}
|
|
45
|
+
currentTask = {
|
|
46
|
+
number: parseInt(taskMatch[1], 10),
|
|
47
|
+
title: taskMatch[2].trim(),
|
|
48
|
+
files: [],
|
|
49
|
+
description: '',
|
|
50
|
+
dependsOn: 'None',
|
|
51
|
+
complexity: 'Medium',
|
|
52
|
+
acceptance: '',
|
|
53
|
+
raw: '',
|
|
54
|
+
};
|
|
55
|
+
taskLines.length = 0;
|
|
56
|
+
}
|
|
57
|
+
else if (currentTask) {
|
|
58
|
+
taskLines.push(line);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Flush last task
|
|
62
|
+
if (currentTask) {
|
|
63
|
+
currentTask.raw = taskLines.join('\n').trim();
|
|
64
|
+
parseTaskFields(currentTask, taskLines);
|
|
65
|
+
result.tasks.push(currentTask);
|
|
66
|
+
}
|
|
67
|
+
if (result.totalTasks === 0) {
|
|
68
|
+
result.totalTasks = result.tasks.length;
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
function parseTaskFields(task, lines) {
|
|
73
|
+
let currentField = '';
|
|
74
|
+
const fieldLines = [];
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
const fieldMatch = line.match(/^-\s+\*\*(\w[\w\s]*)\*\*:\s*(.*)/);
|
|
77
|
+
if (fieldMatch) {
|
|
78
|
+
flushField(task, currentField, fieldLines);
|
|
79
|
+
currentField = fieldMatch[1].trim();
|
|
80
|
+
fieldLines.length = 0;
|
|
81
|
+
const value = fieldMatch[2].trim();
|
|
82
|
+
if (value)
|
|
83
|
+
fieldLines.push(value);
|
|
84
|
+
}
|
|
85
|
+
else if (currentField && line.match(/^\s+-\s+/)) {
|
|
86
|
+
// Sub-list items (like file paths)
|
|
87
|
+
fieldLines.push(line.replace(/^\s+-\s+/, '').trim());
|
|
88
|
+
}
|
|
89
|
+
else if (currentField && line.trim()) {
|
|
90
|
+
fieldLines.push(line.trim());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
flushField(task, currentField, fieldLines);
|
|
94
|
+
}
|
|
95
|
+
function flushField(task, field, lines) {
|
|
96
|
+
if (!field)
|
|
97
|
+
return;
|
|
98
|
+
const value = lines.join('\n').trim();
|
|
99
|
+
switch (field.toLowerCase()) {
|
|
100
|
+
case 'files':
|
|
101
|
+
task.files = lines.map((l) => l.replace(/`/g, '').replace(/\s*\(.*\)$/, '').trim());
|
|
102
|
+
break;
|
|
103
|
+
case 'description':
|
|
104
|
+
task.description = value;
|
|
105
|
+
break;
|
|
106
|
+
case 'depends on':
|
|
107
|
+
task.dependsOn = value || 'None';
|
|
108
|
+
break;
|
|
109
|
+
case 'complexity':
|
|
110
|
+
task.complexity = value;
|
|
111
|
+
break;
|
|
112
|
+
case 'acceptance':
|
|
113
|
+
task.acceptance = value;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/parsers/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a spec markdown file into structured sections.
|
|
3
|
+
*/
|
|
4
|
+
export function parseSpec(content) {
|
|
5
|
+
const lines = content.split('\n');
|
|
6
|
+
const result = {
|
|
7
|
+
title: '',
|
|
8
|
+
sections: {},
|
|
9
|
+
raw: content,
|
|
10
|
+
};
|
|
11
|
+
let currentSection = '';
|
|
12
|
+
const sectionLines = [];
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
// Extract title from first H1
|
|
15
|
+
const h1Match = line.match(/^#\s+(?:Feature:\s*)?(.+)/);
|
|
16
|
+
if (h1Match && !result.title) {
|
|
17
|
+
result.title = h1Match[1].trim();
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
// Extract source from blockquote
|
|
21
|
+
const sourceMatch = line.match(/^>\s*source:\s*(.+)/);
|
|
22
|
+
if (sourceMatch) {
|
|
23
|
+
result.source = sourceMatch[1].trim();
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Detect H2 sections
|
|
27
|
+
const h2Match = line.match(/^##\s+(.+)/);
|
|
28
|
+
if (h2Match) {
|
|
29
|
+
if (currentSection && sectionLines.length > 0) {
|
|
30
|
+
result.sections[currentSection] = sectionLines.join('\n').trim();
|
|
31
|
+
}
|
|
32
|
+
currentSection = h2Match[1].trim();
|
|
33
|
+
sectionLines.length = 0;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (currentSection) {
|
|
37
|
+
sectionLines.push(line);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Flush last section
|
|
41
|
+
if (currentSection && sectionLines.length > 0) {
|
|
42
|
+
result.sections[currentSection] = sectionLines.join('\n').trim();
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/parsers/spec.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAAe;QACzB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,GAAG,EAAE,OAAO;KACb,CAAC;IAEF,IAAI,cAAc,GAAG,EAAE,CAAC;IACxB,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,8BAA8B;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACxD,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACjC,SAAS;QACX,CAAC;QAED,iCAAiC;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QAED,qBAAqB;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACzC,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,cAAc,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9C,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YACnE,CAAC;YACD,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACnC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,IAAI,cAAc,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACnE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type WorkflowState, type FeatureState, type ProjectConfig } from './types.js';
|
|
2
|
+
export declare class StateManager {
|
|
3
|
+
private cwd;
|
|
4
|
+
private workflowDir;
|
|
5
|
+
constructor(cwd?: string);
|
|
6
|
+
get statePath(): string;
|
|
7
|
+
get configPath(): string;
|
|
8
|
+
workflowExists(): boolean;
|
|
9
|
+
ensureWorkflow(): void;
|
|
10
|
+
readState(): WorkflowState;
|
|
11
|
+
writeState(state: WorkflowState): void;
|
|
12
|
+
getFeature(name: string): FeatureState | undefined;
|
|
13
|
+
upsertFeature(feature: FeatureState): void;
|
|
14
|
+
checkPhaseGuard(command: string, featureName: string): void;
|
|
15
|
+
readConfig(): ProjectConfig;
|
|
16
|
+
specsDir(): string;
|
|
17
|
+
plansDir(): string;
|
|
18
|
+
reviewsDir(): string;
|
|
19
|
+
promptsDir(): string;
|
|
20
|
+
templatesDir(): string;
|
|
21
|
+
specPath(name: string): string;
|
|
22
|
+
planPath(name: string): string;
|
|
23
|
+
reviewPath(name: string, taskN: number): string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { PHASE_GUARDS, } from './types.js';
|
|
5
|
+
const WORKFLOW_DIR = '.workflow';
|
|
6
|
+
const STATE_FILE = 'state.yaml';
|
|
7
|
+
const CONFIG_FILE = 'config.yaml';
|
|
8
|
+
export class StateManager {
|
|
9
|
+
cwd;
|
|
10
|
+
workflowDir;
|
|
11
|
+
constructor(cwd = process.cwd()) {
|
|
12
|
+
this.cwd = cwd;
|
|
13
|
+
this.workflowDir = path.join(cwd, WORKFLOW_DIR);
|
|
14
|
+
}
|
|
15
|
+
get statePath() {
|
|
16
|
+
return path.join(this.workflowDir, STATE_FILE);
|
|
17
|
+
}
|
|
18
|
+
get configPath() {
|
|
19
|
+
return path.join(this.workflowDir, CONFIG_FILE);
|
|
20
|
+
}
|
|
21
|
+
workflowExists() {
|
|
22
|
+
return fs.existsSync(this.workflowDir);
|
|
23
|
+
}
|
|
24
|
+
ensureWorkflow() {
|
|
25
|
+
if (!this.workflowExists()) {
|
|
26
|
+
throw new Error('Workflow not initialized. Run `pxs init` first.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
readState() {
|
|
30
|
+
if (!fs.existsSync(this.statePath)) {
|
|
31
|
+
return { features: [] };
|
|
32
|
+
}
|
|
33
|
+
const content = fs.readFileSync(this.statePath, 'utf-8');
|
|
34
|
+
const data = yaml.load(content);
|
|
35
|
+
return data ?? { features: [] };
|
|
36
|
+
}
|
|
37
|
+
writeState(state) {
|
|
38
|
+
this.ensureWorkflow();
|
|
39
|
+
const content = yaml.dump(state, { lineWidth: -1, noRefs: true });
|
|
40
|
+
fs.writeFileSync(this.statePath, content, 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
getFeature(name) {
|
|
43
|
+
const state = this.readState();
|
|
44
|
+
return state.features.find((f) => f.feature === name);
|
|
45
|
+
}
|
|
46
|
+
upsertFeature(feature) {
|
|
47
|
+
const state = this.readState();
|
|
48
|
+
const idx = state.features.findIndex((f) => f.feature === feature.feature);
|
|
49
|
+
if (idx >= 0) {
|
|
50
|
+
state.features[idx] = feature;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
state.features.push(feature);
|
|
54
|
+
}
|
|
55
|
+
this.writeState(state);
|
|
56
|
+
}
|
|
57
|
+
checkPhaseGuard(command, featureName) {
|
|
58
|
+
const allowedPhases = PHASE_GUARDS[command];
|
|
59
|
+
if (!allowedPhases || allowedPhases.length === 0)
|
|
60
|
+
return; // any phase allowed
|
|
61
|
+
const feature = this.getFeature(featureName);
|
|
62
|
+
if (!feature) {
|
|
63
|
+
throw new Error(`Feature "${featureName}" not found in state. Run \`pxs new ${featureName}\` first.`);
|
|
64
|
+
}
|
|
65
|
+
if (!allowedPhases.includes(feature.phase)) {
|
|
66
|
+
throw new Error(`Command "pxs ${command}" cannot run in phase "${feature.phase}". ` +
|
|
67
|
+
`Allowed phases: ${allowedPhases.join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
readConfig() {
|
|
71
|
+
if (!fs.existsSync(this.configPath)) {
|
|
72
|
+
throw new Error('Config not found. Run `pxs init` first.');
|
|
73
|
+
}
|
|
74
|
+
const content = fs.readFileSync(this.configPath, 'utf-8');
|
|
75
|
+
return yaml.load(content);
|
|
76
|
+
}
|
|
77
|
+
// Helper paths
|
|
78
|
+
specsDir() {
|
|
79
|
+
return path.join(this.workflowDir, 'specs');
|
|
80
|
+
}
|
|
81
|
+
plansDir() {
|
|
82
|
+
return path.join(this.workflowDir, 'plans');
|
|
83
|
+
}
|
|
84
|
+
reviewsDir() {
|
|
85
|
+
return path.join(this.workflowDir, 'reviews');
|
|
86
|
+
}
|
|
87
|
+
promptsDir() {
|
|
88
|
+
return path.join(this.workflowDir, 'prompts');
|
|
89
|
+
}
|
|
90
|
+
templatesDir() {
|
|
91
|
+
return path.join(this.workflowDir, 'templates');
|
|
92
|
+
}
|
|
93
|
+
specPath(name) {
|
|
94
|
+
return path.join(this.specsDir(), `${name}.md`);
|
|
95
|
+
}
|
|
96
|
+
planPath(name) {
|
|
97
|
+
return path.join(this.plansDir(), `${name}.md`);
|
|
98
|
+
}
|
|
99
|
+
reviewPath(name, taskN) {
|
|
100
|
+
return path.join(this.reviewsDir(), `${name}-task-${taskN}.md`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/state/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type Phase = 'spec_created' | 'clarifying' | 'spec_approved' | 'plan_pending_approval' | 'ready_to_implement' | 'implementing' | 'completed' | 'merged';
|
|
2
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'review_pending' | 'complete' | 'skipped';
|
|
3
|
+
export type FeatureType = 'feat' | 'fix' | 'refactor' | 'docs' | 'chore';
|
|
4
|
+
export type TestStrategy = 'tdd' | 'after' | 'none';
|
|
5
|
+
export type TestType = 'unit' | 'intg' | 'both';
|
|
6
|
+
export interface TaskState {
|
|
7
|
+
name: string;
|
|
8
|
+
status: TaskStatus;
|
|
9
|
+
}
|
|
10
|
+
export interface SessionInfo {
|
|
11
|
+
backend: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
export interface FeatureState {
|
|
15
|
+
feature: string;
|
|
16
|
+
type: FeatureType;
|
|
17
|
+
branch: string;
|
|
18
|
+
phase: Phase;
|
|
19
|
+
total_tasks: number;
|
|
20
|
+
current_task: number;
|
|
21
|
+
session?: SessionInfo;
|
|
22
|
+
tasks: TaskState[];
|
|
23
|
+
}
|
|
24
|
+
export interface WorkflowState {
|
|
25
|
+
features: FeatureState[];
|
|
26
|
+
}
|
|
27
|
+
export interface ProjectConfig {
|
|
28
|
+
project: {
|
|
29
|
+
name: string;
|
|
30
|
+
language: string;
|
|
31
|
+
framework: string;
|
|
32
|
+
architecture: string;
|
|
33
|
+
lang_framework: string;
|
|
34
|
+
};
|
|
35
|
+
git: {
|
|
36
|
+
convention: string;
|
|
37
|
+
};
|
|
38
|
+
backend: {
|
|
39
|
+
default: string;
|
|
40
|
+
};
|
|
41
|
+
test: {
|
|
42
|
+
strategy: TestStrategy;
|
|
43
|
+
type: TestType;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export declare const PHASE_GUARDS: Record<string, Phase[]>;
|
|
47
|
+
export declare const TASK_TRANSITIONS: Record<TaskStatus, TaskStatus[]>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Phase guard: which phases each command is allowed in
|
|
2
|
+
export const PHASE_GUARDS = {
|
|
3
|
+
new: [], // any phase (creates new feature)
|
|
4
|
+
refine: ['spec_created', 'clarifying'],
|
|
5
|
+
clarify: ['spec_created', 'spec_approved', 'plan_pending_approval'],
|
|
6
|
+
implement: ['ready_to_implement', 'implementing'],
|
|
7
|
+
review: ['implementing', 'completed'],
|
|
8
|
+
status: [], // any phase
|
|
9
|
+
mcp: [], // any phase
|
|
10
|
+
agent: [], // any phase
|
|
11
|
+
skill: [], // any phase
|
|
12
|
+
};
|
|
13
|
+
// Valid task status transitions
|
|
14
|
+
export const TASK_TRANSITIONS = {
|
|
15
|
+
pending: ['in_progress', 'skipped'],
|
|
16
|
+
in_progress: ['review_pending'],
|
|
17
|
+
review_pending: ['complete', 'in_progress'], // in_progress = request-change
|
|
18
|
+
complete: [],
|
|
19
|
+
skipped: [],
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/state/types.ts"],"names":[],"mappings":"AA8DA,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAA4B;IACnD,GAAG,EAAE,EAAE,EAAE,kCAAkC;IAC3C,MAAM,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC;IACtC,OAAO,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,uBAAuB,CAAC;IACnE,SAAS,EAAE,CAAC,oBAAoB,EAAE,cAAc,CAAC;IACjD,MAAM,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,MAAM,EAAE,EAAE,EAAE,YAAY;IACxB,GAAG,EAAE,EAAE,EAAE,YAAY;IACrB,KAAK,EAAE,EAAE,EAAE,YAAY;IACvB,KAAK,EAAE,EAAE,EAAE,YAAY;CACxB,CAAC;AAEF,gCAAgC;AAChC,MAAM,CAAC,MAAM,gBAAgB,GAAqC;IAChE,OAAO,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;IACnC,WAAW,EAAE,CAAC,gBAAgB,CAAC;IAC/B,cAAc,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,+BAA+B;IAC5E,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE;CACZ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function heading(text: string): void;
|
|
2
|
+
export declare function success(text: string): void;
|
|
3
|
+
export declare function error(text: string): void;
|
|
4
|
+
export declare function warn(text: string): void;
|
|
5
|
+
export declare function info(text: string): void;
|
|
6
|
+
export declare function taskIcon(status: string): string;
|
|
7
|
+
export declare function table(headers: string[], rows: string[][]): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export function heading(text) {
|
|
3
|
+
console.log(chalk.bold.cyan(`\n━━━ ${text} ━━━\n`));
|
|
4
|
+
}
|
|
5
|
+
export function success(text) {
|
|
6
|
+
console.log(chalk.green(` ✓ ${text}`));
|
|
7
|
+
}
|
|
8
|
+
export function error(text) {
|
|
9
|
+
console.log(chalk.red(` ✗ ${text}`));
|
|
10
|
+
}
|
|
11
|
+
export function warn(text) {
|
|
12
|
+
console.log(chalk.yellow(` ⚠ ${text}`));
|
|
13
|
+
}
|
|
14
|
+
export function info(text) {
|
|
15
|
+
console.log(chalk.gray(` ℹ ${text}`));
|
|
16
|
+
}
|
|
17
|
+
export function taskIcon(status) {
|
|
18
|
+
switch (status) {
|
|
19
|
+
case 'complete': return '✅';
|
|
20
|
+
case 'in_progress': return '⏳';
|
|
21
|
+
case 'skipped': return '⏭️';
|
|
22
|
+
case 'review_pending': return '🔍';
|
|
23
|
+
case 'pending':
|
|
24
|
+
default: return '⬜';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function table(headers, rows) {
|
|
28
|
+
// Calculate column widths
|
|
29
|
+
const widths = headers.map((h, i) => {
|
|
30
|
+
const colValues = [h, ...rows.map((r) => r[i] ?? '')];
|
|
31
|
+
return Math.max(...colValues.map((v) => v.length));
|
|
32
|
+
});
|
|
33
|
+
// Print header
|
|
34
|
+
const headerLine = headers.map((h, i) => h.padEnd(widths[i])).join(' ');
|
|
35
|
+
console.log(chalk.bold(headerLine));
|
|
36
|
+
// Print rows
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
const line = row.map((cell, i) => (cell ?? '').padEnd(widths[i])).join(' ');
|
|
39
|
+
console.log(line);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.js","sourceRoot":"","sources":["../../../src/utils/display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,UAAU,CAAC,CAAC,OAAO,GAAG,CAAC;QAC5B,KAAK,aAAa,CAAC,CAAC,OAAO,GAAG,CAAC;QAC/B,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC;QAC5B,KAAK,gBAAgB,CAAC,CAAC,OAAO,IAAI,CAAC;QACnC,KAAK,SAAS,CAAC;QACf,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAiB,EAAE,IAAgB;IACvD,0BAA0B;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpC,aAAa;IACb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a prompt template from .workflow/prompts/<name>.md
|
|
3
|
+
*/
|
|
4
|
+
export declare function loadPrompt(name: string, cwd?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Substitute template variables in a prompt string.
|
|
7
|
+
* Variables use {varName} syntax.
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderPrompt(template: string, vars: Record<string, string>): string;
|
|
10
|
+
/**
|
|
11
|
+
* Build the agent/skill instruction block for prompt injection.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildAgentSkillBlock(agents: string[], skills: string[]): string;
|
|
14
|
+
/**
|
|
15
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Load architecture constraints from .workflow/architecture.md if it exists.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadArchitectureConstraint(cwd?: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
23
|
+
*/
|
|
24
|
+
export declare function assemblePrompt(opts: {
|
|
25
|
+
templateName: string;
|
|
26
|
+
vars: Record<string, string>;
|
|
27
|
+
agents?: string[];
|
|
28
|
+
skills?: string[];
|
|
29
|
+
extraText?: string;
|
|
30
|
+
cwd?: string;
|
|
31
|
+
}): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const WORKFLOW_DIR = '.workflow';
|
|
4
|
+
/**
|
|
5
|
+
* Load a prompt template from .workflow/prompts/<name>.md
|
|
6
|
+
*/
|
|
7
|
+
export function loadPrompt(name, cwd = process.cwd()) {
|
|
8
|
+
const promptPath = path.join(cwd, WORKFLOW_DIR, 'prompts', `${name}.md`);
|
|
9
|
+
if (!fs.existsSync(promptPath)) {
|
|
10
|
+
throw new Error(`Prompt template "${name}" not found at ${promptPath}`);
|
|
11
|
+
}
|
|
12
|
+
return fs.readFileSync(promptPath, 'utf-8');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Substitute template variables in a prompt string.
|
|
16
|
+
* Variables use {varName} syntax.
|
|
17
|
+
*/
|
|
18
|
+
export function renderPrompt(template, vars) {
|
|
19
|
+
let result = template;
|
|
20
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
21
|
+
result = result.replaceAll(`{${key}}`, value);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the agent/skill instruction block for prompt injection.
|
|
27
|
+
*/
|
|
28
|
+
export function buildAgentSkillBlock(agents, skills) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
if (agents.length > 0) {
|
|
31
|
+
parts.push(`\n## Agent Instructions\nInvolve the following agents in analysis: ${agents.map(a => `@${a}`).join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
if (skills.length > 0) {
|
|
34
|
+
parts.push(`\n## Skill/MCP Instructions\nUse the following skills/MCPs to assist: ${skills.map(s => `/${s}`).join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
return parts.join('\n');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Load architecture constraints from .workflow/architecture.md if it exists.
|
|
43
|
+
*/
|
|
44
|
+
export function loadArchitectureConstraint(cwd = process.cwd()) {
|
|
45
|
+
const archPath = path.join(cwd, WORKFLOW_DIR, 'architecture.md');
|
|
46
|
+
if (!fs.existsSync(archPath))
|
|
47
|
+
return '';
|
|
48
|
+
const content = fs.readFileSync(archPath, 'utf-8');
|
|
49
|
+
return `\n## Architecture Constraint
|
|
50
|
+
This project follows the architecture specification below. All implementations must comply:
|
|
51
|
+
${content}
|
|
52
|
+
|
|
53
|
+
<HARD-GATE>
|
|
54
|
+
- New files must be placed in the correct directory as defined by the architecture
|
|
55
|
+
- Dependency direction must not be violated (e.g., domain must not reference infrastructure)
|
|
56
|
+
- Follow the architecture's naming conventions and file naming rules
|
|
57
|
+
- If requirements cross layers, use Port/Adapter or the corresponding architectural pattern
|
|
58
|
+
- NestJS projects: new features must create independent Modules; do not stuff logic into unrelated Modules
|
|
59
|
+
</HARD-GATE>`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
63
|
+
*/
|
|
64
|
+
export function assemblePrompt(opts) {
|
|
65
|
+
const template = loadPrompt(opts.templateName, opts.cwd);
|
|
66
|
+
let prompt = renderPrompt(template, opts.vars);
|
|
67
|
+
// Auto-append architecture constraints when architecture.md exists
|
|
68
|
+
const archConstraint = loadArchitectureConstraint(opts.cwd);
|
|
69
|
+
if (archConstraint) {
|
|
70
|
+
prompt += archConstraint;
|
|
71
|
+
}
|
|
72
|
+
const agentSkillBlock = buildAgentSkillBlock(opts.agents ?? [], opts.skills ?? []);
|
|
73
|
+
if (agentSkillBlock) {
|
|
74
|
+
prompt += '\n' + agentSkillBlock;
|
|
75
|
+
}
|
|
76
|
+
if (opts.extraText) {
|
|
77
|
+
prompt += `\n\n## Additional Instructions\n${opts.extraText}`;
|
|
78
|
+
}
|
|
79
|
+
return prompt;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../src/utils/prompt.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pixel613/spec",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Lightweight spec-driven development CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"spec-driven",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"ai",
|
|
14
|
+
"cli",
|
|
15
|
+
"workflow"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/pixel613/spec-cli.git"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"pxs": "./dist/bin/pxs.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"dev": "tsx watch bin/pxs.ts",
|
|
31
|
+
"test": "vitest",
|
|
32
|
+
"prepublishOnly": "npm run build && npm test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"chalk": "^5.4.1",
|
|
36
|
+
"commander": "^13.1.0",
|
|
37
|
+
"inquirer": "^12.6.0",
|
|
38
|
+
"js-yaml": "^4.1.0"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist/",
|
|
42
|
+
"templates/"
|
|
43
|
+
],
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/inquirer": "^9.0.7",
|
|
46
|
+
"@types/js-yaml": "^4.0.9",
|
|
47
|
+
"@types/node": "^22.15.0",
|
|
48
|
+
"tsx": "^4.19.4",
|
|
49
|
+
"typescript": "^5.8.3",
|
|
50
|
+
"vitest": "^3.1.4"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## SF Spec-Driven Workflow
|
|
2
|
+
|
|
3
|
+
This project uses sf for spec-driven development.
|
|
4
|
+
|
|
5
|
+
### Workflow State
|
|
6
|
+
- Read `.workflow/state.yaml` for current progress
|
|
7
|
+
- Read `.workflow/plans/<feature>.md` for implementation plan
|
|
8
|
+
|
|
9
|
+
### Rules
|
|
10
|
+
- Implement tasks in the order specified in the plan; do not skip
|
|
11
|
+
- After completing each task, run `git add -A && git commit`
|
|
12
|
+
- Commit messages follow conventional commits or the project's custom convention
|
|
13
|
+
- Do not implement beyond task spec scope
|
|
14
|
+
- After each task completion, wait for user confirmation before continuing
|
|
15
|
+
|
|
16
|
+
### Prompt Templates
|
|
17
|
+
- Implementation guide: `.workflow/prompts/implement.md`
|
|
18
|
+
- TDD guide: `.workflow/prompts/implement-tdd.md`
|
|
19
|
+
- Review guide: `.workflow/prompts/review.md`
|
|
20
|
+
- Test guide: `.workflow/prompts/test.md`
|