@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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/bin/pxs.d.ts +2 -0
  4. package/dist/bin/pxs.js +5 -0
  5. package/dist/bin/pxs.js.map +1 -0
  6. package/dist/src/backends/claude.d.ts +9 -0
  7. package/dist/src/backends/claude.js +80 -0
  8. package/dist/src/backends/claude.js.map +1 -0
  9. package/dist/src/backends/codex.d.ts +9 -0
  10. package/dist/src/backends/codex.js +72 -0
  11. package/dist/src/backends/codex.js.map +1 -0
  12. package/dist/src/backends/factory.d.ts +2 -0
  13. package/dist/src/backends/factory.js +14 -0
  14. package/dist/src/backends/factory.js.map +1 -0
  15. package/dist/src/backends/interface.d.ts +15 -0
  16. package/dist/src/backends/interface.js +2 -0
  17. package/dist/src/backends/interface.js.map +1 -0
  18. package/dist/src/cli.d.ts +2 -0
  19. package/dist/src/cli.js +79 -0
  20. package/dist/src/cli.js.map +1 -0
  21. package/dist/src/commands/clarify.d.ts +5 -0
  22. package/dist/src/commands/clarify.js +46 -0
  23. package/dist/src/commands/clarify.js.map +1 -0
  24. package/dist/src/commands/implement.d.ts +9 -0
  25. package/dist/src/commands/implement.js +244 -0
  26. package/dist/src/commands/implement.js.map +1 -0
  27. package/dist/src/commands/init.d.ts +6 -0
  28. package/dist/src/commands/init.js +177 -0
  29. package/dist/src/commands/init.js.map +1 -0
  30. package/dist/src/commands/new.d.ts +5 -0
  31. package/dist/src/commands/new.js +143 -0
  32. package/dist/src/commands/new.js.map +1 -0
  33. package/dist/src/commands/refine.d.ts +8 -0
  34. package/dist/src/commands/refine.js +158 -0
  35. package/dist/src/commands/refine.js.map +1 -0
  36. package/dist/src/commands/review.d.ts +4 -0
  37. package/dist/src/commands/review.js +70 -0
  38. package/dist/src/commands/review.js.map +1 -0
  39. package/dist/src/commands/status.d.ts +1 -0
  40. package/dist/src/commands/status.js +53 -0
  41. package/dist/src/commands/status.js.map +1 -0
  42. package/dist/src/discovery/project.d.ts +7 -0
  43. package/dist/src/discovery/project.js +90 -0
  44. package/dist/src/discovery/project.js.map +1 -0
  45. package/dist/src/git/operations.d.ts +10 -0
  46. package/dist/src/git/operations.js +56 -0
  47. package/dist/src/git/operations.js.map +1 -0
  48. package/dist/src/parsers/arguments.d.ts +18 -0
  49. package/dist/src/parsers/arguments.js +43 -0
  50. package/dist/src/parsers/arguments.js.map +1 -0
  51. package/dist/src/parsers/plan.d.ts +23 -0
  52. package/dist/src/parsers/plan.js +117 -0
  53. package/dist/src/parsers/plan.js.map +1 -0
  54. package/dist/src/parsers/spec.d.ts +10 -0
  55. package/dist/src/parsers/spec.js +46 -0
  56. package/dist/src/parsers/spec.js.map +1 -0
  57. package/dist/src/state/manager.d.ts +24 -0
  58. package/dist/src/state/manager.js +103 -0
  59. package/dist/src/state/manager.js.map +1 -0
  60. package/dist/src/state/types.d.ts +47 -0
  61. package/dist/src/state/types.js +21 -0
  62. package/dist/src/state/types.js.map +1 -0
  63. package/dist/src/utils/display.d.ts +7 -0
  64. package/dist/src/utils/display.js +42 -0
  65. package/dist/src/utils/display.js.map +1 -0
  66. package/dist/src/utils/prompt.d.ts +31 -0
  67. package/dist/src/utils/prompt.js +81 -0
  68. package/dist/src/utils/prompt.js.map +1 -0
  69. package/package.json +52 -0
  70. package/templates/agents-md-snippet.md +20 -0
  71. package/templates/architectures/clean/csharp-aspnet.md +56 -0
  72. package/templates/architectures/clean/go-gin.md +50 -0
  73. package/templates/architectures/clean/go-std.md +49 -0
  74. package/templates/architectures/clean/python-fastapi.md +49 -0
  75. package/templates/architectures/clean/typescript-express.md +60 -0
  76. package/templates/architectures/clean/typescript-nestjs.md +61 -0
  77. package/templates/architectures/ddd/csharp-aspnet.md +55 -0
  78. package/templates/architectures/ddd/go-gin.md +53 -0
  79. package/templates/architectures/ddd/python-fastapi.md +52 -0
  80. package/templates/architectures/ddd/typescript-nestjs.md +62 -0
  81. package/templates/architectures/hexagonal/csharp-aspnet.md +45 -0
  82. package/templates/architectures/hexagonal/go-gin.md +47 -0
  83. package/templates/architectures/hexagonal/python-fastapi.md +43 -0
  84. package/templates/architectures/hexagonal/typescript-nestjs.md +44 -0
  85. package/templates/architectures/layered/csharp-aspnet.md +45 -0
  86. package/templates/architectures/layered/go-gin.md +41 -0
  87. package/templates/architectures/layered/python-fastapi.md +42 -0
  88. package/templates/architectures/layered/typescript-nestjs.md +48 -0
  89. package/templates/architectures/modular/csharp-aspnet.md +45 -0
  90. package/templates/architectures/modular/go-gin.md +47 -0
  91. package/templates/architectures/modular/python-fastapi.md +45 -0
  92. package/templates/architectures/modular/typescript-nestjs.md +48 -0
  93. package/templates/claude-commands/sf.clarify.md +18 -0
  94. package/templates/claude-commands/sf.implement.md +79 -0
  95. package/templates/claude-commands/sf.new.md +22 -0
  96. package/templates/claude-commands/sf.refine.md +47 -0
  97. package/templates/claude-commands/sf.review.md +17 -0
  98. package/templates/claude-commands/sf.status.md +12 -0
  99. package/templates/workflow/config.yaml +17 -0
  100. package/templates/workflow/prompts/clarify.md +39 -0
  101. package/templates/workflow/prompts/final-review.md +30 -0
  102. package/templates/workflow/prompts/implement-tdd.md +35 -0
  103. package/templates/workflow/prompts/implement.md +43 -0
  104. package/templates/workflow/prompts/refine.md +52 -0
  105. package/templates/workflow/prompts/review.md +34 -0
  106. package/templates/workflow/prompts/test.md +14 -0
  107. 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,10 @@
1
+ export interface ParsedSpec {
2
+ title: string;
3
+ source?: string;
4
+ sections: Record<string, string>;
5
+ raw: string;
6
+ }
7
+ /**
8
+ * Parse a spec markdown file into structured sections.
9
+ */
10
+ export declare function parseSpec(content: string): ParsedSpec;
@@ -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`