@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.
- package/LICENSE +21 -0
- package/README.md +195 -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 +94 -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/diff.d.ts +1 -0
- package/dist/src/commands/diff.js +81 -0
- package/dist/src/commands/diff.js.map +1 -0
- package/dist/src/commands/implement.d.ts +9 -0
- package/dist/src/commands/implement.js +247 -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 +183 -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 +186 -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/reset.d.ts +3 -0
- package/dist/src/commands/reset.js +44 -0
- package/dist/src/commands/reset.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 +135 -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 +48 -0
- package/dist/src/state/types.js +20 -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 +15 -0
- package/dist/src/utils/prompt.js +139 -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/dart-flutter.md +73 -0
- package/templates/architectures/clean/go-gin.md +50 -0
- package/templates/architectures/clean/go-std.md +49 -0
- package/templates/architectures/clean/kotlin-android.md +70 -0
- package/templates/architectures/clean/python-fastapi.md +49 -0
- package/templates/architectures/clean/swift-ios.md +69 -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/dart-flutter.md +64 -0
- package/templates/architectures/modular/go-gin.md +47 -0
- package/templates/architectures/modular/kotlin-android.md +68 -0
- package/templates/architectures/modular/python-fastapi.md +45 -0
- package/templates/architectures/modular/swift-ios.md +55 -0
- package/templates/architectures/modular/typescript-nestjs.md +48 -0
- package/templates/architectures/mvvm/dart-flutter.md +69 -0
- package/templates/architectures/mvvm/kotlin-android.md +79 -0
- package/templates/architectures/mvvm/swift-ios.md +66 -0
- package/templates/claude-commands/sf.clarify.md +18 -0
- package/templates/claude-commands/sf.implement.md +80 -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 +33 -0
- package/templates/workflow/prompts/test.md +14 -0
- package/templates/workflow/templates/spec-template.md +13 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { StateManager } from '../state/manager.js';
|
|
4
|
+
import { createBackend } from '../backends/factory.js';
|
|
5
|
+
import * as display from '../utils/display.js';
|
|
6
|
+
export async function newCommand(name, options) {
|
|
7
|
+
const state = new StateManager();
|
|
8
|
+
state.ensureWorkflow();
|
|
9
|
+
const specPath = state.specPath(name);
|
|
10
|
+
// Check if spec already exists
|
|
11
|
+
if (fs.existsSync(specPath)) {
|
|
12
|
+
const { overwrite } = await inquirer.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: 'confirm',
|
|
15
|
+
name: 'overwrite',
|
|
16
|
+
message: `Spec "${name}" already exists. Overwrite?`,
|
|
17
|
+
default: false,
|
|
18
|
+
},
|
|
19
|
+
]);
|
|
20
|
+
if (!overwrite) {
|
|
21
|
+
display.info('Aborted.');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Ensure specs dir exists
|
|
26
|
+
fs.mkdirSync(state.specsDir(), { recursive: true });
|
|
27
|
+
if (options.jira && options.jira.length > 0) {
|
|
28
|
+
await createFromJira(name, options.jira, state);
|
|
29
|
+
}
|
|
30
|
+
else if (options.desc) {
|
|
31
|
+
await createFromDesc(name, options.desc, state);
|
|
32
|
+
}
|
|
33
|
+
else if (options.interactive) {
|
|
34
|
+
await createInteractive(name, state);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
createBlank(name, state);
|
|
38
|
+
}
|
|
39
|
+
// Update state
|
|
40
|
+
state.upsertFeature({
|
|
41
|
+
feature: name,
|
|
42
|
+
type: 'feat',
|
|
43
|
+
branch: '',
|
|
44
|
+
phase: 'spec_created',
|
|
45
|
+
total_tasks: 0,
|
|
46
|
+
current_task: 0,
|
|
47
|
+
tasks: [],
|
|
48
|
+
});
|
|
49
|
+
display.success(`Spec created: ${state.specPath(name)}`);
|
|
50
|
+
display.info(`Edit the spec, then run \`pxs refine ${name}\``);
|
|
51
|
+
}
|
|
52
|
+
function createBlank(name, state) {
|
|
53
|
+
const templatePath = state.templatesDir() + '/spec-template.md';
|
|
54
|
+
let template;
|
|
55
|
+
if (fs.existsSync(templatePath)) {
|
|
56
|
+
template = fs.readFileSync(templatePath, 'utf-8');
|
|
57
|
+
template = template.replace('<name>', name);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
template = `# Feature: ${name}
|
|
61
|
+
|
|
62
|
+
## Context
|
|
63
|
+
<!-- 2-3 sentences of project background -->
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
<!-- Numbered list with acceptance criteria -->
|
|
67
|
+
|
|
68
|
+
## Constraints
|
|
69
|
+
<!-- Technical limitations, compatibility requirements -->
|
|
70
|
+
|
|
71
|
+
## Notes
|
|
72
|
+
<!-- Additional notes -->
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
75
|
+
fs.writeFileSync(state.specPath(name), template, 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
async function createFromDesc(name, desc, state) {
|
|
78
|
+
display.info('Generating spec from description...');
|
|
79
|
+
try {
|
|
80
|
+
const config = state.readConfig();
|
|
81
|
+
const backend = createBackend(config.backend.default);
|
|
82
|
+
if (!(await backend.isAvailable())) {
|
|
83
|
+
display.error(`Backend "${config.backend.default}" is not available. Install claude or codex.`);
|
|
84
|
+
createBlank(name, state);
|
|
85
|
+
display.warn('Created blank spec instead.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const prompt = `Create a detailed feature spec in markdown format for the following feature:
|
|
89
|
+
|
|
90
|
+
Name: ${name}
|
|
91
|
+
Description: ${desc}
|
|
92
|
+
|
|
93
|
+
Output format:
|
|
94
|
+
# Feature: ${name}
|
|
95
|
+
|
|
96
|
+
## Context
|
|
97
|
+
(2-3 sentences of background)
|
|
98
|
+
|
|
99
|
+
## Requirements
|
|
100
|
+
(Numbered list with acceptance criteria)
|
|
101
|
+
|
|
102
|
+
## Constraints
|
|
103
|
+
(Technical limitations)
|
|
104
|
+
|
|
105
|
+
## Notes
|
|
106
|
+
(Additional notes)`;
|
|
107
|
+
const result = await backend.execute(prompt);
|
|
108
|
+
fs.writeFileSync(state.specPath(name), result.output, 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
display.warn(`AI generation failed: ${err}. Creating blank spec.`);
|
|
112
|
+
createBlank(name, state);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function createFromJira(name, tickets, state) {
|
|
116
|
+
display.info(`Fetching Jira tickets: ${tickets.join(', ')}...`);
|
|
117
|
+
try {
|
|
118
|
+
const config = state.readConfig();
|
|
119
|
+
const backend = createBackend(config.backend.default);
|
|
120
|
+
if (!(await backend.isAvailable())) {
|
|
121
|
+
display.error(`Backend "${config.backend.default}" is not available.`);
|
|
122
|
+
createBlank(name, state);
|
|
123
|
+
display.warn('Created blank spec instead.');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const ticketList = tickets.map((t) => `- ${t}`).join('\n');
|
|
127
|
+
const prompt = `You have access to a Jira MCP server. Use it to read the following tickets and generate a feature spec.
|
|
128
|
+
|
|
129
|
+
Tickets:
|
|
130
|
+
${ticketList}
|
|
131
|
+
|
|
132
|
+
For each ticket, retrieve: summary, description, acceptance criteria, labels, and priority.
|
|
133
|
+
|
|
134
|
+
Then combine the information into a single feature spec in this format:
|
|
135
|
+
|
|
136
|
+
# Feature: ${name}
|
|
137
|
+
|
|
138
|
+
## Context
|
|
139
|
+
(2-3 sentences of background, derived from ticket descriptions)
|
|
140
|
+
|
|
141
|
+
## Requirements
|
|
142
|
+
(Numbered list with acceptance criteria, merged from all tickets)
|
|
143
|
+
|
|
144
|
+
## Constraints
|
|
145
|
+
(Technical limitations mentioned in tickets)
|
|
146
|
+
|
|
147
|
+
## Source Tickets
|
|
148
|
+
${ticketList}
|
|
149
|
+
|
|
150
|
+
## Notes
|
|
151
|
+
(Additional notes from ticket comments or labels)
|
|
152
|
+
|
|
153
|
+
If you cannot access Jira MCP, output the spec template with the ticket IDs listed so the user can fill in details manually.`;
|
|
154
|
+
const result = await backend.execute(prompt);
|
|
155
|
+
fs.writeFileSync(state.specPath(name), result.output, 'utf-8');
|
|
156
|
+
display.success(`Generated spec from ${tickets.length} Jira ticket(s).`);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
display.warn(`Jira integration failed: ${err}. Creating blank spec.`);
|
|
160
|
+
createBlank(name, state);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function createInteractive(name, state) {
|
|
164
|
+
const answers = await inquirer.prompt([
|
|
165
|
+
{ type: 'input', name: 'context', message: 'Project context (2-3 sentences):' },
|
|
166
|
+
{ type: 'editor', name: 'requirements', message: 'Requirements (one per line):' },
|
|
167
|
+
{ type: 'input', name: 'constraints', message: 'Constraints:' },
|
|
168
|
+
{ type: 'input', name: 'notes', message: 'Additional notes:' },
|
|
169
|
+
]);
|
|
170
|
+
const spec = `# Feature: ${name}
|
|
171
|
+
|
|
172
|
+
## Context
|
|
173
|
+
${answers.context}
|
|
174
|
+
|
|
175
|
+
## Requirements
|
|
176
|
+
${answers.requirements}
|
|
177
|
+
|
|
178
|
+
## Constraints
|
|
179
|
+
${answers.constraints}
|
|
180
|
+
|
|
181
|
+
## Notes
|
|
182
|
+
${answers.notes}
|
|
183
|
+
`;
|
|
184
|
+
fs.writeFileSync(state.specPath(name), spec, 'utf-8');
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { StateManager } from '../state/manager.js';
|
|
4
|
+
import { createBackend } from '../backends/factory.js';
|
|
5
|
+
import { assemblePrompt } from '../utils/prompt.js';
|
|
6
|
+
import { parsePlan } from '../parsers/plan.js';
|
|
7
|
+
import * as display from '../utils/display.js';
|
|
8
|
+
export async function refineCommand(name, args, options) {
|
|
9
|
+
const state = new StateManager();
|
|
10
|
+
state.ensureWorkflow();
|
|
11
|
+
state.checkPhaseGuard('refine', name);
|
|
12
|
+
const specPath = state.specPath(name);
|
|
13
|
+
if (!fs.existsSync(specPath)) {
|
|
14
|
+
display.error(`Spec "${name}" not found. Run \`pxs new ${name}\` first.`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const config = state.readConfig();
|
|
18
|
+
const backend = createBackend(config.backend.default);
|
|
19
|
+
if (!(await backend.isAvailable())) {
|
|
20
|
+
display.error(`Backend "${config.backend.default}" not available.`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let specContent = fs.readFileSync(specPath, 'utf-8');
|
|
24
|
+
const feature = state.getFeature(name);
|
|
25
|
+
let sessionId = feature.session?.id ?? '';
|
|
26
|
+
// Sub-flow 1: Clarify (unless skipped)
|
|
27
|
+
if (!options.skipClarify) {
|
|
28
|
+
display.heading('Phase 1: Requirement Clarification');
|
|
29
|
+
const clarifyPrompt = assemblePrompt({
|
|
30
|
+
templateName: 'clarify',
|
|
31
|
+
vars: { spec_content: specContent },
|
|
32
|
+
agents: args.agents,
|
|
33
|
+
skills: args.skills,
|
|
34
|
+
extraText: args.text,
|
|
35
|
+
});
|
|
36
|
+
const clarifyResult = sessionId
|
|
37
|
+
? await backend.resume(sessionId, clarifyPrompt)
|
|
38
|
+
: await backend.execute(clarifyPrompt);
|
|
39
|
+
sessionId = clarifyResult.sessionId;
|
|
40
|
+
console.log('\n' + clarifyResult.output);
|
|
41
|
+
feature.phase = 'clarifying';
|
|
42
|
+
feature.session = { backend: config.backend.default, id: sessionId };
|
|
43
|
+
state.upsertFeature(feature);
|
|
44
|
+
// Wait for user to answer questions (in CLI mode, AI handles the conversation)
|
|
45
|
+
const { proceed } = await inquirer.prompt([
|
|
46
|
+
{
|
|
47
|
+
type: 'confirm',
|
|
48
|
+
name: 'proceed',
|
|
49
|
+
message: 'Proceed to spec refinement?',
|
|
50
|
+
default: true,
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
if (!proceed) {
|
|
54
|
+
display.info('Paused. Run `pxs refine` again when ready.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Sub-flow 2: Refine spec
|
|
59
|
+
display.heading('Phase 2: Refine Spec');
|
|
60
|
+
specContent = fs.readFileSync(specPath, 'utf-8');
|
|
61
|
+
const refinePrompt = assemblePrompt({
|
|
62
|
+
templateName: 'refine',
|
|
63
|
+
vars: { spec_content: specContent },
|
|
64
|
+
agents: args.agents,
|
|
65
|
+
skills: args.skills,
|
|
66
|
+
extraText: args.text,
|
|
67
|
+
});
|
|
68
|
+
const refineResult = sessionId
|
|
69
|
+
? await backend.resume(sessionId, refinePrompt)
|
|
70
|
+
: await backend.execute(refinePrompt);
|
|
71
|
+
sessionId = refineResult.sessionId;
|
|
72
|
+
// Save refined spec
|
|
73
|
+
fs.writeFileSync(specPath, refineResult.output, 'utf-8');
|
|
74
|
+
display.success(`Refined spec saved to ${specPath}`);
|
|
75
|
+
console.log('\n' + refineResult.output.slice(0, 500) + '...\n');
|
|
76
|
+
feature.phase = 'spec_approved';
|
|
77
|
+
feature.session = { backend: config.backend.default, id: sessionId };
|
|
78
|
+
state.upsertFeature(feature);
|
|
79
|
+
const { approveSpec } = await inquirer.prompt([
|
|
80
|
+
{
|
|
81
|
+
type: 'list',
|
|
82
|
+
name: 'approveSpec',
|
|
83
|
+
message: 'Spec refinement:',
|
|
84
|
+
choices: ['approve', 'edit'],
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
if (approveSpec === 'edit') {
|
|
88
|
+
display.info(`Edit ${specPath} and run \`pxs refine ${name} --skip-clarify\``);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Sub-flow 3: Decompose plan
|
|
92
|
+
display.heading('Phase 3: Decompose Plan');
|
|
93
|
+
specContent = fs.readFileSync(specPath, 'utf-8');
|
|
94
|
+
const planPrompt = `Decompose the following refined spec into an implementation plan with discrete tasks.
|
|
95
|
+
|
|
96
|
+
${specContent}
|
|
97
|
+
|
|
98
|
+
Output the plan in this exact format:
|
|
99
|
+
# Implementation Plan: ${name}
|
|
100
|
+
|
|
101
|
+
> type: feat
|
|
102
|
+
> branch: feat/${name}
|
|
103
|
+
> total_tasks: N
|
|
104
|
+
|
|
105
|
+
## Task 1: <title>
|
|
106
|
+
- **Files**:
|
|
107
|
+
- \`path/to/file\` (new|modify)
|
|
108
|
+
- **Description**: What to implement
|
|
109
|
+
- **Depends on**: None | Task N
|
|
110
|
+
- **Complexity**: Low | Medium | High
|
|
111
|
+
- **Acceptance**: Definition of done
|
|
112
|
+
|
|
113
|
+
(repeat for each task)`;
|
|
114
|
+
const planResult = sessionId
|
|
115
|
+
? await backend.resume(sessionId, planPrompt)
|
|
116
|
+
: await backend.execute(planPrompt);
|
|
117
|
+
sessionId = planResult.sessionId;
|
|
118
|
+
// Save plan
|
|
119
|
+
fs.mkdirSync(state.plansDir(), { recursive: true });
|
|
120
|
+
const planPath = state.planPath(name);
|
|
121
|
+
fs.writeFileSync(planPath, planResult.output, 'utf-8');
|
|
122
|
+
display.success(`Plan saved to ${planPath}`);
|
|
123
|
+
console.log('\n' + planResult.output + '\n');
|
|
124
|
+
// Parse plan for state
|
|
125
|
+
const plan = parsePlan(planResult.output);
|
|
126
|
+
feature.phase = 'plan_pending_approval';
|
|
127
|
+
feature.session = { backend: config.backend.default, id: sessionId };
|
|
128
|
+
state.upsertFeature(feature);
|
|
129
|
+
const { approvePlan } = await inquirer.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: 'list',
|
|
132
|
+
name: 'approvePlan',
|
|
133
|
+
message: 'Implementation plan:',
|
|
134
|
+
choices: ['approve', 'edit', 're-split'],
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
if (approvePlan === 'edit') {
|
|
138
|
+
display.info(`Edit ${planPath} and run \`pxs refine ${name} --skip-clarify\``);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (approvePlan === 're-split') {
|
|
142
|
+
display.info('Re-splitting not yet implemented. Edit the plan manually.');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Approve: update state
|
|
146
|
+
feature.type = plan.type;
|
|
147
|
+
feature.branch = plan.branch || `${plan.type}/${name}`;
|
|
148
|
+
feature.phase = 'ready_to_implement';
|
|
149
|
+
feature.total_tasks = plan.tasks.length;
|
|
150
|
+
feature.current_task = 0;
|
|
151
|
+
feature.tasks = plan.tasks.map((t) => ({
|
|
152
|
+
name: t.title,
|
|
153
|
+
status: 'pending',
|
|
154
|
+
}));
|
|
155
|
+
state.upsertFeature(feature);
|
|
156
|
+
display.success('Plan approved! Run `pxs implement ' + name + '` to start.');
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=refine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../../src/commands/refine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { StateManager } from '../state/manager.js';
|
|
2
|
+
import * as display from '../utils/display.js';
|
|
3
|
+
const VALID_RESET_TARGETS = [
|
|
4
|
+
'spec_created',
|
|
5
|
+
'clarifying',
|
|
6
|
+
'spec_approved',
|
|
7
|
+
'plan_pending_approval',
|
|
8
|
+
'ready_to_implement',
|
|
9
|
+
];
|
|
10
|
+
export async function resetCommand(name, options) {
|
|
11
|
+
const state = new StateManager();
|
|
12
|
+
state.ensureWorkflow();
|
|
13
|
+
const feature = state.getFeature(name);
|
|
14
|
+
if (!feature) {
|
|
15
|
+
display.error(`Feature "${name}" not found.`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const targetPhase = (options.to ?? 'spec_created');
|
|
19
|
+
if (!VALID_RESET_TARGETS.includes(targetPhase)) {
|
|
20
|
+
display.error(`Cannot reset to "${targetPhase}". Valid targets: ${VALID_RESET_TARGETS.join(', ')}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const previousPhase = feature.phase;
|
|
24
|
+
feature.phase = targetPhase;
|
|
25
|
+
// Clear downstream state depending on target
|
|
26
|
+
if (targetPhase === 'spec_created' || targetPhase === 'clarifying'
|
|
27
|
+
|| targetPhase === 'spec_approved' || targetPhase === 'plan_pending_approval') {
|
|
28
|
+
feature.tasks = [];
|
|
29
|
+
feature.total_tasks = 0;
|
|
30
|
+
feature.current_task = 0;
|
|
31
|
+
feature.session = undefined;
|
|
32
|
+
}
|
|
33
|
+
if (targetPhase === 'ready_to_implement') {
|
|
34
|
+
// Reset task statuses to pending
|
|
35
|
+
for (const task of feature.tasks) {
|
|
36
|
+
task.status = 'pending';
|
|
37
|
+
}
|
|
38
|
+
feature.current_task = 0;
|
|
39
|
+
feature.session = undefined;
|
|
40
|
+
}
|
|
41
|
+
state.upsertFeature(feature);
|
|
42
|
+
display.success(`Reset "${name}": ${previousPhase} → ${targetPhase}`);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=reset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.js","sourceRoot":"","sources":["../../../src/commands/reset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAE/C,MAAM,mBAAmB,GAAY;IACnC,cAAc;IACd,YAAY;IACZ,eAAe;IACf,uBAAuB;IACvB,oBAAoB;CACrB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAY,EACZ,OAAwB;IAExB,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IACjC,KAAK,CAAC,cAAc,EAAE,CAAC;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,YAAY,IAAI,cAAc,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,cAAc,CAAU,CAAC;IAE5D,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,KAAK,CACX,oBAAoB,WAAW,qBAAqB,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;IACpC,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC;IAE5B,6CAA6C;IAC7C,IAAI,WAAW,KAAK,cAAc,IAAI,WAAW,KAAK,YAAY;WAC7D,WAAW,KAAK,eAAe,IAAI,WAAW,KAAK,uBAAuB,EAAE,CAAC;QAChF,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC;QACxB,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC;QACzB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,IAAI,WAAW,KAAK,oBAAoB,EAAE,CAAC;QACzC,iCAAiC;QACjC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QAC1B,CAAC;QACD,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC;QACzB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,UAAU,IAAI,MAAM,aAAa,MAAM,WAAW,EAAE,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { StateManager } from '../state/manager.js';
|
|
4
|
+
import * as display from '../utils/display.js';
|
|
5
|
+
export async function reviewCommand(name, options) {
|
|
6
|
+
const state = new StateManager();
|
|
7
|
+
state.ensureWorkflow();
|
|
8
|
+
state.checkPhaseGuard('review', name);
|
|
9
|
+
const feature = state.getFeature(name);
|
|
10
|
+
if (!feature) {
|
|
11
|
+
display.error(`Feature "${name}" not found.`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (options.summary) {
|
|
15
|
+
display.heading(`Review Summary: ${name}`);
|
|
16
|
+
for (let i = 0; i < feature.tasks.length; i++) {
|
|
17
|
+
const t = feature.tasks[i];
|
|
18
|
+
const reviewPath = state.reviewPath(name, i + 1);
|
|
19
|
+
const hasReview = fs.existsSync(reviewPath);
|
|
20
|
+
console.log(` ${i + 1}. ${display.taskIcon(t.status)} ${t.name} ${hasReview ? '(reviewed)' : ''}`);
|
|
21
|
+
}
|
|
22
|
+
// Show final review status
|
|
23
|
+
const finalReviewPath = path.join(state.reviewsDir(), `${name}-final.md`);
|
|
24
|
+
if (fs.existsSync(finalReviewPath)) {
|
|
25
|
+
console.log(`\n Final branch review: exists`);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (options.step !== undefined) {
|
|
30
|
+
const reviewPath = state.reviewPath(name, options.step);
|
|
31
|
+
if (!fs.existsSync(reviewPath)) {
|
|
32
|
+
display.error(`Review for task ${options.step} not found.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const content = fs.readFileSync(reviewPath, 'utf-8');
|
|
36
|
+
display.heading(`Review: ${name} - Task ${options.step}`);
|
|
37
|
+
console.log(content);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Default: if completed, show final review first
|
|
41
|
+
if (feature.phase === 'completed' || feature.phase === 'merged') {
|
|
42
|
+
const finalReviewPath = path.join(state.reviewsDir(), `${name}-final.md`);
|
|
43
|
+
if (fs.existsSync(finalReviewPath)) {
|
|
44
|
+
display.heading(`Final Branch Review: ${name}`);
|
|
45
|
+
console.log(fs.readFileSync(finalReviewPath, 'utf-8'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Show most recent or pending review
|
|
50
|
+
const pendingIdx = feature.tasks.findIndex((t) => t.status === 'review_pending');
|
|
51
|
+
if (pendingIdx >= 0) {
|
|
52
|
+
const reviewPath = state.reviewPath(name, pendingIdx + 1);
|
|
53
|
+
if (fs.existsSync(reviewPath)) {
|
|
54
|
+
display.heading(`Pending Review: Task ${pendingIdx + 1}`);
|
|
55
|
+
console.log(fs.readFileSync(reviewPath, 'utf-8'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Show last completed task's review
|
|
60
|
+
for (let i = feature.tasks.length - 1; i >= 0; i--) {
|
|
61
|
+
const reviewPath = state.reviewPath(name, i + 1);
|
|
62
|
+
if (fs.existsSync(reviewPath)) {
|
|
63
|
+
display.heading(`Latest Review: Task ${i + 1}`);
|
|
64
|
+
console.log(fs.readFileSync(reviewPath, 'utf-8'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
display.info('No reviews found.');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/commands/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statusCommand(name?: string): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { StateManager } from '../state/manager.js';
|
|
2
|
+
import * as display from '../utils/display.js';
|
|
3
|
+
export async function statusCommand(name) {
|
|
4
|
+
const state = new StateManager();
|
|
5
|
+
if (!state.workflowExists()) {
|
|
6
|
+
display.error('Workflow not initialized. Run `pxs init` first.');
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const workflowState = state.readState();
|
|
10
|
+
if (!name) {
|
|
11
|
+
// List all features
|
|
12
|
+
if (workflowState.features.length === 0) {
|
|
13
|
+
display.info('No features tracked. Run `pxs new <name>` to start.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
display.heading('Workflow Status');
|
|
17
|
+
display.table(['Feature', 'Type', 'Phase', 'Tasks', 'Branch'], workflowState.features.map((f) => {
|
|
18
|
+
const completedTasks = f.tasks.filter((t) => t.status === 'complete').length;
|
|
19
|
+
return [
|
|
20
|
+
f.feature,
|
|
21
|
+
f.type,
|
|
22
|
+
f.phase,
|
|
23
|
+
`${completedTasks}/${f.total_tasks}`,
|
|
24
|
+
f.branch || '-',
|
|
25
|
+
];
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Show specific feature
|
|
30
|
+
const feature = state.getFeature(name);
|
|
31
|
+
if (!feature) {
|
|
32
|
+
display.error(`Feature "${name}" not found.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
display.heading(`Feature: ${feature.feature}`);
|
|
36
|
+
console.log(` Type: ${feature.type}`);
|
|
37
|
+
console.log(` Branch: ${feature.branch || '-'}`);
|
|
38
|
+
console.log(` Phase: ${feature.phase}`);
|
|
39
|
+
if (feature.session) {
|
|
40
|
+
console.log(` Backend: ${feature.session.backend}`);
|
|
41
|
+
console.log(` Session: ${feature.session.id}`);
|
|
42
|
+
}
|
|
43
|
+
if (feature.tasks.length > 0) {
|
|
44
|
+
console.log('\n Tasks:');
|
|
45
|
+
for (let i = 0; i < feature.tasks.length; i++) {
|
|
46
|
+
const t = feature.tasks[i];
|
|
47
|
+
const icon = display.taskIcon(t.status);
|
|
48
|
+
console.log(` ${i + 1}. ${icon} ${t.name} (${t.status})`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/status.ts"],"names":[],"mappings":"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"}
|