@pixelcraft-tw/spec 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -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 +94 -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/diff.d.ts +1 -0
  25. package/dist/src/commands/diff.js +81 -0
  26. package/dist/src/commands/diff.js.map +1 -0
  27. package/dist/src/commands/implement.d.ts +9 -0
  28. package/dist/src/commands/implement.js +247 -0
  29. package/dist/src/commands/implement.js.map +1 -0
  30. package/dist/src/commands/init.d.ts +6 -0
  31. package/dist/src/commands/init.js +183 -0
  32. package/dist/src/commands/init.js.map +1 -0
  33. package/dist/src/commands/new.d.ts +5 -0
  34. package/dist/src/commands/new.js +186 -0
  35. package/dist/src/commands/new.js.map +1 -0
  36. package/dist/src/commands/refine.d.ts +8 -0
  37. package/dist/src/commands/refine.js +158 -0
  38. package/dist/src/commands/refine.js.map +1 -0
  39. package/dist/src/commands/reset.d.ts +3 -0
  40. package/dist/src/commands/reset.js +44 -0
  41. package/dist/src/commands/reset.js.map +1 -0
  42. package/dist/src/commands/review.d.ts +4 -0
  43. package/dist/src/commands/review.js +70 -0
  44. package/dist/src/commands/review.js.map +1 -0
  45. package/dist/src/commands/status.d.ts +1 -0
  46. package/dist/src/commands/status.js +53 -0
  47. package/dist/src/commands/status.js.map +1 -0
  48. package/dist/src/discovery/project.d.ts +7 -0
  49. package/dist/src/discovery/project.js +135 -0
  50. package/dist/src/discovery/project.js.map +1 -0
  51. package/dist/src/git/operations.d.ts +10 -0
  52. package/dist/src/git/operations.js +56 -0
  53. package/dist/src/git/operations.js.map +1 -0
  54. package/dist/src/parsers/arguments.d.ts +18 -0
  55. package/dist/src/parsers/arguments.js +43 -0
  56. package/dist/src/parsers/arguments.js.map +1 -0
  57. package/dist/src/parsers/plan.d.ts +23 -0
  58. package/dist/src/parsers/plan.js +117 -0
  59. package/dist/src/parsers/plan.js.map +1 -0
  60. package/dist/src/parsers/spec.d.ts +10 -0
  61. package/dist/src/parsers/spec.js +46 -0
  62. package/dist/src/parsers/spec.js.map +1 -0
  63. package/dist/src/state/manager.d.ts +24 -0
  64. package/dist/src/state/manager.js +103 -0
  65. package/dist/src/state/manager.js.map +1 -0
  66. package/dist/src/state/types.d.ts +48 -0
  67. package/dist/src/state/types.js +20 -0
  68. package/dist/src/state/types.js.map +1 -0
  69. package/dist/src/utils/display.d.ts +7 -0
  70. package/dist/src/utils/display.js +42 -0
  71. package/dist/src/utils/display.js.map +1 -0
  72. package/dist/src/utils/prompt.d.ts +15 -0
  73. package/dist/src/utils/prompt.js +139 -0
  74. package/dist/src/utils/prompt.js.map +1 -0
  75. package/package.json +52 -0
  76. package/templates/agents-md-snippet.md +20 -0
  77. package/templates/architectures/clean/csharp-aspnet.md +56 -0
  78. package/templates/architectures/clean/dart-flutter.md +73 -0
  79. package/templates/architectures/clean/go-gin.md +50 -0
  80. package/templates/architectures/clean/go-std.md +49 -0
  81. package/templates/architectures/clean/kotlin-android.md +70 -0
  82. package/templates/architectures/clean/python-fastapi.md +49 -0
  83. package/templates/architectures/clean/swift-ios.md +69 -0
  84. package/templates/architectures/clean/typescript-express.md +60 -0
  85. package/templates/architectures/clean/typescript-nestjs.md +61 -0
  86. package/templates/architectures/ddd/csharp-aspnet.md +55 -0
  87. package/templates/architectures/ddd/go-gin.md +53 -0
  88. package/templates/architectures/ddd/python-fastapi.md +52 -0
  89. package/templates/architectures/ddd/typescript-nestjs.md +62 -0
  90. package/templates/architectures/hexagonal/csharp-aspnet.md +45 -0
  91. package/templates/architectures/hexagonal/go-gin.md +47 -0
  92. package/templates/architectures/hexagonal/python-fastapi.md +43 -0
  93. package/templates/architectures/hexagonal/typescript-nestjs.md +44 -0
  94. package/templates/architectures/layered/csharp-aspnet.md +45 -0
  95. package/templates/architectures/layered/go-gin.md +41 -0
  96. package/templates/architectures/layered/python-fastapi.md +42 -0
  97. package/templates/architectures/layered/typescript-nestjs.md +48 -0
  98. package/templates/architectures/modular/csharp-aspnet.md +45 -0
  99. package/templates/architectures/modular/dart-flutter.md +64 -0
  100. package/templates/architectures/modular/go-gin.md +47 -0
  101. package/templates/architectures/modular/kotlin-android.md +68 -0
  102. package/templates/architectures/modular/python-fastapi.md +45 -0
  103. package/templates/architectures/modular/swift-ios.md +55 -0
  104. package/templates/architectures/modular/typescript-nestjs.md +48 -0
  105. package/templates/architectures/mvvm/dart-flutter.md +69 -0
  106. package/templates/architectures/mvvm/kotlin-android.md +79 -0
  107. package/templates/architectures/mvvm/swift-ios.md +66 -0
  108. package/templates/claude-commands/sf.clarify.md +18 -0
  109. package/templates/claude-commands/sf.implement.md +80 -0
  110. package/templates/claude-commands/sf.new.md +22 -0
  111. package/templates/claude-commands/sf.refine.md +47 -0
  112. package/templates/claude-commands/sf.review.md +17 -0
  113. package/templates/claude-commands/sf.status.md +12 -0
  114. package/templates/workflow/config.yaml +17 -0
  115. package/templates/workflow/prompts/clarify.md +39 -0
  116. package/templates/workflow/prompts/final-review.md +30 -0
  117. package/templates/workflow/prompts/implement-tdd.md +35 -0
  118. package/templates/workflow/prompts/implement.md +43 -0
  119. package/templates/workflow/prompts/refine.md +52 -0
  120. package/templates/workflow/prompts/review.md +33 -0
  121. package/templates/workflow/prompts/test.md +14 -0
  122. package/templates/workflow/templates/spec-template.md +13 -0
