@bamptee/aia-code 2.0.1 → 2.0.3
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/README.md +55 -4
- package/package.json +7 -1
- package/src/commands/init.js +11 -1
- package/src/models.js +19 -2
- package/src/prompt-builder.js +33 -1
- package/src/providers/anthropic.js +1 -1
- package/src/providers/cli-runner.js +2 -1
- package/src/services/config.js +53 -2
- package/src/services/flow-analyzer.js +32 -0
- package/src/services/runner.js +2 -2
- package/src/services/status.js +14 -0
- package/src/services/suggestions.js +166 -0
- package/src/services/worktrunk.js +197 -0
- package/src/ui/api/config.js +55 -2
- package/src/ui/api/features.js +301 -6
- package/src/ui/api/index.js +2 -0
- package/src/ui/api/worktrunk.js +153 -0
- package/src/ui/public/components/config-view.js +196 -2
- package/src/ui/public/components/dashboard.js +64 -4
- package/src/ui/public/components/feature-detail.js +584 -94
- package/src/ui/public/components/terminal.js +197 -0
- package/src/ui/public/components/worktrunk-panel.js +205 -0
- package/src/ui/public/main.js +23 -1
- package/src/ui/router.js +1 -1
- package/src/ui/server.js +85 -0
- package/src/utils/prompt.js +38 -0
package/README.md
CHANGED
|
@@ -162,7 +162,58 @@ Required templates (one per step you want to run):
|
|
|
162
162
|
.aia/prompts/review.md
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
### 6.
|
|
165
|
+
### 6. Configuration (user + project)
|
|
166
|
+
|
|
167
|
+
AIA uses two configuration files:
|
|
168
|
+
|
|
169
|
+
| File | Scope | Content |
|
|
170
|
+
|------|-------|---------|
|
|
171
|
+
| `~/.aia/config.yaml` | **User (global)** | user_name, communication_language |
|
|
172
|
+
| `.aia/config.yaml` | **Project** | projectName, document_output_language, models, knowledge_default, context_files |
|
|
173
|
+
|
|
174
|
+
When you run AIA, both configs are merged (user preferences + project config).
|
|
175
|
+
|
|
176
|
+
#### User config (`~/.aia/config.yaml`)
|
|
177
|
+
|
|
178
|
+
Your personal preferences, created automatically on first use:
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
# ~/.aia/config.yaml
|
|
182
|
+
user_name: John Doe
|
|
183
|
+
communication_language: French
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- **user_name**: Your name (shown to the AI for context)
|
|
187
|
+
- **communication_language**: Language for AI responses and questions
|
|
188
|
+
|
|
189
|
+
These are stored outside the project, so they're never committed to git.
|
|
190
|
+
|
|
191
|
+
#### Project config (`.aia/config.yaml`)
|
|
192
|
+
|
|
193
|
+
Shared project settings:
|
|
194
|
+
|
|
195
|
+
```yaml
|
|
196
|
+
# .aia/config.yaml
|
|
197
|
+
projectName: My Project
|
|
198
|
+
document_output_language: English
|
|
199
|
+
models:
|
|
200
|
+
# ...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- **document_output_language**: Language for generated documents (specs, plans, etc.) - shared by the whole team
|
|
204
|
+
|
|
205
|
+
#### .gitignore recommendation
|
|
206
|
+
|
|
207
|
+
User preferences are stored in `~/.aia/config.yaml` (outside the project), so nothing extra is needed in `.gitignore`.
|
|
208
|
+
|
|
209
|
+
If you want to ignore local project overrides, add to your `.gitignore`:
|
|
210
|
+
|
|
211
|
+
```gitignore
|
|
212
|
+
# AIA - ignore local overrides
|
|
213
|
+
.aia/local.yaml
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 7. Configure models (project config)
|
|
166
217
|
|
|
167
218
|
In `config.yaml`, assign models to steps with probability weights:
|
|
168
219
|
|
|
@@ -210,7 +261,7 @@ Use aliases to delegate to the CLI's default model:
|
|
|
210
261
|
| `gpt-*`, `o[0-9]*` | `codex exec` | `gpt-4.1`, `o3`, `o4-mini` |
|
|
211
262
|
| `gemini-*` | `gemini` | `gemini-2.5-pro`, `gemini-2.5-flash` |
|
|
212
263
|
|
|
213
|
-
###
|
|
264
|
+
### 8. Run the feature pipeline
|
|
214
265
|
|
|
215
266
|
#### Step by step
|
|
216
267
|
|
|
@@ -315,7 +366,7 @@ The `init.md` file serves as the sole input context for the dev-plan step. Verbo
|
|
|
315
366
|
aia quick add-rate-limit "Add rate limiting to the /api/upload endpoint" -v
|
|
316
367
|
```
|
|
317
368
|
|
|
318
|
-
###
|
|
369
|
+
### 9. Print mode vs Agent mode
|
|
319
370
|
|
|
320
371
|
By default, AIA runs in **print mode** -- the AI generates text (specs, plans, reviews) saved to `.md` files.
|
|
321
372
|
|
|
@@ -341,7 +392,7 @@ The `implement` step always runs in agent mode automatically.
|
|
|
341
392
|
|
|
342
393
|
Idle timeout resets every time the CLI produces output, so long-running steps that stream continuously won't time out.
|
|
343
394
|
|
|
344
|
-
###
|
|
395
|
+
### 10. Scan your repo
|
|
345
396
|
|
|
346
397
|
```bash
|
|
347
398
|
aia repo scan
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamptee/aia-code",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "AI Architecture Assistant - orchestrate AI-assisted development workflows via CLI tools (Claude, Codex, Gemini)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,9 +33,15 @@
|
|
|
33
33
|
"start": "node bin/aia.js"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"@iarna/toml": "^2.2.5",
|
|
37
|
+
"busboy": "^1.6.0",
|
|
36
38
|
"chalk": "^5.3.0",
|
|
37
39
|
"commander": "^12.1.0",
|
|
38
40
|
"fs-extra": "^11.2.0",
|
|
41
|
+
"node-pty": "^1.0.0",
|
|
42
|
+
"ws": "^8.18.0",
|
|
43
|
+
"xterm": "^5.3.0",
|
|
44
|
+
"xterm-addon-fit": "^0.8.0",
|
|
39
45
|
"yaml": "^2.7.0"
|
|
40
46
|
}
|
|
41
47
|
}
|
package/src/commands/init.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { createAiaStructure } from '../services/scaffold.js';
|
|
3
|
-
import { writeDefaultConfig } from '../services/config.js';
|
|
3
|
+
import { writeDefaultConfig, isGlobalConfigured, updateGlobalConfig } from '../services/config.js';
|
|
4
4
|
import { writeDefaultPrompts } from '../services/prompts.js';
|
|
5
5
|
import { AIA_DIR } from '../constants.js';
|
|
6
|
+
import { ask, askRequired, isInteractive } from '../utils/prompt.js';
|
|
6
7
|
|
|
7
8
|
export function registerInitCommand(program) {
|
|
8
9
|
program
|
|
@@ -10,6 +11,15 @@ export function registerInitCommand(program) {
|
|
|
10
11
|
.description('Initialize .aia folder structure, default config, and prompt templates')
|
|
11
12
|
.action(async () => {
|
|
12
13
|
try {
|
|
14
|
+
// First-time user setup
|
|
15
|
+
if (!(await isGlobalConfigured()) && isInteractive()) {
|
|
16
|
+
console.log(chalk.cyan('First time setup - let\'s configure your preferences.\n'));
|
|
17
|
+
const name = await askRequired('Your name');
|
|
18
|
+
const lang = await ask('Communication language', 'English');
|
|
19
|
+
await updateGlobalConfig({ user_name: name, communication_language: lang });
|
|
20
|
+
console.log(chalk.green('\nUser preferences saved to ~/.aia/config.yaml\n'));
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
await createAiaStructure();
|
|
14
24
|
await writeDefaultConfig();
|
|
15
25
|
await writeDefaultPrompts();
|
package/src/models.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import yaml from 'yaml';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { AIA_DIR } from './constants.js';
|
|
6
|
+
import { loadGlobalConfig } from './services/config.js';
|
|
6
7
|
|
|
7
8
|
export async function loadConfig(root = process.cwd()) {
|
|
8
9
|
const configPath = path.join(root, AIA_DIR, 'config.yaml');
|
|
@@ -12,13 +13,29 @@ export async function loadConfig(root = process.cwd()) {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const raw = await fs.readFile(configPath, 'utf-8');
|
|
15
|
-
const
|
|
16
|
+
const projectConfig = yaml.parse(raw);
|
|
16
17
|
|
|
17
|
-
validateConfig(
|
|
18
|
+
validateConfig(projectConfig, configPath);
|
|
19
|
+
|
|
20
|
+
// Merge with global user config
|
|
21
|
+
const globalConfig = await loadGlobalConfig();
|
|
22
|
+
const config = { ...globalConfig, ...projectConfig };
|
|
18
23
|
|
|
19
24
|
return config;
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
// Load only project config (without global merge)
|
|
28
|
+
export async function loadProjectConfig(root = process.cwd()) {
|
|
29
|
+
const configPath = path.join(root, AIA_DIR, 'config.yaml');
|
|
30
|
+
|
|
31
|
+
if (!(await fs.pathExists(configPath))) {
|
|
32
|
+
throw new Error(`Config not found: ${configPath}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const raw = await fs.readFile(configPath, 'utf-8');
|
|
36
|
+
return yaml.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
function validateConfig(config, configPath) {
|
|
23
40
|
if (!config || typeof config !== 'object') {
|
|
24
41
|
throw new Error(`Invalid config: ${configPath} must be a YAML object.`);
|
package/src/prompt-builder.js
CHANGED
|
@@ -77,7 +77,7 @@ async function loadPromptTemplate(step, root) {
|
|
|
77
77
|
return content;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export async function buildPrompt(feature, step, { description, instructions, root = process.cwd() } = {}) {
|
|
80
|
+
export async function buildPrompt(feature, step, { description, instructions, history, attachments, root = process.cwd() } = {}) {
|
|
81
81
|
const config = await loadConfig(root);
|
|
82
82
|
|
|
83
83
|
const [context, knowledgeCategories, initSpecs, featureContent, previousOutput, task] = await Promise.all([
|
|
@@ -95,12 +95,41 @@ export async function buildPrompt(feature, step, { description, instructions, ro
|
|
|
95
95
|
|
|
96
96
|
parts.push('IMPORTANT: You are working on a feature development pipeline. Everything you need is provided below in this prompt. Do NOT attempt to read, search for, or reference any external files. Do NOT say files are missing. Work exclusively with the content given below.\n');
|
|
97
97
|
|
|
98
|
+
// Inject user preferences - document language is the ONLY thing that matters for output
|
|
99
|
+
const docLang = config.document_output_language || 'English';
|
|
100
|
+
|
|
101
|
+
parts.push('=== OUTPUT LANGUAGE ===\n');
|
|
102
|
+
parts.push(`Write ALL your output in ${docLang}. This is mandatory and non-negotiable.`);
|
|
103
|
+
parts.push(`Do NOT use any other language for the document content.\n`);
|
|
104
|
+
|
|
105
|
+
// Add conversation history if present (for multi-turn)
|
|
106
|
+
if (history && history.length > 0) {
|
|
107
|
+
parts.push('=== CONVERSATION HISTORY ===\n');
|
|
108
|
+
for (const msg of history) {
|
|
109
|
+
const prefix = msg.role === 'user' ? 'User' : 'Agent';
|
|
110
|
+
parts.push(`${prefix}: ${msg.content}`);
|
|
111
|
+
}
|
|
112
|
+
parts.push('');
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
if (description) {
|
|
99
116
|
parts.push('=== DESCRIPTION ===\n');
|
|
100
117
|
parts.push(description);
|
|
101
118
|
parts.push('');
|
|
102
119
|
}
|
|
103
120
|
|
|
121
|
+
if (attachments && attachments.length > 0) {
|
|
122
|
+
parts.push('=== ATTACHMENTS ===\n');
|
|
123
|
+
parts.push('The user has attached the following files. Use the Read tool to view them:\n');
|
|
124
|
+
for (const a of attachments) {
|
|
125
|
+
// F11: Sanitize filename to prevent prompt injection
|
|
126
|
+
const safeFilename = String(a.filename || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 255);
|
|
127
|
+
const safePath = String(a.path || '').slice(0, 1000);
|
|
128
|
+
parts.push(`- ${safeFilename}: ${safePath}`);
|
|
129
|
+
}
|
|
130
|
+
parts.push('');
|
|
131
|
+
}
|
|
132
|
+
|
|
104
133
|
if (context) {
|
|
105
134
|
parts.push('=== CONTEXT ===\n');
|
|
106
135
|
parts.push(context);
|
|
@@ -136,5 +165,8 @@ export async function buildPrompt(feature, step, { description, instructions, ro
|
|
|
136
165
|
parts.push('\n\n=== TASK ===\n');
|
|
137
166
|
parts.push(task);
|
|
138
167
|
|
|
168
|
+
// Add language reminder at the end (always, to reinforce)
|
|
169
|
+
parts.push(`\n\n---\nREMINDER: Your entire output MUST be written in ${docLang}.`);
|
|
170
|
+
|
|
139
171
|
return parts.join('\n');
|
|
140
172
|
}
|
|
@@ -6,7 +6,7 @@ export async function generate(prompt, model, { verbose = false, apply = false,
|
|
|
6
6
|
args.push('--model', model);
|
|
7
7
|
}
|
|
8
8
|
if (apply) {
|
|
9
|
-
args.push('--allowedTools', 'Edit
|
|
9
|
+
args.push('--allowedTools', 'Edit,Write,Bash,Read,Glob,Grep');
|
|
10
10
|
}
|
|
11
11
|
if (verbose || apply) {
|
|
12
12
|
args.push('--verbose');
|
|
@@ -9,9 +9,10 @@ export function runCli(command, args, { stdin: stdinData, verbose = false, apply
|
|
|
9
9
|
idleTimeoutMs = apply ? AGENT_IDLE_TIMEOUT_MS : DEFAULT_IDLE_TIMEOUT_MS;
|
|
10
10
|
}
|
|
11
11
|
return new Promise((resolve, reject) => {
|
|
12
|
+
const { CLAUDECODE, ...cleanEnv } = process.env;
|
|
12
13
|
const child = spawn(command, args, {
|
|
13
14
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
-
env: { ...
|
|
15
|
+
env: { ...cleanEnv, FORCE_COLOR: '0' },
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
const chunks = [];
|
package/src/services/config.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import fs from 'fs-extra';
|
|
3
4
|
import yaml from 'yaml';
|
|
4
5
|
import { AIA_DIR } from '../constants.js';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// Global user config directory
|
|
8
|
+
const GLOBAL_AIA_DIR = path.join(os.homedir(), '.aia');
|
|
9
|
+
const GLOBAL_CONFIG_PATH = path.join(GLOBAL_AIA_DIR, 'config.yaml');
|
|
10
|
+
|
|
11
|
+
// Default config for PROJECT (.aia/config.yaml)
|
|
12
|
+
const DEFAULT_PROJECT_CONFIG = {
|
|
7
13
|
projectName: 'My Project',
|
|
14
|
+
document_output_language: 'English',
|
|
8
15
|
models: {
|
|
9
16
|
brief: [
|
|
10
17
|
{ model: 'claude-default', weight: 1 },
|
|
@@ -42,6 +49,12 @@ const DEFAULT_CONFIG = {
|
|
|
42
49
|
],
|
|
43
50
|
};
|
|
44
51
|
|
|
52
|
+
// Default config for USER (~/.aia/config.yaml)
|
|
53
|
+
const DEFAULT_USER_CONFIG = {
|
|
54
|
+
user_name: '',
|
|
55
|
+
communication_language: 'English',
|
|
56
|
+
};
|
|
57
|
+
|
|
45
58
|
export async function writeDefaultConfig(root = process.cwd()) {
|
|
46
59
|
const configPath = path.join(root, AIA_DIR, 'config.yaml');
|
|
47
60
|
|
|
@@ -49,6 +62,44 @@ export async function writeDefaultConfig(root = process.cwd()) {
|
|
|
49
62
|
return;
|
|
50
63
|
}
|
|
51
64
|
|
|
52
|
-
const content = yaml.stringify(
|
|
65
|
+
const content = yaml.stringify(DEFAULT_PROJECT_CONFIG);
|
|
53
66
|
await fs.writeFile(configPath, content, 'utf-8');
|
|
54
67
|
}
|
|
68
|
+
|
|
69
|
+
export async function ensureGlobalConfig() {
|
|
70
|
+
await fs.ensureDir(GLOBAL_AIA_DIR);
|
|
71
|
+
|
|
72
|
+
if (!(await fs.pathExists(GLOBAL_CONFIG_PATH))) {
|
|
73
|
+
const content = yaml.stringify(DEFAULT_USER_CONFIG);
|
|
74
|
+
await fs.writeFile(GLOBAL_CONFIG_PATH, content, 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function loadGlobalConfig() {
|
|
79
|
+
await ensureGlobalConfig();
|
|
80
|
+
|
|
81
|
+
const raw = await fs.readFile(GLOBAL_CONFIG_PATH, 'utf-8');
|
|
82
|
+
return yaml.parse(raw) || {};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function saveGlobalConfig(config) {
|
|
86
|
+
await fs.ensureDir(GLOBAL_AIA_DIR);
|
|
87
|
+
const content = yaml.stringify(config);
|
|
88
|
+
await fs.writeFile(GLOBAL_CONFIG_PATH, content, 'utf-8');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function isGlobalConfigured() {
|
|
92
|
+
const config = await loadGlobalConfig();
|
|
93
|
+
return config && config.user_name && config.user_name.trim() !== '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function updateGlobalConfig(updates) {
|
|
97
|
+
const existing = await loadGlobalConfig();
|
|
98
|
+
const merged = { ...existing, ...updates };
|
|
99
|
+
await saveGlobalConfig(merged);
|
|
100
|
+
return merged;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getGlobalConfigPath() {
|
|
104
|
+
return GLOBAL_CONFIG_PATH;
|
|
105
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const COMPLEXITY_INDICATORS = {
|
|
2
|
+
simple: ['fix', 'bug', 'typo', 'update', 'small', 'quick', 'minor', 'patch', 'hotfix', 'tweak'],
|
|
3
|
+
complex: ['refactor', 'architecture', 'migration', 'redesign', 'integration', 'new feature', 'complex', 'rewrite', 'overhaul', 'system']
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function suggestFlowType(description) {
|
|
7
|
+
if (!description || description.length < 50) {
|
|
8
|
+
return 'quick';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const lower = description.toLowerCase();
|
|
12
|
+
let simpleScore = 0;
|
|
13
|
+
let complexScore = 0;
|
|
14
|
+
|
|
15
|
+
COMPLEXITY_INDICATORS.simple.forEach(word => {
|
|
16
|
+
if (lower.includes(word)) simpleScore++;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
COMPLEXITY_INDICATORS.complex.forEach(word => {
|
|
20
|
+
if (lower.includes(word)) complexScore++;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (complexScore > simpleScore) {
|
|
24
|
+
return 'full';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (simpleScore > 0 || description.length < 200) {
|
|
28
|
+
return 'quick';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return 'full';
|
|
32
|
+
}
|
package/src/services/runner.js
CHANGED
|
@@ -8,7 +8,7 @@ import { callModel } from './model-call.js';
|
|
|
8
8
|
import { loadStatus, updateStepStatus } from './status.js';
|
|
9
9
|
import { logExecution } from '../logger.js';
|
|
10
10
|
|
|
11
|
-
export async function runStep(step, feature, { description, instructions, model: modelOverride, verbose = false, apply = false, root = process.cwd(), onData } = {}) {
|
|
11
|
+
export async function runStep(step, feature, { description, instructions, history, attachments, model: modelOverride, verbose = false, apply = false, root = process.cwd(), onData } = {}) {
|
|
12
12
|
if (!FEATURE_STEPS.includes(step)) {
|
|
13
13
|
throw new Error(`Unknown step "${step}". Valid steps: ${FEATURE_STEPS.join(', ')}`);
|
|
14
14
|
}
|
|
@@ -27,7 +27,7 @@ export async function runStep(step, feature, { description, instructions, model:
|
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
const model = modelOverride || await resolveModel(step, root);
|
|
30
|
-
const prompt = await buildPrompt(feature, step, { description, instructions, root });
|
|
30
|
+
const prompt = await buildPrompt(feature, step, { description, instructions, history, attachments, root });
|
|
31
31
|
|
|
32
32
|
const start = performance.now();
|
|
33
33
|
const output = await callModel(model, prompt, { verbose, apply: shouldApply, onData });
|
package/src/services/status.js
CHANGED
|
@@ -46,6 +46,20 @@ export async function updateStepStatus(feature, step, value, root = process.cwd(
|
|
|
46
46
|
await fs.writeFile(statusPath(feature, root), content, 'utf-8');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export async function updateFlowType(feature, flowType, root = process.cwd()) {
|
|
50
|
+
const status = await loadStatus(feature, root);
|
|
51
|
+
status.flow = flowType; // 'quick' or 'full'
|
|
52
|
+
|
|
53
|
+
// Set current_step based on flow type if not already started
|
|
54
|
+
const allPending = Object.values(status.steps).every(s => s === STEP_STATUS.PENDING);
|
|
55
|
+
if (allPending) {
|
|
56
|
+
status.current_step = flowType === 'quick' ? 'dev-plan' : 'brief';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const content = yaml.stringify(status);
|
|
60
|
+
await fs.writeFile(statusPath(feature, root), content, 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
export async function resetStep(feature, step, root = process.cwd()) {
|
|
50
64
|
if (!FEATURE_STEPS.includes(step)) {
|
|
51
65
|
throw new Error(`Unknown step "${step}". Valid steps: ${FEATURE_STEPS.join(', ')}`);
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const STEP_GUIDANCE = {
|
|
2
|
+
en: {
|
|
3
|
+
brief: {
|
|
4
|
+
summary: 'Product brief completed',
|
|
5
|
+
next: 'ba-spec',
|
|
6
|
+
actions: [
|
|
7
|
+
'Review the brief for completeness',
|
|
8
|
+
'Run: aia next <feature>',
|
|
9
|
+
],
|
|
10
|
+
tips: ['If too vague, iterate with more context'],
|
|
11
|
+
},
|
|
12
|
+
'ba-spec': {
|
|
13
|
+
summary: 'Business analysis completed',
|
|
14
|
+
next: 'questions',
|
|
15
|
+
actions: [
|
|
16
|
+
'Check requirements are measurable',
|
|
17
|
+
'Run: aia next <feature>',
|
|
18
|
+
],
|
|
19
|
+
tips: ['Missing acceptance criteria? Iterate.'],
|
|
20
|
+
},
|
|
21
|
+
questions: {
|
|
22
|
+
summary: 'Questions identified',
|
|
23
|
+
next: 'tech-spec',
|
|
24
|
+
actions: [
|
|
25
|
+
'Answer key questions in init.md or as iteration instructions',
|
|
26
|
+
'Run: aia next <feature>',
|
|
27
|
+
],
|
|
28
|
+
tips: ['Unanswered questions will affect quality of next steps'],
|
|
29
|
+
},
|
|
30
|
+
'tech-spec': {
|
|
31
|
+
summary: 'Technical specification ready',
|
|
32
|
+
next: 'challenge',
|
|
33
|
+
actions: [
|
|
34
|
+
'Review technical choices and constraints',
|
|
35
|
+
'Run: aia next <feature>',
|
|
36
|
+
],
|
|
37
|
+
tips: ['Consider performance and scalability implications'],
|
|
38
|
+
},
|
|
39
|
+
challenge: {
|
|
40
|
+
summary: 'Challenges identified',
|
|
41
|
+
next: 'dev-plan',
|
|
42
|
+
actions: [
|
|
43
|
+
'Address critical challenges before proceeding',
|
|
44
|
+
'Run: aia next <feature>',
|
|
45
|
+
],
|
|
46
|
+
tips: ['Unresolved challenges may cause issues during implementation'],
|
|
47
|
+
},
|
|
48
|
+
'dev-plan': {
|
|
49
|
+
summary: 'Development plan ready',
|
|
50
|
+
next: 'implement',
|
|
51
|
+
actions: [
|
|
52
|
+
'Review task breakdown',
|
|
53
|
+
'Run: aia next <feature> -a (agent mode)',
|
|
54
|
+
],
|
|
55
|
+
tips: ['Implementation will modify your codebase'],
|
|
56
|
+
},
|
|
57
|
+
implement: {
|
|
58
|
+
summary: 'Implementation completed',
|
|
59
|
+
next: 'review',
|
|
60
|
+
actions: [
|
|
61
|
+
'Run tests',
|
|
62
|
+
'Manual verification',
|
|
63
|
+
'Run: aia next <feature>',
|
|
64
|
+
],
|
|
65
|
+
tips: ['Check files created/modified'],
|
|
66
|
+
},
|
|
67
|
+
review: {
|
|
68
|
+
summary: 'Feature complete!',
|
|
69
|
+
next: null,
|
|
70
|
+
actions: [
|
|
71
|
+
'Address review comments',
|
|
72
|
+
'Merge to main branch',
|
|
73
|
+
],
|
|
74
|
+
tips: [],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
fr: {
|
|
78
|
+
brief: {
|
|
79
|
+
summary: 'Brief produit terminé',
|
|
80
|
+
next: 'ba-spec',
|
|
81
|
+
actions: [
|
|
82
|
+
'Vérifier que le brief est complet',
|
|
83
|
+
'Lancer : aia next <feature>',
|
|
84
|
+
],
|
|
85
|
+
tips: ['Si trop vague, itérer avec plus de contexte'],
|
|
86
|
+
},
|
|
87
|
+
'ba-spec': {
|
|
88
|
+
summary: 'Analyse métier terminée',
|
|
89
|
+
next: 'questions',
|
|
90
|
+
actions: [
|
|
91
|
+
'Vérifier que les exigences sont mesurables',
|
|
92
|
+
'Lancer : aia next <feature>',
|
|
93
|
+
],
|
|
94
|
+
tips: ['Critères d\'acceptation manquants ? Itérer.'],
|
|
95
|
+
},
|
|
96
|
+
questions: {
|
|
97
|
+
summary: 'Questions identifiées',
|
|
98
|
+
next: 'tech-spec',
|
|
99
|
+
actions: [
|
|
100
|
+
'Répondre aux questions clés dans init.md ou via instructions d\'itération',
|
|
101
|
+
'Lancer : aia next <feature>',
|
|
102
|
+
],
|
|
103
|
+
tips: ['Les questions sans réponse affecteront la qualité des étapes suivantes'],
|
|
104
|
+
},
|
|
105
|
+
'tech-spec': {
|
|
106
|
+
summary: 'Spécification technique prête',
|
|
107
|
+
next: 'challenge',
|
|
108
|
+
actions: [
|
|
109
|
+
'Revoir les choix techniques et contraintes',
|
|
110
|
+
'Lancer : aia next <feature>',
|
|
111
|
+
],
|
|
112
|
+
tips: ['Considérer les implications de performance et scalabilité'],
|
|
113
|
+
},
|
|
114
|
+
challenge: {
|
|
115
|
+
summary: 'Défis identifiés',
|
|
116
|
+
next: 'dev-plan',
|
|
117
|
+
actions: [
|
|
118
|
+
'Traiter les défis critiques avant de continuer',
|
|
119
|
+
'Lancer : aia next <feature>',
|
|
120
|
+
],
|
|
121
|
+
tips: ['Les défis non résolus peuvent causer des problèmes lors de l\'implémentation'],
|
|
122
|
+
},
|
|
123
|
+
'dev-plan': {
|
|
124
|
+
summary: 'Plan de développement prêt',
|
|
125
|
+
next: 'implement',
|
|
126
|
+
actions: [
|
|
127
|
+
'Revoir le découpage des tâches',
|
|
128
|
+
'Lancer : aia next <feature> -a (mode agent)',
|
|
129
|
+
],
|
|
130
|
+
tips: ['L\'implémentation va modifier votre code'],
|
|
131
|
+
},
|
|
132
|
+
implement: {
|
|
133
|
+
summary: 'Implémentation terminée',
|
|
134
|
+
next: 'review',
|
|
135
|
+
actions: [
|
|
136
|
+
'Lancer les tests',
|
|
137
|
+
'Vérification manuelle',
|
|
138
|
+
'Lancer : aia next <feature>',
|
|
139
|
+
],
|
|
140
|
+
tips: ['Vérifier les fichiers créés/modifiés'],
|
|
141
|
+
},
|
|
142
|
+
review: {
|
|
143
|
+
summary: 'Feature terminée !',
|
|
144
|
+
next: null,
|
|
145
|
+
actions: [
|
|
146
|
+
'Traiter les commentaires de review',
|
|
147
|
+
'Merger sur la branche principale',
|
|
148
|
+
],
|
|
149
|
+
tips: [],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
function getLanguageCode(language) {
|
|
155
|
+
if (!language) return 'en';
|
|
156
|
+
const lower = language.toLowerCase();
|
|
157
|
+
if (lower.includes('french') || lower.includes('français')) return 'fr';
|
|
158
|
+
if (lower.includes('english') || lower.includes('anglais')) return 'en';
|
|
159
|
+
return 'en';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function getGuidance(step, language = 'English') {
|
|
163
|
+
const langCode = getLanguageCode(language);
|
|
164
|
+
const guidance = STEP_GUIDANCE[langCode]?.[step] || STEP_GUIDANCE.en[step];
|
|
165
|
+
return guidance || null;
|
|
166
|
+
}
|