@autocode-cli/autocode 0.1.15 → 0.1.18
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 +38 -38
- package/dist/cli/commands/comment.d.ts +1 -1
- package/dist/cli/commands/comment.js +11 -11
- package/dist/cli/commands/comment.js.map +1 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/list.d.ts +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/list.js +65 -22
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/move.d.ts +1 -1
- package/dist/cli/commands/move.js +12 -12
- package/dist/cli/commands/move.js.map +1 -1
- package/dist/cli/commands/new.d.ts +1 -1
- package/dist/cli/commands/new.d.ts.map +1 -1
- package/dist/cli/commands/new.js +26 -13
- package/dist/cli/commands/new.js.map +1 -1
- package/dist/cli/commands/next.d.ts +1 -1
- package/dist/cli/commands/next.js +15 -15
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/parent.d.ts +9 -0
- package/dist/cli/commands/parent.d.ts.map +1 -0
- package/dist/cli/commands/parent.js +94 -0
- package/dist/cli/commands/parent.js.map +1 -0
- package/dist/cli/commands/serve.js +8 -8
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/show.d.ts +1 -1
- package/dist/cli/commands/show.d.ts.map +1 -1
- package/dist/cli/commands/show.js +51 -31
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/stats.js +2 -2
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/cli/commands/sync.js +3 -3
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +5 -3
- package/dist/cli/parser.js.map +1 -1
- package/dist/core/column.js +2 -2
- package/dist/core/column.js.map +1 -1
- package/dist/core/hierarchy.d.ts +97 -0
- package/dist/core/hierarchy.d.ts.map +1 -0
- package/dist/core/hierarchy.js +274 -0
- package/dist/core/hierarchy.js.map +1 -0
- package/dist/core/issue.d.ts +62 -0
- package/dist/core/issue.d.ts.map +1 -0
- package/dist/core/issue.js +247 -0
- package/dist/core/issue.js.map +1 -0
- package/dist/core/sync.d.ts +9 -9
- package/dist/core/sync.d.ts.map +1 -1
- package/dist/core/sync.js +61 -58
- package/dist/core/sync.js.map +1 -1
- package/dist/core/workflow.d.ts +22 -20
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/core/workflow.js +68 -51
- package/dist/core/workflow.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/server/api-autocomplete.test.d.ts +6 -0
- package/dist/server/api-autocomplete.test.d.ts.map +1 -0
- package/dist/server/api-autocomplete.test.js +249 -0
- package/dist/server/api-autocomplete.test.js.map +1 -0
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +216 -106
- package/dist/server/api.js.map +1 -1
- package/dist/server/dashboard/pages/column-prompt.d.ts +1 -1
- package/dist/server/dashboard/pages/column-prompt.d.ts.map +1 -1
- package/dist/server/dashboard/pages/column-prompt.js +11 -11
- package/dist/server/dashboard/pages/column-prompt.js.map +1 -1
- package/dist/server/dashboard/pages/column-terminal.d.ts +1 -1
- package/dist/server/dashboard/pages/column-terminal.d.ts.map +1 -1
- package/dist/server/dashboard/pages/column-terminal.js +14 -14
- package/dist/server/dashboard/pages/column-terminal.js.map +1 -1
- package/dist/server/dashboard/pages/index.d.ts +3 -1
- package/dist/server/dashboard/pages/index.d.ts.map +1 -1
- package/dist/server/dashboard/pages/index.js +3 -1
- package/dist/server/dashboard/pages/index.js.map +1 -1
- package/dist/server/dashboard/pages/issue-graph.d.ts +9 -0
- package/dist/server/dashboard/pages/issue-graph.d.ts.map +1 -0
- package/dist/server/dashboard/pages/issue-graph.js +581 -0
- package/dist/server/dashboard/pages/issue-graph.js.map +1 -0
- package/dist/server/dashboard/pages/issue-view.d.ts +8 -0
- package/dist/server/dashboard/pages/issue-view.d.ts.map +1 -0
- package/dist/server/dashboard/pages/{ticket-view.js → issue-view.js} +128 -128
- package/dist/server/dashboard/pages/issue-view.js.map +1 -0
- package/dist/server/dashboard/pages/main-dashboard.d.ts.map +1 -1
- package/dist/server/dashboard/pages/main-dashboard.js +23 -22
- package/dist/server/dashboard/pages/main-dashboard.js.map +1 -1
- package/dist/server/dashboard/pages/new-issue.d.ts +8 -0
- package/dist/server/dashboard/pages/new-issue.d.ts.map +1 -0
- package/dist/server/dashboard/pages/new-issue.js +648 -0
- package/dist/server/dashboard/pages/new-issue.js.map +1 -0
- package/dist/server/dashboard/pages/new-issue.test.d.ts +6 -0
- package/dist/server/dashboard/pages/new-issue.test.d.ts.map +1 -0
- package/dist/server/dashboard/pages/new-issue.test.js +349 -0
- package/dist/server/dashboard/pages/new-issue.test.js.map +1 -0
- package/dist/server/dashboard/pages/pipeline-configurator.js +1 -1
- package/dist/server/dashboard/pages/shared.d.ts +2 -2
- package/dist/server/dashboard/pages/shared.d.ts.map +1 -1
- package/dist/server/dashboard/pages/shared.js +5 -5
- package/dist/server/dashboard/pages/shared.js.map +1 -1
- package/dist/server/dashboard/pages/stats-page.js +11 -11
- package/dist/server/dashboard/scripts/index.js +134 -134
- package/dist/server/dashboard/styles/base.js +2 -2
- package/dist/server/dashboard/styles/board.d.ts +1 -1
- package/dist/server/dashboard/styles/board.js +10 -10
- package/dist/server/dashboard.d.ts +1 -1
- package/dist/server/dashboard.d.ts.map +1 -1
- package/dist/server/dashboard.js +1 -1
- package/dist/server/dashboard.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +40 -24
- package/dist/server/index.js.map +1 -1
- package/dist/server/websocket.d.ts +6 -6
- package/dist/server/websocket.d.ts.map +1 -1
- package/dist/server/websocket.js +10 -10
- package/dist/server/websocket.js.map +1 -1
- package/dist/services/claude.d.ts +13 -13
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +98 -98
- package/dist/services/claude.js.map +1 -1
- package/dist/services/issue-io.d.ts +81 -0
- package/dist/services/issue-io.d.ts.map +1 -0
- package/dist/services/{ticket-io.js → issue-io.js} +54 -53
- package/dist/services/issue-io.js.map +1 -0
- package/dist/services/stats.d.ts +1 -1
- package/dist/services/stats.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/fs.d.ts +2 -2
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +2 -2
- package/dist/utils/fs.js.map +1 -1
- package/package.json +3 -3
- package/templates/catalog.yaml +2 -2
- package/templates/prompts/api-endpoints-test.en.md +2 -2
- package/templates/prompts/api-endpoints-test.fr.md +2 -2
- package/templates/prompts/backlog.en.md +6 -6
- package/templates/prompts/backlog.fr.md +1 -1
- package/templates/prompts/changelog.en.md +6 -6
- package/templates/prompts/changelog.fr.md +3 -3
- package/templates/prompts/dashboard-responsive.en.md +1 -1
- package/templates/prompts/deploy-prod.en.md +3 -3
- package/templates/prompts/deploy-prod.fr.md +3 -3
- package/templates/prompts/deploy-staging.en.md +3 -3
- package/templates/prompts/deploy-staging.fr.md +3 -3
- package/templates/prompts/design.en.md +5 -5
- package/templates/prompts/design.fr.md +3 -3
- package/templates/prompts/dev.en.md +4 -4
- package/templates/prompts/dev.fr.md +3 -3
- package/templates/prompts/done.en.md +5 -5
- package/templates/prompts/done.fr.md +2 -2
- package/templates/prompts/error-handling-review.en.md +1 -1
- package/templates/prompts/error-handling-review.fr.md +1 -1
- package/templates/prompts/file-watcher-test.en.md +4 -4
- package/templates/prompts/file-watcher-test.fr.md +1 -1
- package/templates/prompts/git-commit.en.md +5 -5
- package/templates/prompts/git-commit.fr.md +3 -3
- package/templates/prompts/git-push.en.md +3 -3
- package/templates/prompts/git-push.fr.md +3 -3
- package/templates/prompts/git-tag.en.md +3 -3
- package/templates/prompts/git-tag.fr.md +3 -3
- package/templates/prompts/in-progress.en.md +6 -6
- package/templates/prompts/in-progress.fr.md +3 -3
- package/templates/prompts/qualification.en.md +8 -8
- package/templates/prompts/qualification.fr.md +3 -3
- package/templates/prompts/retest-cypress.en.md +3 -3
- package/templates/prompts/retest-cypress.fr.md +3 -3
- package/templates/prompts/retest-playwright.en.md +4 -4
- package/templates/prompts/retest-playwright.fr.md +3 -3
- package/templates/prompts/retest.en.md +4 -4
- package/templates/prompts/retest.fr.md +3 -3
- package/templates/prompts/review-best-practices.en.md +3 -3
- package/templates/prompts/review-best-practices.fr.md +3 -3
- package/templates/prompts/review-code.en.md +4 -4
- package/templates/prompts/review-code.fr.md +3 -3
- package/templates/prompts/review-consistency.en.md +3 -3
- package/templates/prompts/review-consistency.fr.md +3 -3
- package/templates/prompts/review-no-duplication.en.md +3 -3
- package/templates/prompts/review-no-duplication.fr.md +3 -3
- package/templates/prompts/review-security.en.md +4 -4
- package/templates/prompts/review-security.fr.md +3 -3
- package/templates/prompts/specification.en.md +5 -5
- package/templates/prompts/specification.fr.md +3 -3
- package/templates/prompts/splitter.en.md +13 -13
- package/templates/prompts/splitter.fr.md +3 -3
- package/templates/prompts/template-validation.en.md +1 -1
- package/templates/prompts/template-validation.fr.md +1 -1
- package/templates/prompts/testing-coverage.en.md +1 -1
- package/templates/prompts/testing-coverage.fr.md +1 -1
- package/templates/prompts/testing-cypress.en.md +4 -4
- package/templates/prompts/testing-cypress.fr.md +3 -3
- package/templates/prompts/testing-integration.en.md +4 -4
- package/templates/prompts/testing-integration.fr.md +3 -3
- package/templates/prompts/testing-playwright.en.md +4 -4
- package/templates/prompts/testing-playwright.fr.md +3 -3
- package/templates/prompts/testing-unit.en.md +4 -4
- package/templates/prompts/testing-unit.fr.md +3 -3
- package/templates/prompts/update-docs.en.md +3 -3
- package/templates/prompts/update-docs.fr.md +3 -3
- package/templates/prompts/validate-staging.en.md +3 -3
- package/templates/prompts/validate-staging.fr.md +3 -3
- package/templates/prompts/websocket-test.en.md +1 -1
- package/dist/core/ticket.d.ts +0 -50
- package/dist/core/ticket.d.ts.map +0 -1
- package/dist/core/ticket.js +0 -224
- package/dist/core/ticket.js.map +0 -1
- package/dist/server/dashboard/pages/ticket-view.d.ts +0 -8
- package/dist/server/dashboard/pages/ticket-view.d.ts.map +0 -1
- package/dist/server/dashboard/pages/ticket-view.js.map +0 -1
- package/dist/services/ticket-io.d.ts +0 -80
- package/dist/services/ticket-io.d.ts.map +0 -1
- package/dist/services/ticket-io.js.map +0 -1
package/dist/server/api.js
CHANGED
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { URL } from 'node:url';
|
|
6
|
-
import {
|
|
6
|
+
import { listIssues, getIssue, createIssue, updateIssue, addComment, findIssueDir, } from '../core/issue.js';
|
|
7
|
+
import { getChildren, getSubtree, setParent, } from '../core/hierarchy.js';
|
|
7
8
|
import { getColumns, getColumnBySlug } from '../core/column.js';
|
|
8
|
-
import {
|
|
9
|
+
import { moveAndProcessIssue, buildPrompt, getActionContent, executeClaudePrompt } from '../services/claude.js';
|
|
9
10
|
import { getStats } from '../services/stats.js';
|
|
10
11
|
import { getWorkflowSummary } from '../core/workflow.js';
|
|
11
|
-
import {
|
|
12
|
+
import { getProcessingIssues } from './websocket.js';
|
|
12
13
|
import { getConfig, getTemplatesDir } from '../utils/config.js';
|
|
13
14
|
import { readFileSafe, writeFileSafe, pathExists, getFileMtime, ensureDir } from '../utils/fs.js';
|
|
14
|
-
import { readAttachments } from '../services/
|
|
15
|
+
import { readAttachments } from '../services/issue-io.js';
|
|
15
16
|
import { writeFileSync, unlinkSync, readdirSync } from 'node:fs';
|
|
16
17
|
import { loadCatalog, getPromptContent } from '../core/catalog.js';
|
|
17
18
|
import { pipelineExists, loadPipelinesConfig, getActivePipeline, setActivePipeline, createPipeline, deletePipeline, listPipelines, } from '../core/pipeline.js';
|
|
18
|
-
import { previewSync, applySync,
|
|
19
|
+
import { previewSync, applySync, hasTransitionIssues, getTransitionIssues, resolveTransitionIssues, } from '../core/sync.js';
|
|
19
20
|
import { executeAutocreate, isClaudeAvailable } from '../services/autocreate.js';
|
|
20
21
|
/**
|
|
21
22
|
* Parse URL parameters from pattern
|
|
@@ -146,7 +147,7 @@ async function buildAnalysisPrompt(pipeline, catalog, lang) {
|
|
|
146
147
|
* Define API routes
|
|
147
148
|
*/
|
|
148
149
|
const routes = [
|
|
149
|
-
// GET /api/status - Get server status including processing
|
|
150
|
+
// GET /api/status - Get server status including processing issues
|
|
150
151
|
{
|
|
151
152
|
method: 'GET',
|
|
152
153
|
pattern: /^\/api\/status$/,
|
|
@@ -154,7 +155,7 @@ const routes = [
|
|
|
154
155
|
sendJson(res, {
|
|
155
156
|
success: true,
|
|
156
157
|
data: {
|
|
157
|
-
|
|
158
|
+
processingIssues: getProcessingIssues()
|
|
158
159
|
}
|
|
159
160
|
});
|
|
160
161
|
},
|
|
@@ -173,40 +174,140 @@ const routes = [
|
|
|
173
174
|
totals: stats.totals,
|
|
174
175
|
session: stats.session,
|
|
175
176
|
history: stats.history.slice(0, 20), // Limit to last 20 calls in API
|
|
176
|
-
|
|
177
|
+
processingIssues: getProcessingIssues()
|
|
177
178
|
}
|
|
178
179
|
});
|
|
179
180
|
},
|
|
180
181
|
},
|
|
181
|
-
// GET /api/
|
|
182
|
+
// GET /api/issues
|
|
182
183
|
{
|
|
183
184
|
method: 'GET',
|
|
184
|
-
pattern: /^\/api\/
|
|
185
|
+
pattern: /^\/api\/issues$/,
|
|
185
186
|
handler: async (_req, res) => {
|
|
186
187
|
const config = getConfig();
|
|
187
|
-
const
|
|
188
|
+
const issues = listIssues(config.root);
|
|
188
189
|
const columns = getColumns();
|
|
189
|
-
sendJson(res, { success: true, data: {
|
|
190
|
+
sendJson(res, { success: true, data: { issues, columns } });
|
|
190
191
|
},
|
|
191
192
|
},
|
|
192
|
-
// GET /api/
|
|
193
|
+
// GET /api/issues/:key
|
|
193
194
|
{
|
|
194
195
|
method: 'GET',
|
|
195
|
-
pattern: /^\/api\/
|
|
196
|
+
pattern: /^\/api\/issues\/(AC-\d{6})$/,
|
|
196
197
|
handler: async (_req, res, params) => {
|
|
197
198
|
const config = getConfig();
|
|
198
|
-
const
|
|
199
|
-
if (!
|
|
200
|
-
sendJson(res, { success: false, error: '
|
|
199
|
+
const issue = getIssue(config.root, params.p0);
|
|
200
|
+
if (!issue) {
|
|
201
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
201
202
|
return;
|
|
202
203
|
}
|
|
203
|
-
sendJson(res, { success: true, data:
|
|
204
|
+
sendJson(res, { success: true, data: issue });
|
|
204
205
|
},
|
|
205
206
|
},
|
|
206
|
-
//
|
|
207
|
+
// GET /api/issues/:key/children - Get direct children
|
|
208
|
+
{
|
|
209
|
+
method: 'GET',
|
|
210
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/children$/,
|
|
211
|
+
handler: async (_req, res, params) => {
|
|
212
|
+
const config = getConfig();
|
|
213
|
+
const issue = getIssue(config.root, params.p0);
|
|
214
|
+
if (!issue) {
|
|
215
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const children = getChildren(config.root, params.p0);
|
|
219
|
+
sendJson(res, { success: true, data: children });
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
// GET /api/issues/:key/subtree - Get all descendants
|
|
223
|
+
{
|
|
224
|
+
method: 'GET',
|
|
225
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/subtree$/,
|
|
226
|
+
handler: async (_req, res, params) => {
|
|
227
|
+
const config = getConfig();
|
|
228
|
+
const issue = getIssue(config.root, params.p0);
|
|
229
|
+
if (!issue) {
|
|
230
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const subtree = getSubtree(config.root, params.p0);
|
|
234
|
+
sendJson(res, { success: true, data: subtree });
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
// PATCH /api/issues/:key/parent - Set parent
|
|
238
|
+
{
|
|
239
|
+
method: 'PATCH',
|
|
240
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/parent$/,
|
|
241
|
+
handler: async (req, res, params) => {
|
|
242
|
+
const body = await parseBody(req);
|
|
243
|
+
const config = getConfig();
|
|
244
|
+
const issue = getIssue(config.root, params.p0);
|
|
245
|
+
if (!issue) {
|
|
246
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const result = setParent(config.root, params.p0, body?.parent_key ?? null);
|
|
250
|
+
if (!result.success) {
|
|
251
|
+
sendJson(res, { success: false, error: result.error }, 400);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const updated = getIssue(config.root, params.p0);
|
|
255
|
+
sendJson(res, { success: true, data: updated });
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
// POST /api/issues/autocomplete - Generate issue fields using Claude
|
|
207
259
|
{
|
|
208
260
|
method: 'POST',
|
|
209
|
-
pattern: /^\/api\/
|
|
261
|
+
pattern: /^\/api\/issues\/autocomplete$/,
|
|
262
|
+
handler: async (req, res) => {
|
|
263
|
+
const body = await parseBody(req);
|
|
264
|
+
if (!body || !body.title || !body.title.trim()) {
|
|
265
|
+
sendJson(res, { success: false, error: 'Title is required' }, 400);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const lang = body.lang || 'fr';
|
|
269
|
+
const title = body.title.trim();
|
|
270
|
+
try {
|
|
271
|
+
const prompt = `Tu es un assistant qui aide à créer des tickets de développement.
|
|
272
|
+
À partir du titre suivant, génère les champs du ticket en JSON.
|
|
273
|
+
|
|
274
|
+
Titre: "${title}"
|
|
275
|
+
|
|
276
|
+
Génère un JSON avec:
|
|
277
|
+
- description: Une description détaillée (2-4 phrases) en ${lang === 'fr' ? 'français' : 'anglais'}
|
|
278
|
+
- priority: P0, P1, P2, ou P3 (P2 par défaut pour la plupart des tâches)
|
|
279
|
+
- labels: Un tableau de labels parmi [bug, feature, enhancement, documentation, urgent, refactoring, security, performance, ui, api] (1-3 labels pertinents)
|
|
280
|
+
- acceptance_criteria: Un tableau de 2-5 critères d'acceptation en ${lang === 'fr' ? 'français' : 'anglais'}
|
|
281
|
+
- semver: "patch", "minor", ou "major" selon l'impact
|
|
282
|
+
|
|
283
|
+
Réponds UNIQUEMENT avec le JSON, sans formatage markdown ni explication.`;
|
|
284
|
+
const result = executeClaudePrompt(prompt, 60000);
|
|
285
|
+
if (!result.success) {
|
|
286
|
+
sendJson(res, { success: false, error: result.error || 'Claude failed' }, 500);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
// Parse the JSON response
|
|
290
|
+
const output = result.output?.trim() || '';
|
|
291
|
+
// Remove potential markdown code blocks
|
|
292
|
+
const jsonStr = output.replace(/^```json?\n?/, '').replace(/\n?```$/, '').trim();
|
|
293
|
+
try {
|
|
294
|
+
const data = JSON.parse(jsonStr);
|
|
295
|
+
sendJson(res, { success: true, data });
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
sendJson(res, { success: false, error: 'Failed to parse Claude response' }, 500);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
const message = error instanceof Error ? error.message : 'Autocomplete failed';
|
|
303
|
+
sendJson(res, { success: false, error: message }, 500);
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
// POST /api/issues (accepts parent_key for hierarchy)
|
|
308
|
+
{
|
|
309
|
+
method: 'POST',
|
|
310
|
+
pattern: /^\/api\/issues$/,
|
|
210
311
|
handler: async (req, res) => {
|
|
211
312
|
const body = await parseBody(req);
|
|
212
313
|
if (!body || !body.title) {
|
|
@@ -214,14 +315,23 @@ const routes = [
|
|
|
214
315
|
return;
|
|
215
316
|
}
|
|
216
317
|
const config = getConfig();
|
|
217
|
-
|
|
218
|
-
|
|
318
|
+
try {
|
|
319
|
+
const issue = createIssue(config.root, {
|
|
320
|
+
...body,
|
|
321
|
+
parent_key: body.parent_key,
|
|
322
|
+
});
|
|
323
|
+
sendJson(res, { success: true, data: issue }, 201);
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
const message = error instanceof Error ? error.message : 'Failed to create issue';
|
|
327
|
+
sendJson(res, { success: false, error: message }, 400);
|
|
328
|
+
}
|
|
219
329
|
},
|
|
220
330
|
},
|
|
221
|
-
// PATCH /api/
|
|
331
|
+
// PATCH /api/issues/:key
|
|
222
332
|
{
|
|
223
333
|
method: 'PATCH',
|
|
224
|
-
pattern: /^\/api\/
|
|
334
|
+
pattern: /^\/api\/issues\/(AC-\d{6})$/,
|
|
225
335
|
handler: async (req, res, params) => {
|
|
226
336
|
const body = await parseBody(req);
|
|
227
337
|
if (!body) {
|
|
@@ -229,18 +339,18 @@ const routes = [
|
|
|
229
339
|
return;
|
|
230
340
|
}
|
|
231
341
|
const config = getConfig();
|
|
232
|
-
const
|
|
233
|
-
if (!
|
|
234
|
-
sendJson(res, { success: false, error: '
|
|
342
|
+
const issue = updateIssue(config.root, params.p0, body);
|
|
343
|
+
if (!issue) {
|
|
344
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
235
345
|
return;
|
|
236
346
|
}
|
|
237
|
-
sendJson(res, { success: true, data:
|
|
347
|
+
sendJson(res, { success: true, data: issue });
|
|
238
348
|
},
|
|
239
349
|
},
|
|
240
|
-
// POST /api/
|
|
350
|
+
// POST /api/issues/:key/comments
|
|
241
351
|
{
|
|
242
352
|
method: 'POST',
|
|
243
|
-
pattern: /^\/api\/
|
|
353
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/comments$/,
|
|
244
354
|
handler: async (req, res, params) => {
|
|
245
355
|
const body = await parseBody(req);
|
|
246
356
|
if (!body || !body.text) {
|
|
@@ -249,25 +359,25 @@ const routes = [
|
|
|
249
359
|
}
|
|
250
360
|
const config = getConfig();
|
|
251
361
|
const source = body.source === 'claude' ? 'claude' : 'user';
|
|
252
|
-
const
|
|
253
|
-
if (!
|
|
254
|
-
sendJson(res, { success: false, error: '
|
|
362
|
+
const issue = addComment(config.root, params.p0, body.text, undefined, source);
|
|
363
|
+
if (!issue) {
|
|
364
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
255
365
|
return;
|
|
256
366
|
}
|
|
257
|
-
sendJson(res, { success: true, data:
|
|
367
|
+
sendJson(res, { success: true, data: issue });
|
|
258
368
|
},
|
|
259
369
|
},
|
|
260
|
-
// POST /api/
|
|
370
|
+
// POST /api/issues/:key/move
|
|
261
371
|
{
|
|
262
372
|
method: 'POST',
|
|
263
|
-
pattern: /^\/api\/
|
|
373
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/move$/,
|
|
264
374
|
handler: async (req, res, params) => {
|
|
265
375
|
const body = await parseBody(req);
|
|
266
376
|
if (!body || !body.column) {
|
|
267
377
|
sendJson(res, { success: false, error: 'Column is required' }, 400);
|
|
268
378
|
return;
|
|
269
379
|
}
|
|
270
|
-
const
|
|
380
|
+
const issueKey = params.p0;
|
|
271
381
|
// Get target column slug
|
|
272
382
|
const targetColumn = getColumnBySlug(body.column);
|
|
273
383
|
if (!targetColumn) {
|
|
@@ -275,7 +385,7 @@ const routes = [
|
|
|
275
385
|
return;
|
|
276
386
|
}
|
|
277
387
|
// Use new unified flow: move first, then execute action
|
|
278
|
-
const result = await
|
|
388
|
+
const result = await moveAndProcessIssue(issueKey, targetColumn.slug, {
|
|
279
389
|
force: body.force,
|
|
280
390
|
lang: body.lang,
|
|
281
391
|
});
|
|
@@ -285,26 +395,26 @@ const routes = [
|
|
|
285
395
|
}
|
|
286
396
|
sendJson(res, {
|
|
287
397
|
success: true,
|
|
288
|
-
data: result.
|
|
398
|
+
data: result.issue,
|
|
289
399
|
actionExecuted: result.actionExecuted,
|
|
290
400
|
});
|
|
291
401
|
},
|
|
292
402
|
},
|
|
293
|
-
// POST /api/
|
|
403
|
+
// POST /api/issues/:key/next
|
|
294
404
|
{
|
|
295
405
|
method: 'POST',
|
|
296
|
-
pattern: /^\/api\/
|
|
406
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/next$/,
|
|
297
407
|
handler: async (req, res, params) => {
|
|
298
408
|
const config = getConfig();
|
|
299
|
-
const
|
|
409
|
+
const issueKey = params.p0;
|
|
300
410
|
const body = await parseBody(req);
|
|
301
|
-
// Get current
|
|
302
|
-
const
|
|
303
|
-
if (!
|
|
304
|
-
sendJson(res, { success: false, error: '
|
|
411
|
+
// Get current issue to find next column
|
|
412
|
+
const issue = getIssue(config.root, issueKey);
|
|
413
|
+
if (!issue) {
|
|
414
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
305
415
|
return;
|
|
306
416
|
}
|
|
307
|
-
const currentColumn = getColumnBySlug(
|
|
417
|
+
const currentColumn = getColumnBySlug(issue.column_slug);
|
|
308
418
|
if (!currentColumn) {
|
|
309
419
|
sendJson(res, { success: false, error: 'Current column not found' }, 400);
|
|
310
420
|
return;
|
|
@@ -316,28 +426,28 @@ const routes = [
|
|
|
316
426
|
return;
|
|
317
427
|
}
|
|
318
428
|
// Use new unified flow: move first, then execute action
|
|
319
|
-
const result = await
|
|
429
|
+
const result = await moveAndProcessIssue(issueKey, nextColumn.slug, { lang: body?.lang });
|
|
320
430
|
if (!result.success) {
|
|
321
431
|
sendJson(res, { success: false, error: result.error }, 400);
|
|
322
432
|
return;
|
|
323
433
|
}
|
|
324
434
|
sendJson(res, {
|
|
325
435
|
success: true,
|
|
326
|
-
data: result.
|
|
436
|
+
data: result.issue,
|
|
327
437
|
actionExecuted: result.actionExecuted,
|
|
328
438
|
});
|
|
329
439
|
},
|
|
330
440
|
},
|
|
331
|
-
// POST /api/
|
|
441
|
+
// POST /api/issues/:key/archive
|
|
332
442
|
{
|
|
333
443
|
method: 'POST',
|
|
334
|
-
pattern: /^\/api\/
|
|
444
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/archive$/,
|
|
335
445
|
handler: async (req, res, params) => {
|
|
336
|
-
const
|
|
446
|
+
const issueKey = params.p0;
|
|
337
447
|
const body = await parseBody(req);
|
|
338
448
|
const columns = getColumns();
|
|
339
449
|
const lastColumn = columns[columns.length - 1];
|
|
340
|
-
const result = await
|
|
450
|
+
const result = await moveAndProcessIssue(issueKey, lastColumn.slug, {
|
|
341
451
|
force: true,
|
|
342
452
|
lang: body?.lang,
|
|
343
453
|
});
|
|
@@ -347,30 +457,30 @@ const routes = [
|
|
|
347
457
|
}
|
|
348
458
|
sendJson(res, {
|
|
349
459
|
success: true,
|
|
350
|
-
data: result.
|
|
460
|
+
data: result.issue,
|
|
351
461
|
});
|
|
352
462
|
},
|
|
353
463
|
},
|
|
354
|
-
// GET /api/
|
|
464
|
+
// GET /api/issues/:key/log
|
|
355
465
|
// Returns all column logs concatenated (since logs are per-column: claude-{columnSlug}.log)
|
|
356
466
|
{
|
|
357
467
|
method: 'GET',
|
|
358
|
-
pattern: /^\/api\/
|
|
468
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/log$/,
|
|
359
469
|
handler: async (_req, res, params) => {
|
|
360
470
|
const config = getConfig();
|
|
361
|
-
const
|
|
362
|
-
if (!
|
|
363
|
-
sendJson(res, { success: false, error: '
|
|
471
|
+
const issueDir = findIssueDir(config.root, params.p0);
|
|
472
|
+
if (!issueDir) {
|
|
473
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
364
474
|
return;
|
|
365
475
|
}
|
|
366
476
|
// Find all claude-*.log files and sort by modification time
|
|
367
477
|
let logFiles = [];
|
|
368
478
|
try {
|
|
369
|
-
const files = readdirSync(
|
|
479
|
+
const files = readdirSync(issueDir);
|
|
370
480
|
logFiles = files
|
|
371
481
|
.filter(f => f.startsWith('claude-') && f.endsWith('.log'))
|
|
372
482
|
.map(f => {
|
|
373
|
-
const filePath = join(
|
|
483
|
+
const filePath = join(issueDir, f);
|
|
374
484
|
const mtime = getFileMtime(filePath);
|
|
375
485
|
return { path: filePath, mtime: mtime || new Date(0) };
|
|
376
486
|
})
|
|
@@ -408,20 +518,20 @@ const routes = [
|
|
|
408
518
|
});
|
|
409
519
|
},
|
|
410
520
|
},
|
|
411
|
-
// GET /api/
|
|
521
|
+
// GET /api/issues/:key/log/:columnSlug
|
|
412
522
|
{
|
|
413
523
|
method: 'GET',
|
|
414
|
-
pattern: /^\/api\/
|
|
524
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/log\/([^/]+)$/,
|
|
415
525
|
handler: async (_req, res, params) => {
|
|
416
526
|
const config = getConfig();
|
|
417
|
-
const
|
|
527
|
+
const issueKey = params.p0;
|
|
418
528
|
const columnSlug = params.p1;
|
|
419
|
-
const
|
|
420
|
-
if (!
|
|
421
|
-
sendJson(res, { success: false, error: '
|
|
529
|
+
const issueDir = findIssueDir(config.root, issueKey);
|
|
530
|
+
if (!issueDir) {
|
|
531
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
422
532
|
return;
|
|
423
533
|
}
|
|
424
|
-
const logFile = join(
|
|
534
|
+
const logFile = join(issueDir, `claude-${columnSlug}.log`);
|
|
425
535
|
if (!pathExists(logFile)) {
|
|
426
536
|
sendJson(res, {
|
|
427
537
|
success: true,
|
|
@@ -436,17 +546,17 @@ const routes = [
|
|
|
436
546
|
});
|
|
437
547
|
},
|
|
438
548
|
},
|
|
439
|
-
// GET /api/
|
|
549
|
+
// GET /api/issues/:key/prompt/:columnSlug
|
|
440
550
|
{
|
|
441
551
|
method: 'GET',
|
|
442
|
-
pattern: /^\/api\/
|
|
552
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/prompt\/([^/]+)$/,
|
|
443
553
|
handler: async (_req, res, params) => {
|
|
444
554
|
const config = getConfig();
|
|
445
|
-
const
|
|
555
|
+
const issueKey = params.p0;
|
|
446
556
|
const columnSlug = params.p1;
|
|
447
|
-
const
|
|
448
|
-
if (!
|
|
449
|
-
sendJson(res, { success: false, error: '
|
|
557
|
+
const issue = getIssue(config.root, issueKey);
|
|
558
|
+
if (!issue) {
|
|
559
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
450
560
|
return;
|
|
451
561
|
}
|
|
452
562
|
const column = getColumnBySlug(columnSlug);
|
|
@@ -459,8 +569,8 @@ const routes = [
|
|
|
459
569
|
sendJson(res, { success: false, error: 'No ACTION file for this column' }, 404);
|
|
460
570
|
return;
|
|
461
571
|
}
|
|
462
|
-
const
|
|
463
|
-
const prompt = buildPrompt(
|
|
572
|
+
const issueDir = findIssueDir(config.root, issueKey);
|
|
573
|
+
const prompt = buildPrompt(issue, actionContent, issueDir || undefined, config.lang);
|
|
464
574
|
sendJson(res, {
|
|
465
575
|
success: true,
|
|
466
576
|
data: {
|
|
@@ -471,33 +581,33 @@ const routes = [
|
|
|
471
581
|
});
|
|
472
582
|
},
|
|
473
583
|
},
|
|
474
|
-
// GET /api/
|
|
584
|
+
// GET /api/issues/:key/attachments
|
|
475
585
|
{
|
|
476
586
|
method: 'GET',
|
|
477
|
-
pattern: /^\/api\/
|
|
587
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/attachments$/,
|
|
478
588
|
handler: async (_req, res, params) => {
|
|
479
589
|
const config = getConfig();
|
|
480
|
-
const
|
|
481
|
-
if (!
|
|
482
|
-
sendJson(res, { success: false, error: '
|
|
590
|
+
const issueDir = findIssueDir(config.root, params.p0);
|
|
591
|
+
if (!issueDir) {
|
|
592
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
483
593
|
return;
|
|
484
594
|
}
|
|
485
|
-
const attachments = readAttachments(
|
|
595
|
+
const attachments = readAttachments(issueDir);
|
|
486
596
|
sendJson(res, { success: true, data: attachments });
|
|
487
597
|
},
|
|
488
598
|
},
|
|
489
|
-
// POST /api/
|
|
599
|
+
// POST /api/issues/:key/attachments
|
|
490
600
|
{
|
|
491
601
|
method: 'POST',
|
|
492
|
-
pattern: /^\/api\/
|
|
602
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/attachments$/,
|
|
493
603
|
handler: async (req, res, params) => {
|
|
494
604
|
const config = getConfig();
|
|
495
|
-
const
|
|
496
|
-
if (!
|
|
497
|
-
sendJson(res, { success: false, error: '
|
|
605
|
+
const issueDir = findIssueDir(config.root, params.p0);
|
|
606
|
+
if (!issueDir) {
|
|
607
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
498
608
|
return;
|
|
499
609
|
}
|
|
500
|
-
const attachmentsDir = join(
|
|
610
|
+
const attachmentsDir = join(issueDir, 'attachments');
|
|
501
611
|
ensureDir(attachmentsDir);
|
|
502
612
|
try {
|
|
503
613
|
const files = await parseMultipartBody(req);
|
|
@@ -521,19 +631,19 @@ const routes = [
|
|
|
521
631
|
}
|
|
522
632
|
},
|
|
523
633
|
},
|
|
524
|
-
// DELETE /api/
|
|
634
|
+
// DELETE /api/issues/:key/attachments/:filename
|
|
525
635
|
{
|
|
526
636
|
method: 'DELETE',
|
|
527
|
-
pattern: /^\/api\/
|
|
637
|
+
pattern: /^\/api\/issues\/(AC-\d{6})\/attachments\/([^/]+)$/,
|
|
528
638
|
handler: async (_req, res, params) => {
|
|
529
639
|
const config = getConfig();
|
|
530
|
-
const
|
|
531
|
-
if (!
|
|
532
|
-
sendJson(res, { success: false, error: '
|
|
640
|
+
const issueDir = findIssueDir(config.root, params.p0);
|
|
641
|
+
if (!issueDir) {
|
|
642
|
+
sendJson(res, { success: false, error: 'Issue not found' }, 404);
|
|
533
643
|
return;
|
|
534
644
|
}
|
|
535
645
|
const filename = decodeURIComponent(params.p1);
|
|
536
|
-
const filePath = join(
|
|
646
|
+
const filePath = join(issueDir, 'attachments', filename);
|
|
537
647
|
if (!pathExists(filePath)) {
|
|
538
648
|
sendJson(res, { success: false, error: 'File not found' }, 404);
|
|
539
649
|
return;
|
|
@@ -637,8 +747,8 @@ const routes = [
|
|
|
637
747
|
pattern: /^\/api\/summary$/,
|
|
638
748
|
handler: async (_req, res) => {
|
|
639
749
|
const config = getConfig();
|
|
640
|
-
const
|
|
641
|
-
const summary = getWorkflowSummary(
|
|
750
|
+
const issues = listIssues(config.root);
|
|
751
|
+
const summary = getWorkflowSummary(issues);
|
|
642
752
|
sendJson(res, { success: true, data: summary });
|
|
643
753
|
},
|
|
644
754
|
},
|
|
@@ -722,10 +832,10 @@ Generate EXACTLY this format for EACH language, nothing else:
|
|
|
722
832
|
2. [action 2]
|
|
723
833
|
...
|
|
724
834
|
8. If out of scope work detected: \`autocode new "<title>" "<description>" --priority P2 --labels "<labels>" --acceptance "<criteria>" --semver patch --column backlog\`
|
|
725
|
-
9. Document: \`autocode comment <
|
|
726
|
-
10. Advance: \`autocode next <
|
|
835
|
+
9. Document: \`autocode comment <issue-key> "[summary]"\`
|
|
836
|
+
10. Advance: \`autocode next <issue-key>\`
|
|
727
837
|
|
|
728
|
-
> Or return: \`autocode move <
|
|
838
|
+
> Or return: \`autocode move <issue-key> <target-column-slug>\`
|
|
729
839
|
|
|
730
840
|
## Validation Criteria
|
|
731
841
|
|
|
@@ -1037,26 +1147,26 @@ IMPORTANT: Output ONLY the two prompts separated by ===EN=== and ===FR===, nothi
|
|
|
1037
1147
|
sendJson(res, { success: true, data: result });
|
|
1038
1148
|
},
|
|
1039
1149
|
},
|
|
1040
|
-
// GET /api/transition - Get
|
|
1150
|
+
// GET /api/transition - Get issues in transition
|
|
1041
1151
|
{
|
|
1042
1152
|
method: 'GET',
|
|
1043
1153
|
pattern: /^\/api\/transition$/,
|
|
1044
1154
|
handler: async (_req, res) => {
|
|
1045
1155
|
const config = getConfig();
|
|
1046
|
-
const
|
|
1047
|
-
sendJson(res, { success: true, data:
|
|
1156
|
+
const issues = getTransitionIssues(config.root);
|
|
1157
|
+
sendJson(res, { success: true, data: issues });
|
|
1048
1158
|
},
|
|
1049
1159
|
},
|
|
1050
|
-
// GET /api/transition/has - Check if there are transition
|
|
1160
|
+
// GET /api/transition/has - Check if there are transition issues
|
|
1051
1161
|
{
|
|
1052
1162
|
method: 'GET',
|
|
1053
1163
|
pattern: /^\/api\/transition\/has$/,
|
|
1054
1164
|
handler: async (_req, res) => {
|
|
1055
1165
|
const config = getConfig();
|
|
1056
|
-
sendJson(res, { success: true, data: { hasTransition:
|
|
1166
|
+
sendJson(res, { success: true, data: { hasTransition: hasTransitionIssues(config.root) } });
|
|
1057
1167
|
},
|
|
1058
1168
|
},
|
|
1059
|
-
// POST /api/transition/resolve - Resolve transition
|
|
1169
|
+
// POST /api/transition/resolve - Resolve transition issues
|
|
1060
1170
|
{
|
|
1061
1171
|
method: 'POST',
|
|
1062
1172
|
pattern: /^\/api\/transition\/resolve$/,
|
|
@@ -1067,7 +1177,7 @@ IMPORTANT: Output ONLY the two prompts separated by ===EN=== and ===FR===, nothi
|
|
|
1067
1177
|
return;
|
|
1068
1178
|
}
|
|
1069
1179
|
const config = getConfig();
|
|
1070
|
-
const result =
|
|
1180
|
+
const result = resolveTransitionIssues(config.root, body.actions);
|
|
1071
1181
|
sendJson(res, { success: true, data: result });
|
|
1072
1182
|
},
|
|
1073
1183
|
},
|