@@ -0,0 +1,48 @@
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
+ base_branch?: string;
19
+ phase: Phase;
20
+ total_tasks: number;
21
+ current_task: number;
22
+ session?: SessionInfo;
23
+ tasks: TaskState[];
24
+ }
25
+ export interface WorkflowState {
26
+ features: FeatureState[];
27
+ }
28
+ export interface ProjectConfig {
29
+ project: {
30
+ name: string;
31
+ language: string;
32
+ framework: string;
33
+ architecture: string;
34
+ lang_framework: string;
35
+ };
36
+ git: {
37
+ convention: string;
38
+ };
39
+ backend: {
40
+ default: string;
41
+ };
42
+ test: {
43
+ strategy: TestStrategy;
44
+ type: TestType;
45
+ };
46
+ }
47
+ export declare const PHASE_GUARDS: Record<string, Phase[]>;
48
+ export declare const TASK_TRANSITIONS: Record<TaskStatus, TaskStatus[]>;
@@ -0,0 +1,20 @@
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
+ reset: [], // any phase
10
+ diff: ['ready_to_implement', 'implementing', 'completed', 'merged'],
11
+ };
12
+ // Valid task status transitions
13
+ export const TASK_TRANSITIONS = {
14
+ pending: ['in_progress', 'skipped'],
15
+ in_progress: ['review_pending'],
16
+ review_pending: ['complete', 'in_progress'], // in_progress = request-change
17
+ complete: [],
18
+ skipped: [],
19
+ };
20
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/state/types.ts"],"names":[],"mappings":"AA+DA,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,KAAK,EAAE,EAAE,EAAE,YAAY;IACvB,IAAI,EAAE,CAAC,oBAAoB,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,CAAC;CACpE,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,15 @@
1
+ /**
2
+ * Load a prompt template from .workflow/prompts/<name>.md
3
+ */
4
+ export declare function loadPrompt(name: string, cwd?: string): string;
5
+ /**
6
+ * Assemble a full prompt from template name + variables + agents/skills + extra text.
7
+ */
8
+ export declare function assemblePrompt(opts: {
9
+ templateName: string;
10
+ vars: Record<string, string>;
11
+ agents?: string[];
12
+ skills?: string[];
13
+ extraText?: string;
14
+ cwd?: string;
15
+ }): string;
@@ -0,0 +1,139 @@
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
+ 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
+ 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
+ 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
+ * Discover review-related slash commands and skills in the project.
63
+ * Scans .claude/commands/ for review-related files.
64
+ */
65
+ function discoverReviewTools(cwd = process.cwd()) {
66
+ const commands = [];
67
+ const skills = [];
68
+ // Scan .claude/commands/ for review-related slash commands
69
+ const commandsDir = path.join(cwd, '.claude', 'commands');
70
+ if (fs.existsSync(commandsDir)) {
71
+ try {
72
+ const files = fs.readdirSync(commandsDir);
73
+ for (const file of files) {
74
+ if (!file.endsWith('.md'))
75
+ continue;
76
+ const name = file.replace('.md', '');
77
+ const lower = name.toLowerCase();
78
+ if (lower.includes('review') || lower.includes('simplify') || lower.includes('lint')
79
+ || lower.includes('audit') || lower.includes('check')) {
80
+ commands.push(`/${name}`);
81
+ }
82
+ }
83
+ }
84
+ catch {
85
+ // ignore read errors
86
+ }
87
+ }
88
+ // Well-known review skills available in Claude Code
89
+ const knownReviewSkills = ['simplify', 'code-review'];
90
+ skills.push(...knownReviewSkills);
91
+ return { commands, skills };
92
+ }
93
+ /**
94
+ * Build auto-discovery block for review prompts when no agents/skills are provided.
95
+ */
96
+ function buildReviewAutoDiscoveryBlock(cwd = process.cwd()) {
97
+ const { commands, skills } = discoverReviewTools(cwd);
98
+ const parts = [];
99
+ parts.push('\n## Review Tool Instructions');
100
+ parts.push('You MUST perform a thorough code review. Do NOT just summarize the git diff.');
101
+ parts.push('Analyze the actual code changes for correctness, security, performance, and quality.');
102
+ if (commands.length > 0) {
103
+ parts.push(`\nAvailable project review commands: ${commands.join(', ')}`);
104
+ parts.push('Consider using these to assist the review.');
105
+ }
106
+ if (skills.length > 0) {
107
+ parts.push(`\nAvailable review skills: ${skills.map(s => `/${s}`).join(', ')}`);
108
+ parts.push('Use /simplify or /code-review if available to perform deeper analysis.');
109
+ }
110
+ return parts.join('\n');
111
+ }
112
+ const REVIEW_TEMPLATES = ['review', 'final-review'];
113
+ /**
114
+ * Assemble a full prompt from template name + variables + agents/skills + extra text.
115
+ */
116
+ export function assemblePrompt(opts) {
117
+ const template = loadPrompt(opts.templateName, opts.cwd);
118
+ let prompt = renderPrompt(template, opts.vars);
119
+ // Auto-append architecture constraints when architecture.md exists
120
+ const archConstraint = loadArchitectureConstraint(opts.cwd);
121
+ if (archConstraint) {
122
+ prompt += archConstraint;
123
+ }
124
+ const agents = opts.agents ?? [];
125
+ const skills = opts.skills ?? [];
126
+ const agentSkillBlock = buildAgentSkillBlock(agents, skills);
127
+ if (agentSkillBlock) {
128
+ prompt += '\n' + agentSkillBlock;
129
+ }
130
+ // For review templates: inject auto-discovery when no agents/skills are provided
131
+ if (REVIEW_TEMPLATES.includes(opts.templateName) && agents.length === 0 && skills.length === 0) {
132
+ prompt += buildReviewAutoDiscoveryBlock(opts.cwd);
133
+ }
134
+ if (opts.extraText) {
135
+ prompt += `\n\n## Additional Instructions\n${opts.extraText}`;
136
+ }
137
+ return prompt;
138
+ }
139
+ //# 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": "@pixelcraft-tw/spec",
3
+ "version": "1.0.0",
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/pixelcraft-tw/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`
@@ -0,0 +1,56 @@
1
+ # Architecture: Clean Architecture (C# + ASP.NET Core)
2
+
3
+ ## Project Structure
4
+ ```
5
+ src/
6
+ ├── Domain/
7
+ │ ├── Entities/Order.cs
8
+ │ ├── ValueObjects/Money.cs
9
+ │ ├── Events/OrderCreatedEvent.cs
10
+ │ ├── Errors/DomainException.cs
11
+ │ └── Interfaces/IOrderRepository.cs
12
+ ├── Application/
13
+ │ ├── UseCases/CreateOrder/
14
+ │ │ ├── CreateOrderCommand.cs
15
+ │ │ ├── CreateOrderHandler.cs
16
+ │ │ └── CreateOrderValidator.cs
17
+ │ ├── Common/
18
+ │ │ └── Behaviors/ValidationBehavior.cs
19
+ │ └── DependencyInjection.cs
20
+ ├── Infrastructure/
21
+ │ ├── Persistence/
22
+ │ │ ├── ApplicationDbContext.cs
23
+ │ │ ├── Repositories/OrderRepository.cs
24
+ │ │ └── Configurations/OrderConfiguration.cs
25
+ │ ├── Services/
26
+ │ │ └── PaymentGateway.cs
27
+ │ └── DependencyInjection.cs
28
+ └── WebApi/
29
+ ├── Controllers/OrderController.cs
30
+ ├── Middleware/ExceptionHandlerMiddleware.cs
31
+ ├── Filters/
32
+ └── Program.cs
33
+ ```
34
+
35
+ ## Dependency Rule
36
+ - Domain → depends on nothing outside, pure C# classes
37
+ - Application → depends only on Domain; uses MediatR for CQRS
38
+ - Infrastructure → implements Domain interfaces, EF Core DbContext
39
+ - WebApi → depends on Application; ASP.NET Controllers + minimal APIs
40
+
41
+ ## Conventions
42
+ - MediatR for Command/Query handling
43
+ - FluentValidation for input validation
44
+ - EF Core with Fluent API configuration (no data annotations on Domain)
45
+ - DI registration in each layer's DependencyInjection.cs
46
+
47
+ ## File Naming
48
+ - PascalCase: CreateOrderCommand.cs
49
+ - One class per file
50
+ - Folder-per-feature in Application (e.g., UseCases/CreateOrder/)
51
+
52
+ ## Testing
53
+ - Domain: pure unit tests (xUnit)
54
+ - Application: mock repositories (NSubstitute/Moq)
55
+ - Infrastructure: integration tests (WebApplicationFactory, TestContainers)
56
+ - WebApi: E2E tests (WebApplicationFactory)
@@ -0,0 +1,73 @@
1
+ # Architecture: Clean Architecture (Flutter)
2
+
3
+ ## Layer-Based Structure
4
+ Flutter app organized into domain, application, infrastructure, and presentation layers under `lib/`.
5
+
6
+ ## Directory Structure
7
+ ```
8
+ lib/
9
+ ├── domain/
10
+ │ ├── entities/order.dart
11
+ │ ├── value_objects/money.dart
12
+ │ └── errors/domain_error.dart
13
+ ├── application/
14
+ │ ├── use_cases/create_order_use_case.dart
15
+ │ ├── ports/
16
+ │ │ ├── order_repository_port.dart
17
+ │ │ └── payment_gateway_port.dart
18
+ │ └── dtos/create_order_dto.dart
19
+ ├── infrastructure/
20
+ │ ├── repositories/order_repository.dart
21
+ │ ├── services/payment_gateway.dart
22
+ │ ├── database/
23
+ │ │ └── app_database.dart
24
+ │ └── config/env.dart
25
+ ├── presentation/
26
+ │ ├── pages/order_page.dart
27
+ │ ├── widgets/order_card.dart
28
+ │ ├── providers/order_provider.dart
29
+ │ └── router/app_router.dart
30
+ └── main.dart
31
+ test/
32
+ ├── domain/
33
+ ├── application/
34
+ ├── infrastructure/
35
+ └── presentation/
36
+ integration_test/
37
+ └── app_test.dart
38
+ ```
39
+
40
+ ## Dependency Rule
41
+ - domain → depends on nothing outside, pure Dart classes
42
+ - application → depends only on domain; use cases call externals via port abstract classes
43
+ - infrastructure → implements application ports
44
+ - presentation → depends on application; widgets + providers
45
+
46
+ ## Recommended Dependencies
47
+ - State Management: Riverpod + flutter_hooks
48
+ - Networking: dio + retrofit
49
+ - Navigation: go_router
50
+ - Local Storage: drift (SQL), shared_preferences (KV)
51
+ - DI: Riverpod (self-contained)
52
+
53
+ ## Conventions
54
+ - Use Cases are single-purpose classes with a `call()` method
55
+ - Providers (Riverpod) expose use cases and state to UI
56
+ - Presentation layer never imports infrastructure directly
57
+ - Error handling via Either pattern or sealed result classes
58
+
59
+ ## File Naming
60
+ - snake_case: create_order_use_case.dart
61
+ - Entity: *_entity.dart or just *.dart under entities/
62
+ - Port: *_port.dart
63
+ - Repository: *_repository.dart
64
+ - Provider: *_provider.dart
65
+ - Page: *_page.dart
66
+ - Widget: *_widget.dart or descriptive name
67
+
68
+ ## Testing
69
+ - domain: pure unit tests, no mocks
70
+ - application: mock ports
71
+ - infrastructure: integration tests
72
+ - presentation: widget tests with WidgetTester
73
+ - integration_test/: full app flow tests
@@ -0,0 +1,50 @@
1
+ # Architecture: Clean Architecture (Go + Gin)
2
+
3
+ ## Directory Structure
4
+ ```
5
+ internal/
6
+ ├── domain/
7
+ │ ├── entity/order.go # Entities, Value Objects
8
+ │ └── event/order_created.go # Domain Events
9
+ ├── usecase/
10
+ │ ├── create_order.go # Use Cases (one file per use case)
11
+ │ └── interfaces.go # Port interfaces
12
+ ├── infrastructure/
13
+ │ ├── postgres/
14
+ │ │ └── order_repo.go # Repository implementation
15
+ │ ├── redis/
16
+ │ │ └── cache.go # Cache implementation (if any)
17
+ │ └── config/
18
+ │ └── env.go # Environment configuration
19
+ ├── handler/
20
+ │ └── http/
21
+ │ ├── order_handler.go # Gin handler
22
+ │ ├── middleware/
23
+ │ │ └── auth.go # Middleware
24
+ │ └── router.go # Route definitions
25
+ └── pkg/ # Cross-project reusable packages
26
+ └── logger/
27
+ ```
28
+
29
+ ## Dependency Rule
30
+ - domain → depends on nothing outside, pure Go structs and interfaces
31
+ - usecase → depends only on domain; calls infrastructure through interfaces
32
+ - infrastructure → implements usecase interfaces
33
+ - handler → depends on usecase; handles HTTP layer
34
+
35
+ ## Go Conventions
36
+ - Interfaces defined at the consumer side (usecase/), not implementation side (infrastructure/)
37
+ - Error handling uses custom error types defined in domain/
38
+ - Struct initialization uses New functions: NewOrderUseCase(), NewOrderRepo()
39
+ - Context passed throughout: handler → usecase → repository
40
+
41
+ ## File Naming
42
+ - snake_case: create_order.go
43
+ - Test files: create_order_test.go
44
+ - One type per file within same package
45
+
46
+ ## Testing
47
+ - domain: pure unit tests
48
+ - usecase: mock interfaces (using mockgen or hand-written mocks)
49
+ - infrastructure: integration tests (using testcontainers or test DB)
50
+ - handler: HTTP tests (using httptest)
@@ -0,0 +1,49 @@
1
+ # Architecture: Clean Architecture (Go Standard Library)
2
+
3
+ ## Directory Structure
4
+ ```
5
+ internal/
6
+ ├── domain/
7
+ │ ├── entity/order.go # Entities, Value Objects
8
+ │ └── errors.go # Domain errors
9
+ ├── usecase/
10
+ │ ├── create_order.go # Use Cases
11
+ │ └── interfaces.go # Port interfaces
12
+ ├── infrastructure/
13
+ │ ├── postgres/
14
+ │ │ └── order_repo.go # Repository implementation
15
+ │ └── config/
16
+ │ └── env.go # Environment configuration
17
+ ├── handler/
18
+ │ └── http/
19
+ │ ├── order_handler.go # net/http handler
20
+ │ ├── middleware/
21
+ │ │ └── auth.go # Middleware
22
+ │ └── router.go # http.ServeMux routes
23
+ └── pkg/
24
+ └── logger/
25
+ ```
26
+
27
+ ## Dependency Rule
28
+ - domain → depends on nothing outside, pure Go structs and interfaces
29
+ - usecase → depends only on domain; calls infrastructure through interfaces
30
+ - infrastructure → implements usecase interfaces
31
+ - handler → depends on usecase; handles HTTP layer using net/http
32
+
33
+ ## Go Conventions
34
+ - Use net/http ServeMux (Go 1.22+ enhanced routing) instead of external routers
35
+ - Interfaces defined at the consumer side (usecase/)
36
+ - Error handling uses custom error types defined in domain/
37
+ - Struct initialization uses New functions
38
+ - Context passed throughout: handler → usecase → repository
39
+
40
+ ## File Naming
41
+ - snake_case: create_order.go
42
+ - Test files: create_order_test.go
43
+ - One type per file within same package
44
+
45
+ ## Testing
46
+ - domain: pure unit tests
47
+ - usecase: mock interfaces
48
+ - infrastructure: integration tests (using testcontainers)
49
+ - handler: HTTP tests (using httptest)
@@ -0,0 +1,70 @@
1
+ # Architecture: Clean Architecture (Android / Kotlin)
2
+
3
+ ## Layer-Based Structure
4
+ Android app organized into domain, data, and presentation packages following Clean Architecture with Jetpack Compose.
5
+
6
+ ## Directory Structure
7
+ ```
8
+ app/src/main/java/com/example/app/
9
+ ├── domain/
10
+ │ ├── model/Order.kt
11
+ │ ├── repository/OrderRepository.kt # interface
12
+ │ ├── usecase/CreateOrderUseCase.kt
13
+ │ └── error/DomainError.kt
14
+ ├── data/
15
+ │ ├── repository/OrderRepositoryImpl.kt
16
+ │ ├── remote/
17
+ │ │ ├── api/OrderApi.kt # Retrofit interface
18
+ │ │ └── dto/OrderDto.kt
19
+ │ ├── local/
20
+ │ │ ├── dao/OrderDao.kt # Room DAO
21
+ │ │ └── entity/OrderEntity.kt
22
+ │ └── mapper/OrderMapper.kt
23
+ ├── presentation/
24
+ │ ├── order/
25
+ │ │ ├── OrderScreen.kt
26
+ │ │ └── OrderViewModel.kt
27
+ │ ├── navigation/AppNavigation.kt
28
+ │ └── theme/Theme.kt
29
+ ├── di/
30
+ │ ├── AppModule.kt
31
+ │ ├── NetworkModule.kt
32
+ │ └── DatabaseModule.kt
33
+ └── App.kt # Application class
34
+ app/src/test/ # Unit tests
35
+ app/src/androidTest/ # Instrumented tests
36
+ ```
37
+
38
+ ## Dependency Rule
39
+ - domain → depends on nothing outside, pure Kotlin classes + interfaces
40
+ - data → implements domain repository interfaces
41
+ - presentation → depends on domain; Compose screens + ViewModels
42
+ - di → wires everything together via Hilt modules
43
+
44
+ ## Recommended Dependencies
45
+ - State Management: Hilt + StateFlow + Compose State
46
+ - Networking: Retrofit + OkHttp
47
+ - DI: Hilt
48
+ - Navigation: Navigation Compose
49
+ - Local Storage: Room
50
+ - Async: Kotlin Coroutines + Flow
51
+
52
+ ## Conventions
53
+ - Use Cases are single-purpose classes with an `operator fun invoke()` or `execute()` method
54
+ - ViewModels extend `ViewModel()`, expose `StateFlow` to Compose
55
+ - Repository interfaces live in domain, implementations in data
56
+ - Hilt @Module classes in di/ package
57
+
58
+ ## File Naming
59
+ - PascalCase: CreateOrderUseCase.kt
60
+ - Interface: OrderRepository.kt (domain), OrderRepositoryImpl.kt (data)
61
+ - DAO: *Dao.kt
62
+ - API: *Api.kt
63
+ - Screen: *Screen.kt
64
+ - ViewModel: *ViewModel.kt
65
+
66
+ ## Testing
67
+ - domain: pure unit tests, JUnit
68
+ - data: integration tests with Room in-memory DB
69
+ - presentation: Compose UI tests with composeTestRule
70
+ - Use Cases: unit test with mock repositories