@proletariat/cli 0.3.16 → 0.3.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/dist/commands/action/create.d.ts +1 -0
- package/dist/commands/action/create.js +74 -38
- package/dist/commands/action/delete.d.ts +1 -0
- package/dist/commands/action/delete.js +23 -24
- package/dist/commands/action/index.d.ts +1 -0
- package/dist/commands/action/index.js +5 -10
- package/dist/commands/action/list.d.ts +1 -0
- package/dist/commands/action/list.js +3 -1
- package/dist/commands/action/run.d.ts +1 -0
- package/dist/commands/action/run.js +44 -32
- package/dist/commands/action/show.d.ts +2 -0
- package/dist/commands/action/update.d.ts +1 -0
- package/dist/commands/action/update.js +80 -39
- package/dist/commands/agent/auth.d.ts +2 -0
- package/dist/commands/agent/auth.js +44 -3
- package/dist/commands/agent/discover.d.ts +2 -0
- package/dist/commands/agent/discover.js +35 -3
- package/dist/commands/agent/index.d.ts +1 -0
- package/dist/commands/agent/index.js +25 -45
- package/dist/commands/agent/list.d.ts +8 -3
- package/dist/commands/agent/list.js +16 -29
- package/dist/commands/agent/login.d.ts +1 -0
- package/dist/commands/agent/login.js +14 -32
- package/dist/commands/agent/rebuild.d.ts +1 -0
- package/dist/commands/agent/rebuild.js +2 -2
- package/dist/commands/agent/remove.d.ts +17 -0
- package/dist/commands/agent/remove.js +144 -0
- package/dist/commands/agent/restart.d.ts +1 -0
- package/dist/commands/agent/restart.js +2 -2
- package/dist/commands/agent/shell.d.ts +1 -0
- package/dist/commands/agent/shell.js +63 -76
- package/dist/commands/agent/staff/add.d.ts +1 -0
- package/dist/commands/agent/staff/add.js +7 -1
- package/dist/commands/agent/staff/index.d.ts +1 -0
- package/dist/commands/agent/staff/index.js +5 -4
- package/dist/commands/agent/staff/remove.d.ts +1 -0
- package/dist/commands/agent/status.d.ts +1 -0
- package/dist/commands/agent/status.js +11 -23
- package/dist/commands/agent/temp/cleanup.d.ts +1 -0
- package/dist/commands/agent/temp/index.d.ts +1 -0
- package/dist/commands/agent/temp/index.js +4 -3
- package/dist/commands/agent/themes/index.d.ts +1 -0
- package/dist/commands/agent/themes/index.js +9 -3
- package/dist/commands/agent/themes/set.d.ts +1 -0
- package/dist/commands/agent/themes/set.js +7 -1
- package/dist/commands/agent/visit.d.ts +1 -0
- package/dist/commands/agent/visit.js +11 -23
- package/dist/commands/autocomplete/setup.d.ts +11 -0
- package/dist/commands/autocomplete/setup.js +113 -8
- package/dist/commands/board/index.d.ts +4 -0
- package/dist/commands/board/index.js +32 -30
- package/dist/commands/board/watch.d.ts +2 -0
- package/dist/commands/branch/create.d.ts +1 -0
- package/dist/commands/branch/create.js +33 -41
- package/dist/commands/branch/index.d.ts +1 -0
- package/dist/commands/branch/list.d.ts +2 -0
- package/dist/commands/branch/validate.d.ts +2 -0
- package/dist/commands/branch/where.d.ts +1 -0
- package/dist/commands/claude.d.ts +6 -0
- package/dist/commands/claude.js +166 -116
- package/dist/commands/commit.d.ts +6 -0
- package/dist/commands/commit.js +68 -73
- package/dist/commands/config/index.d.ts +13 -0
- package/dist/commands/config/index.js +142 -98
- package/dist/commands/docker/clean.d.ts +2 -1
- package/dist/commands/docker/clean.js +20 -29
- package/dist/commands/docker/index.d.ts +1 -0
- package/dist/commands/docker/index.js +37 -41
- package/dist/commands/docker/prune.d.ts +2 -1
- package/dist/commands/docker/prune.js +20 -27
- package/dist/commands/docker/restart.d.ts +2 -1
- package/dist/commands/docker/restart.js +20 -29
- package/dist/commands/docker/stop.d.ts +2 -1
- package/dist/commands/docker/stop.js +20 -29
- package/dist/commands/epic/activate.d.ts +1 -0
- package/dist/commands/epic/archive.d.ts +1 -0
- package/dist/commands/epic/create.d.ts +1 -0
- package/dist/commands/epic/index.d.ts +1 -0
- package/dist/commands/epic/link/block.d.ts +1 -0
- package/dist/commands/epic/link/duplicates.d.ts +1 -0
- package/dist/commands/epic/link/index.d.ts +1 -0
- package/dist/commands/epic/link/relates.d.ts +1 -0
- package/dist/commands/epic/link/remove.d.ts +1 -0
- package/dist/commands/epic/list.d.ts +2 -0
- package/dist/commands/epic/move.d.ts +1 -0
- package/dist/commands/epic/progress.d.ts +1 -0
- package/dist/commands/epic/project.d.ts +1 -0
- package/dist/commands/epic/reorder.d.ts +1 -0
- package/dist/commands/epic/spec.d.ts +1 -0
- package/dist/commands/epic/ticket.d.ts +1 -0
- package/dist/commands/epic/view.d.ts +1 -0
- package/dist/commands/execution/index.d.ts +1 -0
- package/dist/commands/execution/index.js +9 -25
- package/dist/commands/execution/list.d.ts +2 -0
- package/dist/commands/execution/logs.d.ts +1 -0
- package/dist/commands/execution/logs.js +6 -16
- package/dist/commands/execution/stop.d.ts +1 -0
- package/dist/commands/execution/stop.js +4 -15
- package/dist/commands/gh/index.d.ts +1 -0
- package/dist/commands/gh/index.js +27 -27
- package/dist/commands/gh/login.d.ts +4 -0
- package/dist/commands/gh/login.js +31 -0
- package/dist/commands/gh/status.d.ts +4 -0
- package/dist/commands/gh/status.js +27 -4
- package/dist/commands/gh/token.d.ts +4 -0
- package/dist/commands/gh/token.js +49 -5
- package/dist/commands/phase/create.d.ts +1 -1
- package/dist/commands/phase/create.js +116 -74
- package/dist/commands/phase/delete.d.ts +1 -0
- package/dist/commands/phase/delete.js +23 -22
- package/dist/commands/phase/list.d.ts +1 -0
- package/dist/commands/phase/list.js +3 -5
- package/dist/commands/phase/move.d.ts +1 -0
- package/dist/commands/phase/move.js +39 -39
- package/dist/commands/phase/template/apply.d.ts +1 -0
- package/dist/commands/phase/template/create.d.ts +2 -0
- package/dist/commands/phase/template/delete.d.ts +1 -0
- package/dist/commands/phase/template/index.d.ts +1 -0
- package/dist/commands/phase/template/list.d.ts +1 -0
- package/dist/commands/phase/template/update.d.ts +2 -0
- package/dist/commands/phase/update.d.ts +1 -1
- package/dist/commands/phase/update.js +89 -55
- package/dist/commands/pmo/init.d.ts +2 -0
- package/dist/commands/pmo/init.js +84 -22
- package/dist/commands/pr/create.d.ts +12 -3
- package/dist/commands/pr/create.js +130 -147
- package/dist/commands/pr/index.d.ts +6 -3
- package/dist/commands/pr/index.js +41 -39
- package/dist/commands/pr/link.d.ts +7 -3
- package/dist/commands/pr/link.js +126 -150
- package/dist/commands/pr/status.d.ts +6 -3
- package/dist/commands/pr/status.js +101 -126
- package/dist/commands/project/archive.d.ts +1 -0
- package/dist/commands/project/archive.js +15 -20
- package/dist/commands/project/create.d.ts +1 -0
- package/dist/commands/project/create.js +13 -5
- package/dist/commands/project/delete.d.ts +1 -0
- package/dist/commands/project/delete.js +14 -28
- package/dist/commands/project/index.d.ts +1 -0
- package/dist/commands/project/index.js +0 -5
- package/dist/commands/project/list.d.ts +2 -0
- package/dist/commands/project/list.js +21 -3
- package/dist/commands/project/spec.d.ts +1 -0
- package/dist/commands/project/spec.js +17 -23
- package/dist/commands/project/unarchive.d.ts +2 -0
- package/dist/commands/project/unarchive.js +21 -2
- package/dist/commands/project/view.d.ts +1 -0
- package/dist/commands/project/view.js +34 -22
- package/dist/commands/repo/add.d.ts +2 -0
- package/dist/commands/repo/add.js +44 -1
- package/dist/commands/repo/index.d.ts +1 -0
- package/dist/commands/repo/index.js +20 -38
- package/dist/commands/repo/list.d.ts +2 -0
- package/dist/commands/repo/remove.d.ts +1 -0
- package/dist/commands/repo/remove.js +45 -63
- package/dist/commands/repo/view.d.ts +2 -0
- package/dist/commands/repo/view.js +30 -5
- package/dist/commands/roadmap/add-project.d.ts +1 -0
- package/dist/commands/roadmap/create.d.ts +1 -0
- package/dist/commands/roadmap/delete.d.ts +1 -0
- package/dist/commands/roadmap/generate.d.ts +1 -0
- package/dist/commands/roadmap/index.d.ts +1 -0
- package/dist/commands/roadmap/list.d.ts +2 -0
- package/dist/commands/roadmap/remove-project.d.ts +1 -0
- package/dist/commands/roadmap/reorder.d.ts +1 -0
- package/dist/commands/roadmap/update.d.ts +1 -0
- package/dist/commands/roadmap/view.d.ts +1 -0
- package/dist/commands/session/attach.d.ts +1 -0
- package/dist/commands/session/index.d.ts +1 -0
- package/dist/commands/session/index.js +8 -25
- package/dist/commands/session/list.d.ts +2 -0
- package/dist/commands/spec/create.d.ts +1 -1
- package/dist/commands/spec/create.js +64 -65
- package/dist/commands/spec/index.d.ts +1 -0
- package/dist/commands/spec/index.js +36 -22
- package/dist/commands/spec/link/depends.d.ts +1 -0
- package/dist/commands/spec/link/depends.js +6 -6
- package/dist/commands/spec/link/duplicates.d.ts +1 -0
- package/dist/commands/spec/link/duplicates.js +6 -6
- package/dist/commands/spec/link/index.d.ts +2 -1
- package/dist/commands/spec/link/index.js +0 -4
- package/dist/commands/spec/link/relates.d.ts +1 -0
- package/dist/commands/spec/link/relates.js +6 -6
- package/dist/commands/spec/link/remove.d.ts +2 -1
- package/dist/commands/spec/link/remove.js +6 -6
- package/dist/commands/spec/list.d.ts +2 -0
- package/dist/commands/spec/list.js +25 -0
- package/dist/commands/spec/plan.d.ts +2 -1
- package/dist/commands/spec/plan.js +19 -26
- package/dist/commands/spec/ticket.d.ts +2 -1
- package/dist/commands/spec/ticket.js +48 -34
- package/dist/commands/spec/view.d.ts +2 -1
- package/dist/commands/spec/view.js +25 -16
- package/dist/commands/status/create.d.ts +1 -1
- package/dist/commands/status/create.js +80 -64
- package/dist/commands/status/delete.d.ts +2 -1
- package/dist/commands/status/delete.js +26 -22
- package/dist/commands/status/index.d.ts +1 -0
- package/dist/commands/status/index.js +26 -19
- package/dist/commands/status/list.d.ts +1 -0
- package/dist/commands/status/list.js +12 -7
- package/dist/commands/status/move.d.ts +2 -1
- package/dist/commands/status/move.js +62 -61
- package/dist/commands/status/update.d.ts +2 -2
- package/dist/commands/status/update.js +110 -77
- package/dist/commands/template/delete.d.ts +1 -0
- package/dist/commands/template/delete.js +47 -48
- package/dist/commands/template/index.d.ts +1 -0
- package/dist/commands/template/index.js +26 -33
- package/dist/commands/template/list.d.ts +1 -0
- package/dist/commands/template/phase/create.d.ts +1 -0
- package/dist/commands/template/phase/create.js +6 -0
- package/dist/commands/template/phase/index.d.ts +1 -0
- package/dist/commands/template/phase/index.js +27 -26
- package/dist/commands/template/phase/update.d.ts +1 -0
- package/dist/commands/template/phase/update.js +6 -0
- package/dist/commands/template/ticket/index.d.ts +1 -0
- package/dist/commands/template/ticket/index.js +27 -26
- package/dist/commands/template/ticket/save.d.ts +1 -0
- package/dist/commands/template/ticket/save.js +6 -0
- package/dist/commands/terminal/title.d.ts +26 -0
- package/dist/commands/terminal/title.js +37 -3
- package/dist/commands/ticket/bulk.d.ts +1 -0
- package/dist/commands/ticket/complete.d.ts +1 -0
- package/dist/commands/ticket/complete.js +18 -14
- package/dist/commands/ticket/create.d.ts +1 -0
- package/dist/commands/ticket/create.js +45 -41
- package/dist/commands/ticket/delete.d.ts +1 -0
- package/dist/commands/ticket/delete.js +1 -1
- package/dist/commands/ticket/edit.d.ts +1 -0
- package/dist/commands/ticket/edit.js +1 -1
- package/dist/commands/ticket/epic.d.ts +1 -0
- package/dist/commands/ticket/epic.js +2 -2
- package/dist/commands/ticket/index.d.ts +1 -0
- package/dist/commands/ticket/link/block.d.ts +1 -0
- package/dist/commands/ticket/link/block.js +1 -1
- package/dist/commands/ticket/link/duplicates.d.ts +1 -0
- package/dist/commands/ticket/link/duplicates.js +1 -1
- package/dist/commands/ticket/link/index.d.ts +1 -0
- package/dist/commands/ticket/link/index.js +9 -8
- package/dist/commands/ticket/link/relates.d.ts +1 -0
- package/dist/commands/ticket/link/relates.js +1 -1
- package/dist/commands/ticket/link/remove.d.ts +1 -0
- package/dist/commands/ticket/link/remove.js +1 -1
- package/dist/commands/ticket/list.d.ts +2 -0
- package/dist/commands/ticket/move.d.ts +1 -0
- package/dist/commands/ticket/move.js +27 -19
- package/dist/commands/ticket/project.d.ts +1 -0
- package/dist/commands/ticket/project.js +3 -3
- package/dist/commands/ticket/reassign.d.ts +1 -0
- package/dist/commands/ticket/reassign.js +1 -1
- package/dist/commands/ticket/spec.d.ts +1 -0
- package/dist/commands/ticket/spec.js +3 -3
- package/dist/commands/ticket/status.d.ts +1 -0
- package/dist/commands/ticket/status.js +1 -1
- package/dist/commands/ticket/template/apply.d.ts +1 -0
- package/dist/commands/ticket/template/create.d.ts +2 -0
- package/dist/commands/ticket/template/delete.d.ts +1 -0
- package/dist/commands/ticket/template/index.d.ts +1 -0
- package/dist/commands/ticket/template/list.d.ts +1 -0
- package/dist/commands/ticket/template/save.d.ts +2 -0
- package/dist/commands/ticket/update.d.ts +1 -0
- package/dist/commands/ticket/update.js +1 -1
- package/dist/commands/ticket/view.d.ts +1 -0
- package/dist/commands/ticket/view.js +1 -1
- package/dist/commands/work/complete.d.ts +1 -0
- package/dist/commands/work/index.d.ts +1 -0
- package/dist/commands/work/ready.d.ts +1 -0
- package/dist/commands/work/revise.d.ts +1 -0
- package/dist/commands/work/spawn-all.d.ts +2 -0
- package/dist/commands/work/spawn-all.js +11 -4
- package/dist/commands/work/spawn.d.ts +1 -0
- package/dist/commands/work/spawn.js +261 -166
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +270 -189
- package/dist/commands/work/watch.d.ts +1 -0
- package/dist/commands/work/watch.js +63 -58
- package/dist/commands/workflow/create.d.ts +1 -0
- package/dist/commands/workflow/create.js +2 -4
- package/dist/commands/workflow/delete.d.ts +1 -0
- package/dist/commands/workflow/delete.js +21 -33
- package/dist/commands/workflow/index.d.ts +1 -0
- package/dist/commands/workflow/list.d.ts +1 -0
- package/dist/commands/workflow/list.js +3 -6
- package/dist/commands/workflow/switch.d.ts +2 -0
- package/dist/commands/workflow/switch.js +46 -21
- package/dist/commands/workflow/view.d.ts +1 -0
- package/dist/commands/workflow/view.js +18 -27
- package/dist/commands/workspace/remove.d.ts +2 -2
- package/dist/commands/workspace/remove.js +16 -21
- package/dist/commands/workspace/use.d.ts +2 -2
- package/dist/commands/workspace/use.js +12 -18
- package/dist/lib/agents/commands.d.ts +1 -1
- package/dist/lib/agents/commands.js +4 -4
- package/dist/lib/database/drizzle-schema.d.ts +5009 -0
- package/dist/lib/database/drizzle-schema.js +699 -0
- package/dist/lib/database/drizzle.d.ts +29 -0
- package/dist/lib/database/drizzle.js +37 -0
- package/dist/lib/database/index.d.ts +1 -0
- package/dist/lib/database/index.js +1 -1
- package/dist/lib/execution/config.d.ts +6 -0
- package/dist/lib/execution/config.js +31 -13
- package/dist/lib/execution/devcontainer.js +13 -7
- package/dist/lib/execution/runners.js +24 -7
- package/dist/lib/execution/spawner.js +19 -0
- package/dist/lib/flags/index.d.ts +4 -0
- package/dist/lib/flags/index.js +4 -0
- package/dist/lib/flags/resolver.d.ts +224 -0
- package/dist/lib/flags/resolver.js +313 -0
- package/dist/lib/pmo/base-command.d.ts +53 -3
- package/dist/lib/pmo/base-command.js +92 -13
- package/dist/lib/pmo/find-pmo.d.ts +1 -1
- package/dist/lib/pmo/find-pmo.js +4 -4
- package/dist/lib/pmo/index.d.ts +1 -1
- package/dist/lib/pmo/index.js +1 -1
- package/dist/lib/pmo/storage/helpers.js +2 -1
- package/dist/lib/pmo/storage/index.d.ts +9 -0
- package/dist/lib/pmo/storage/index.js +14 -0
- package/dist/lib/pmo/storage/projects.d.ts +28 -13
- package/dist/lib/pmo/storage/projects.js +110 -34
- package/dist/lib/pmo/storage/roadmaps.d.ts +2 -0
- package/dist/lib/pmo/storage/roadmaps.js +182 -111
- package/dist/lib/pmo/storage/specs.js +13 -16
- package/dist/lib/pmo/storage/subtasks.js +17 -2
- package/dist/lib/pmo/storage/tickets.d.ts +12 -2
- package/dist/lib/pmo/storage/tickets.js +63 -5
- package/dist/lib/pmo/storage/types.d.ts +7 -3
- package/dist/lib/pmo/storage/views.d.ts +12 -1
- package/dist/lib/pmo/storage/views.js +61 -6
- package/dist/lib/prompt-command.d.ts +90 -0
- package/dist/lib/prompt-command.js +102 -0
- package/dist/lib/prompt-json.d.ts +34 -4
- package/dist/lib/prompt-json.js +35 -3
- package/dist/lib/repos/index.js +15 -15
- package/dist/lib/workspace.d.ts +4 -3
- package/dist/lib/workspace.js +3 -3
- package/oclif.manifest.json +4610 -2997
- package/package.json +13 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Roadmap operations for PMO.
|
|
3
3
|
* Roadmaps are curated collections of projects for documentation/visualization.
|
|
4
|
+
*
|
|
5
|
+
* This module uses Drizzle ORM for type-safe database queries.
|
|
4
6
|
*/
|
|
5
|
-
import {
|
|
7
|
+
import { eq, and, like, or, desc, asc, sql, gte, gt, lt, lte } from 'drizzle-orm';
|
|
8
|
+
import { pmoRoadmaps, pmoRoadmapProjects, pmoProjects, } from '../../database/drizzle-schema.js';
|
|
6
9
|
import { PMOError } from '../types.js';
|
|
7
|
-
const T = PMO_TABLES;
|
|
8
10
|
export class RoadmapStorage {
|
|
9
11
|
ctx;
|
|
10
12
|
constructor(ctx) {
|
|
@@ -16,15 +18,22 @@ export class RoadmapStorage {
|
|
|
16
18
|
*/
|
|
17
19
|
async createRoadmap(roadmap) {
|
|
18
20
|
const id = roadmap.id || `roadmap-${Date.now()}`;
|
|
19
|
-
const now = Date.
|
|
21
|
+
const now = new Date().toISOString();
|
|
20
22
|
// If setting as default, unset other defaults first
|
|
21
23
|
if (roadmap.isDefault) {
|
|
22
|
-
this.ctx.
|
|
24
|
+
this.ctx.drizzle
|
|
25
|
+
.update(pmoRoadmaps)
|
|
26
|
+
.set({ isDefault: false })
|
|
27
|
+
.run();
|
|
23
28
|
}
|
|
24
|
-
this.ctx.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
this.ctx.drizzle.insert(pmoRoadmaps).values({
|
|
30
|
+
id,
|
|
31
|
+
name: roadmap.name,
|
|
32
|
+
description: roadmap.description || null,
|
|
33
|
+
isDefault: roadmap.isDefault || false,
|
|
34
|
+
createdAt: now,
|
|
35
|
+
updatedAt: now,
|
|
36
|
+
}).run();
|
|
28
37
|
return {
|
|
29
38
|
id,
|
|
30
39
|
name: roadmap.name,
|
|
@@ -38,9 +47,11 @@ export class RoadmapStorage {
|
|
|
38
47
|
* Get a roadmap by ID.
|
|
39
48
|
*/
|
|
40
49
|
async getRoadmap(id) {
|
|
41
|
-
const row = this.ctx.
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
const row = this.ctx.drizzle
|
|
51
|
+
.select()
|
|
52
|
+
.from(pmoRoadmaps)
|
|
53
|
+
.where(eq(pmoRoadmaps.id, id))
|
|
54
|
+
.get();
|
|
44
55
|
if (!row)
|
|
45
56
|
return null;
|
|
46
57
|
return this.rowToRoadmap(row);
|
|
@@ -49,18 +60,23 @@ export class RoadmapStorage {
|
|
|
49
60
|
* List roadmaps with optional filters.
|
|
50
61
|
*/
|
|
51
62
|
async listRoadmaps(filter) {
|
|
52
|
-
let query =
|
|
53
|
-
|
|
63
|
+
let query = this.ctx.drizzle
|
|
64
|
+
.select()
|
|
65
|
+
.from(pmoRoadmaps)
|
|
66
|
+
.$dynamic();
|
|
67
|
+
const conditions = [];
|
|
54
68
|
if (filter?.search) {
|
|
55
|
-
|
|
56
|
-
params.push(`%${filter.search}%`, `%${filter.search}%`);
|
|
69
|
+
conditions.push(or(like(pmoRoadmaps.name, `%${filter.search}%`), like(pmoRoadmaps.description, `%${filter.search}%`)));
|
|
57
70
|
}
|
|
58
71
|
if (filter?.isDefault !== undefined) {
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
conditions.push(eq(pmoRoadmaps.isDefault, filter.isDefault));
|
|
73
|
+
}
|
|
74
|
+
if (conditions.length > 0) {
|
|
75
|
+
query = query.where(and(...conditions));
|
|
61
76
|
}
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
const rows = query
|
|
78
|
+
.orderBy(desc(pmoRoadmaps.isDefault), asc(pmoRoadmaps.name))
|
|
79
|
+
.all();
|
|
64
80
|
return rows.map(row => this.rowToRoadmap(row));
|
|
65
81
|
}
|
|
66
82
|
/**
|
|
@@ -71,29 +87,30 @@ export class RoadmapStorage {
|
|
|
71
87
|
if (!roadmap) {
|
|
72
88
|
throw new PMOError('NOT_FOUND', `Roadmap not found: ${id}`);
|
|
73
89
|
}
|
|
74
|
-
const updates =
|
|
75
|
-
const params = [];
|
|
90
|
+
const updates = {};
|
|
76
91
|
if (changes.name !== undefined) {
|
|
77
|
-
updates.
|
|
78
|
-
params.push(changes.name);
|
|
92
|
+
updates.name = changes.name;
|
|
79
93
|
}
|
|
80
94
|
if (changes.description !== undefined) {
|
|
81
|
-
updates.
|
|
82
|
-
params.push(changes.description || null);
|
|
95
|
+
updates.description = changes.description || null;
|
|
83
96
|
}
|
|
84
97
|
if (changes.isDefault !== undefined) {
|
|
85
98
|
// If setting as default, unset other defaults first
|
|
86
99
|
if (changes.isDefault) {
|
|
87
|
-
this.ctx.
|
|
100
|
+
this.ctx.drizzle
|
|
101
|
+
.update(pmoRoadmaps)
|
|
102
|
+
.set({ isDefault: false })
|
|
103
|
+
.run();
|
|
88
104
|
}
|
|
89
|
-
updates.
|
|
90
|
-
params.push(changes.isDefault ? 1 : 0);
|
|
105
|
+
updates.isDefault = changes.isDefault;
|
|
91
106
|
}
|
|
92
|
-
if (updates.length > 0) {
|
|
93
|
-
updates.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
if (Object.keys(updates).length > 0) {
|
|
108
|
+
updates.updatedAt = new Date().toISOString();
|
|
109
|
+
this.ctx.drizzle
|
|
110
|
+
.update(pmoRoadmaps)
|
|
111
|
+
.set(updates)
|
|
112
|
+
.where(eq(pmoRoadmaps.id, id))
|
|
113
|
+
.run();
|
|
97
114
|
}
|
|
98
115
|
return (await this.getRoadmap(id));
|
|
99
116
|
}
|
|
@@ -106,15 +123,21 @@ export class RoadmapStorage {
|
|
|
106
123
|
throw new PMOError('NOT_FOUND', `Roadmap not found: ${id}`);
|
|
107
124
|
}
|
|
108
125
|
// Delete roadmap (cascades to roadmap_projects)
|
|
109
|
-
this.ctx.
|
|
126
|
+
this.ctx.drizzle
|
|
127
|
+
.delete(pmoRoadmaps)
|
|
128
|
+
.where(eq(pmoRoadmaps.id, id))
|
|
129
|
+
.run();
|
|
110
130
|
}
|
|
111
131
|
/**
|
|
112
132
|
* Get the default roadmap.
|
|
113
133
|
*/
|
|
114
134
|
async getDefaultRoadmap() {
|
|
115
|
-
const row = this.ctx.
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
const row = this.ctx.drizzle
|
|
136
|
+
.select()
|
|
137
|
+
.from(pmoRoadmaps)
|
|
138
|
+
.where(eq(pmoRoadmaps.isDefault, true))
|
|
139
|
+
.limit(1)
|
|
140
|
+
.get();
|
|
118
141
|
if (!row)
|
|
119
142
|
return null;
|
|
120
143
|
return this.rowToRoadmap(row);
|
|
@@ -130,12 +153,26 @@ export class RoadmapStorage {
|
|
|
130
153
|
* List projects in a roadmap, ordered by position.
|
|
131
154
|
*/
|
|
132
155
|
async listRoadmapProjects(roadmapId) {
|
|
133
|
-
const rows = this.ctx.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
156
|
+
const rows = this.ctx.drizzle
|
|
157
|
+
.select({
|
|
158
|
+
id: pmoProjects.id,
|
|
159
|
+
name: pmoProjects.name,
|
|
160
|
+
template: pmoProjects.template,
|
|
161
|
+
description: pmoProjects.description,
|
|
162
|
+
status: pmoProjects.status,
|
|
163
|
+
phaseId: pmoProjects.phaseId,
|
|
164
|
+
workflowId: pmoProjects.workflowId,
|
|
165
|
+
isArchived: pmoProjects.isArchived,
|
|
166
|
+
targetDate: pmoProjects.targetDate,
|
|
167
|
+
initiativeId: pmoProjects.initiativeId,
|
|
168
|
+
createdAt: pmoProjects.createdAt,
|
|
169
|
+
updatedAt: pmoProjects.updatedAt,
|
|
170
|
+
})
|
|
171
|
+
.from(pmoProjects)
|
|
172
|
+
.innerJoin(pmoRoadmapProjects, eq(pmoProjects.id, pmoRoadmapProjects.projectId))
|
|
173
|
+
.where(eq(pmoRoadmapProjects.roadmapId, roadmapId))
|
|
174
|
+
.orderBy(asc(pmoRoadmapProjects.position))
|
|
175
|
+
.all();
|
|
139
176
|
return rows.map(row => this.rowToProject(row));
|
|
140
177
|
}
|
|
141
178
|
/**
|
|
@@ -148,34 +185,44 @@ export class RoadmapStorage {
|
|
|
148
185
|
throw new PMOError('NOT_FOUND', `Roadmap not found: ${roadmapId}`);
|
|
149
186
|
}
|
|
150
187
|
// Check if project is already in roadmap
|
|
151
|
-
const existing = this.ctx.
|
|
152
|
-
|
|
153
|
-
|
|
188
|
+
const existing = this.ctx.drizzle
|
|
189
|
+
.select()
|
|
190
|
+
.from(pmoRoadmapProjects)
|
|
191
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), eq(pmoRoadmapProjects.projectId, projectId)))
|
|
192
|
+
.get();
|
|
154
193
|
if (existing) {
|
|
155
194
|
throw new PMOError('CONFLICT', `Project ${projectId} is already in roadmap ${roadmapId}`);
|
|
156
195
|
}
|
|
157
196
|
// Get next position if not provided
|
|
158
197
|
if (position === undefined) {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
198
|
+
const maxPosResult = this.ctx.drizzle
|
|
199
|
+
.select({ maxPos: sql `COALESCE(MAX(${pmoRoadmapProjects.position}), -1)` })
|
|
200
|
+
.from(pmoRoadmapProjects)
|
|
201
|
+
.where(eq(pmoRoadmapProjects.roadmapId, roadmapId))
|
|
202
|
+
.get();
|
|
203
|
+
position = (maxPosResult?.maxPos ?? -1) + 1;
|
|
163
204
|
}
|
|
164
205
|
else {
|
|
165
206
|
// Shift existing projects at or after this position
|
|
166
|
-
this.ctx.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
207
|
+
this.ctx.drizzle
|
|
208
|
+
.update(pmoRoadmapProjects)
|
|
209
|
+
.set({ position: sql `${pmoRoadmapProjects.position} + 1` })
|
|
210
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), gte(pmoRoadmapProjects.position, position)))
|
|
211
|
+
.run();
|
|
171
212
|
}
|
|
172
|
-
const now = Date.
|
|
173
|
-
this.ctx.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
213
|
+
const now = new Date().toISOString();
|
|
214
|
+
this.ctx.drizzle.insert(pmoRoadmapProjects).values({
|
|
215
|
+
roadmapId,
|
|
216
|
+
projectId,
|
|
217
|
+
position,
|
|
218
|
+
createdAt: now,
|
|
219
|
+
}).run();
|
|
177
220
|
// Update roadmap timestamp
|
|
178
|
-
this.ctx.
|
|
221
|
+
this.ctx.drizzle
|
|
222
|
+
.update(pmoRoadmaps)
|
|
223
|
+
.set({ updatedAt: now })
|
|
224
|
+
.where(eq(pmoRoadmaps.id, roadmapId))
|
|
225
|
+
.run();
|
|
179
226
|
return {
|
|
180
227
|
roadmapId,
|
|
181
228
|
projectId,
|
|
@@ -188,33 +235,45 @@ export class RoadmapStorage {
|
|
|
188
235
|
*/
|
|
189
236
|
async removeProjectFromRoadmap(roadmapId, projectId) {
|
|
190
237
|
// Get current position for reordering
|
|
191
|
-
const current = this.ctx.
|
|
192
|
-
|
|
193
|
-
|
|
238
|
+
const current = this.ctx.drizzle
|
|
239
|
+
.select({ position: pmoRoadmapProjects.position })
|
|
240
|
+
.from(pmoRoadmapProjects)
|
|
241
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), eq(pmoRoadmapProjects.projectId, projectId)))
|
|
242
|
+
.get();
|
|
194
243
|
if (!current) {
|
|
195
244
|
throw new PMOError('NOT_FOUND', `Project ${projectId} not in roadmap ${roadmapId}`);
|
|
196
245
|
}
|
|
197
246
|
// Delete the association
|
|
198
|
-
this.ctx.
|
|
199
|
-
|
|
200
|
-
|
|
247
|
+
this.ctx.drizzle
|
|
248
|
+
.delete(pmoRoadmapProjects)
|
|
249
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), eq(pmoRoadmapProjects.projectId, projectId)))
|
|
250
|
+
.run();
|
|
201
251
|
// Reorder remaining projects to fill the gap
|
|
202
|
-
this.ctx.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
252
|
+
this.ctx.drizzle
|
|
253
|
+
.update(pmoRoadmapProjects)
|
|
254
|
+
.set({ position: sql `${pmoRoadmapProjects.position} - 1` })
|
|
255
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), gt(pmoRoadmapProjects.position, current.position)))
|
|
256
|
+
.run();
|
|
207
257
|
// Update roadmap timestamp
|
|
208
|
-
this.ctx.
|
|
258
|
+
this.ctx.drizzle
|
|
259
|
+
.update(pmoRoadmaps)
|
|
260
|
+
.set({ updatedAt: new Date().toISOString() })
|
|
261
|
+
.where(eq(pmoRoadmaps.id, roadmapId))
|
|
262
|
+
.run();
|
|
209
263
|
}
|
|
210
264
|
/**
|
|
211
265
|
* Reorder a project within a roadmap.
|
|
212
266
|
*/
|
|
213
267
|
async reorderRoadmapProject(roadmapId, projectId, newPosition) {
|
|
214
268
|
// Get current position
|
|
215
|
-
const current = this.ctx.
|
|
216
|
-
|
|
217
|
-
|
|
269
|
+
const current = this.ctx.drizzle
|
|
270
|
+
.select({
|
|
271
|
+
position: pmoRoadmapProjects.position,
|
|
272
|
+
createdAt: pmoRoadmapProjects.createdAt,
|
|
273
|
+
})
|
|
274
|
+
.from(pmoRoadmapProjects)
|
|
275
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), eq(pmoRoadmapProjects.projectId, projectId)))
|
|
276
|
+
.get();
|
|
218
277
|
if (!current) {
|
|
219
278
|
throw new PMOError('NOT_FOUND', `Project ${projectId} not in roadmap ${roadmapId}`);
|
|
220
279
|
}
|
|
@@ -224,51 +283,63 @@ export class RoadmapStorage {
|
|
|
224
283
|
roadmapId,
|
|
225
284
|
projectId,
|
|
226
285
|
position: newPosition,
|
|
227
|
-
createdAt: new Date(current.
|
|
286
|
+
createdAt: new Date(current.createdAt),
|
|
228
287
|
};
|
|
229
288
|
}
|
|
230
|
-
// Shift positions
|
|
289
|
+
// Shift positions
|
|
231
290
|
if (newPosition < oldPosition) {
|
|
232
291
|
// Moving up: increment positions in between
|
|
233
|
-
this.ctx.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
292
|
+
this.ctx.drizzle
|
|
293
|
+
.update(pmoRoadmapProjects)
|
|
294
|
+
.set({ position: sql `${pmoRoadmapProjects.position} + 1` })
|
|
295
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), gte(pmoRoadmapProjects.position, newPosition), lt(pmoRoadmapProjects.position, oldPosition)))
|
|
296
|
+
.run();
|
|
238
297
|
}
|
|
239
298
|
else {
|
|
240
299
|
// Moving down: decrement positions in between
|
|
241
|
-
this.ctx.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
300
|
+
this.ctx.drizzle
|
|
301
|
+
.update(pmoRoadmapProjects)
|
|
302
|
+
.set({ position: sql `${pmoRoadmapProjects.position} - 1` })
|
|
303
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), gt(pmoRoadmapProjects.position, oldPosition), lte(pmoRoadmapProjects.position, newPosition)))
|
|
304
|
+
.run();
|
|
246
305
|
}
|
|
247
306
|
// Update the target project's position
|
|
248
|
-
this.ctx.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
307
|
+
this.ctx.drizzle
|
|
308
|
+
.update(pmoRoadmapProjects)
|
|
309
|
+
.set({ position: newPosition })
|
|
310
|
+
.where(and(eq(pmoRoadmapProjects.roadmapId, roadmapId), eq(pmoRoadmapProjects.projectId, projectId)))
|
|
311
|
+
.run();
|
|
253
312
|
// Update roadmap timestamp
|
|
254
|
-
this.ctx.
|
|
313
|
+
this.ctx.drizzle
|
|
314
|
+
.update(pmoRoadmaps)
|
|
315
|
+
.set({ updatedAt: new Date().toISOString() })
|
|
316
|
+
.where(eq(pmoRoadmaps.id, roadmapId))
|
|
317
|
+
.run();
|
|
255
318
|
return {
|
|
256
319
|
roadmapId,
|
|
257
320
|
projectId,
|
|
258
321
|
position: newPosition,
|
|
259
|
-
createdAt: new Date(current.
|
|
322
|
+
createdAt: new Date(current.createdAt),
|
|
260
323
|
};
|
|
261
324
|
}
|
|
262
325
|
/**
|
|
263
326
|
* Get all roadmaps that contain a project.
|
|
264
327
|
*/
|
|
265
328
|
async getRoadmapsForProject(projectId) {
|
|
266
|
-
const rows = this.ctx.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
329
|
+
const rows = this.ctx.drizzle
|
|
330
|
+
.select({
|
|
331
|
+
id: pmoRoadmaps.id,
|
|
332
|
+
name: pmoRoadmaps.name,
|
|
333
|
+
description: pmoRoadmaps.description,
|
|
334
|
+
isDefault: pmoRoadmaps.isDefault,
|
|
335
|
+
createdAt: pmoRoadmaps.createdAt,
|
|
336
|
+
updatedAt: pmoRoadmaps.updatedAt,
|
|
337
|
+
})
|
|
338
|
+
.from(pmoRoadmaps)
|
|
339
|
+
.innerJoin(pmoRoadmapProjects, eq(pmoRoadmaps.id, pmoRoadmapProjects.roadmapId))
|
|
340
|
+
.where(eq(pmoRoadmapProjects.projectId, projectId))
|
|
341
|
+
.orderBy(asc(pmoRoadmaps.name))
|
|
342
|
+
.all();
|
|
272
343
|
return rows.map(row => this.rowToRoadmap(row));
|
|
273
344
|
}
|
|
274
345
|
// ===== Helpers =====
|
|
@@ -277,9 +348,9 @@ export class RoadmapStorage {
|
|
|
277
348
|
id: row.id,
|
|
278
349
|
name: row.name,
|
|
279
350
|
description: row.description || undefined,
|
|
280
|
-
isDefault: row.
|
|
281
|
-
createdAt: new Date(row.
|
|
282
|
-
updatedAt: new Date(row.
|
|
351
|
+
isDefault: row.isDefault ?? false,
|
|
352
|
+
createdAt: new Date(row.createdAt || Date.now()),
|
|
353
|
+
updatedAt: new Date(row.updatedAt || Date.now()),
|
|
283
354
|
};
|
|
284
355
|
}
|
|
285
356
|
rowToProject(row) {
|
|
@@ -289,13 +360,13 @@ export class RoadmapStorage {
|
|
|
289
360
|
template: row.template || undefined,
|
|
290
361
|
description: row.description || undefined,
|
|
291
362
|
status: (row.status || 'active'),
|
|
292
|
-
phaseId: row.
|
|
293
|
-
workflowId: row.
|
|
294
|
-
isArchived: row.
|
|
295
|
-
targetDate: row.
|
|
296
|
-
initiativeId: row.
|
|
297
|
-
createdAt: new Date(row.
|
|
298
|
-
updatedAt: new Date(row.
|
|
363
|
+
phaseId: row.phaseId || undefined,
|
|
364
|
+
workflowId: row.workflowId || undefined,
|
|
365
|
+
isArchived: row.isArchived ?? false,
|
|
366
|
+
targetDate: row.targetDate ? new Date(row.targetDate) : undefined,
|
|
367
|
+
initiativeId: row.initiativeId || undefined,
|
|
368
|
+
createdAt: new Date(row.createdAt || Date.now()),
|
|
369
|
+
updatedAt: new Date(row.updatedAt || Date.now()),
|
|
299
370
|
};
|
|
300
371
|
}
|
|
301
372
|
}
|
|
@@ -19,21 +19,22 @@ export class SpecStorage {
|
|
|
19
19
|
const now = Date.now();
|
|
20
20
|
this.ctx.db.prepare(`
|
|
21
21
|
INSERT INTO ${T.specs} (
|
|
22
|
-
id, title, status, type, tags,
|
|
22
|
+
id, title, status, type, tags,
|
|
23
23
|
problem, solution, decisions, not_now, ui_ux,
|
|
24
24
|
acceptance_criteria, open_questions,
|
|
25
25
|
requirements_functional, requirements_technical,
|
|
26
26
|
context, created_at, updated_at
|
|
27
27
|
)
|
|
28
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
29
|
-
`).run(id, spec.title || 'Untitled Spec', spec.status || 'draft', spec.type || null, spec.tags ? JSON.stringify(spec.tags) : null, spec.
|
|
28
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
29
|
+
`).run(id, spec.title || 'Untitled Spec', spec.status || 'draft', spec.type || null, spec.tags ? JSON.stringify(spec.tags) : null, spec.problem || null, spec.solution || null, spec.decisions || null, spec.notNow || null, spec.uiUx || null, spec.acceptanceCriteria || null, spec.openQuestions || null, spec.requirementsFunctional || null, spec.requirementsTechnical || null, spec.context || null, now, now);
|
|
30
30
|
return {
|
|
31
31
|
id,
|
|
32
32
|
title: spec.title || 'Untitled Spec',
|
|
33
33
|
status: spec.status || 'draft',
|
|
34
34
|
type: spec.type,
|
|
35
35
|
tags: spec.tags,
|
|
36
|
-
|
|
36
|
+
// Note: dependsOn is now handled via spec_dependencies table
|
|
37
|
+
dependsOn: undefined,
|
|
37
38
|
problem: spec.problem,
|
|
38
39
|
solution: spec.solution,
|
|
39
40
|
decisions: spec.decisions,
|
|
@@ -53,7 +54,7 @@ export class SpecStorage {
|
|
|
53
54
|
*/
|
|
54
55
|
async getSpec(id) {
|
|
55
56
|
const row = this.ctx.db.prepare(`
|
|
56
|
-
SELECT id, title, status, type, tags,
|
|
57
|
+
SELECT id, title, status, type, tags,
|
|
57
58
|
problem, solution, decisions, not_now, ui_ux,
|
|
58
59
|
acceptance_criteria, open_questions,
|
|
59
60
|
requirements_functional, requirements_technical,
|
|
@@ -69,7 +70,7 @@ export class SpecStorage {
|
|
|
69
70
|
*/
|
|
70
71
|
async listSpecs(filter) {
|
|
71
72
|
let query = `
|
|
72
|
-
SELECT id, title, status, type, tags,
|
|
73
|
+
SELECT id, title, status, type, tags,
|
|
73
74
|
problem, solution, decisions, not_now, ui_ux,
|
|
74
75
|
acceptance_criteria, open_questions,
|
|
75
76
|
requirements_functional, requirements_technical,
|
|
@@ -119,10 +120,6 @@ export class SpecStorage {
|
|
|
119
120
|
updates.push('tags = ?');
|
|
120
121
|
params.push(JSON.stringify(changes.tags));
|
|
121
122
|
}
|
|
122
|
-
if (changes.dependsOn !== undefined) {
|
|
123
|
-
updates.push('depends_on = ?');
|
|
124
|
-
params.push(JSON.stringify(changes.dependsOn));
|
|
125
|
-
}
|
|
126
123
|
if (changes.problem !== undefined) {
|
|
127
124
|
updates.push('problem = ?');
|
|
128
125
|
params.push(changes.problem);
|
|
@@ -272,7 +269,7 @@ export class SpecStorage {
|
|
|
272
269
|
throw new PMOError('NOT_FOUND', `Spec not found: ${dependsOnId}`);
|
|
273
270
|
}
|
|
274
271
|
this.ctx.db.prepare(`
|
|
275
|
-
INSERT OR IGNORE INTO ${T.spec_dependencies} (spec_id,
|
|
272
|
+
INSERT OR IGNORE INTO ${T.spec_dependencies} (spec_id, depends_on_spec_id, created_at)
|
|
276
273
|
VALUES (?, ?, ?)
|
|
277
274
|
`).run(specId, dependsOnId, Date.now());
|
|
278
275
|
}
|
|
@@ -282,7 +279,7 @@ export class SpecStorage {
|
|
|
282
279
|
async removeSpecDependency(specId, dependsOnId) {
|
|
283
280
|
this.ctx.db.prepare(`
|
|
284
281
|
DELETE FROM ${T.spec_dependencies}
|
|
285
|
-
WHERE spec_id = ? AND
|
|
282
|
+
WHERE spec_id = ? AND depends_on_spec_id = ?
|
|
286
283
|
`).run(specId, dependsOnId);
|
|
287
284
|
}
|
|
288
285
|
/**
|
|
@@ -290,12 +287,12 @@ export class SpecStorage {
|
|
|
290
287
|
*/
|
|
291
288
|
async getSpecDependencies(specId) {
|
|
292
289
|
const rows = this.ctx.db.prepare(`
|
|
293
|
-
SELECT
|
|
290
|
+
SELECT depends_on_spec_id FROM ${T.spec_dependencies} WHERE spec_id = ?
|
|
294
291
|
`).all(specId);
|
|
295
292
|
const specs = [];
|
|
296
293
|
for (const row of rows) {
|
|
297
294
|
// eslint-disable-next-line no-await-in-loop -- Sequential lookup for relationship chain
|
|
298
|
-
const spec = await this.getSpec(row.
|
|
295
|
+
const spec = await this.getSpec(row.depends_on_spec_id);
|
|
299
296
|
if (spec)
|
|
300
297
|
specs.push(spec);
|
|
301
298
|
}
|
|
@@ -306,7 +303,7 @@ export class SpecStorage {
|
|
|
306
303
|
*/
|
|
307
304
|
async getSpecDependents(specId) {
|
|
308
305
|
const rows = this.ctx.db.prepare(`
|
|
309
|
-
SELECT spec_id FROM ${T.spec_dependencies} WHERE
|
|
306
|
+
SELECT spec_id FROM ${T.spec_dependencies} WHERE depends_on_spec_id = ?
|
|
310
307
|
`).all(specId);
|
|
311
308
|
const specs = [];
|
|
312
309
|
for (const row of rows) {
|
|
@@ -352,7 +349,7 @@ export class SpecStorage {
|
|
|
352
349
|
*/
|
|
353
350
|
async getSpecsForProject(projectId) {
|
|
354
351
|
const rows = this.ctx.db.prepare(`
|
|
355
|
-
SELECT s.id, s.title, s.status, s.type, s.tags,
|
|
352
|
+
SELECT s.id, s.title, s.status, s.type, s.tags,
|
|
356
353
|
s.problem, s.solution, s.decisions, s.not_now, s.ui_ux,
|
|
357
354
|
s.acceptance_criteria, s.open_questions,
|
|
358
355
|
s.requirements_functional, s.requirements_technical,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subtask operations for tickets.
|
|
3
3
|
*/
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
4
5
|
import { PMO_TABLES } from '../schema.js';
|
|
5
6
|
import { PMOError } from '../types.js';
|
|
6
7
|
import { slugify } from '../utils.js';
|
|
@@ -26,7 +27,20 @@ export class SubtaskStorage {
|
|
|
26
27
|
SELECT COALESCE(MAX(position), -1) as max_pos
|
|
27
28
|
FROM ${T.subtasks} WHERE ticket_id = ?
|
|
28
29
|
`).get(ticketId);
|
|
29
|
-
|
|
30
|
+
// Generate unique ID - start with slugified title, append counter if collision
|
|
31
|
+
const baseId = slugify(title);
|
|
32
|
+
let id = baseId;
|
|
33
|
+
let counter = 1;
|
|
34
|
+
// Check for existing subtask with same ID and append counter if needed
|
|
35
|
+
while (true) {
|
|
36
|
+
const existing = this.ctx.db.prepare(`
|
|
37
|
+
SELECT 1 FROM ${T.subtasks} WHERE ticket_id = ? AND id = ?
|
|
38
|
+
`).get(ticketId, id);
|
|
39
|
+
if (!existing)
|
|
40
|
+
break;
|
|
41
|
+
counter++;
|
|
42
|
+
id = `${baseId}-${counter}`;
|
|
43
|
+
}
|
|
30
44
|
const position = maxPos.max_pos + 1;
|
|
31
45
|
this.ctx.db.prepare(`
|
|
32
46
|
INSERT INTO ${T.subtasks} (id, ticket_id, title, done, position)
|
|
@@ -122,7 +136,8 @@ export class AcceptanceCriteriaStorage {
|
|
|
122
136
|
SELECT COALESCE(MAX(position), -1) as max_pos
|
|
123
137
|
FROM ${T.ticket_acceptance_criteria} WHERE ticket_id = ?
|
|
124
138
|
`).get(ticketId);
|
|
125
|
-
|
|
139
|
+
// Use UUID to guarantee uniqueness even when multiple ACs are added in the same millisecond
|
|
140
|
+
const id = `ac-${randomUUID()}`;
|
|
126
141
|
const position = maxPos.max_pos + 1;
|
|
127
142
|
this.ctx.db.prepare(`
|
|
128
143
|
INSERT INTO ${T.ticket_acceptance_criteria} (id, ticket_id, criterion, verifiable, verified, position)
|
|
@@ -8,6 +8,16 @@ import { StorageContext } from './types.js';
|
|
|
8
8
|
export declare class TicketStorage {
|
|
9
9
|
private ctx;
|
|
10
10
|
constructor(ctx: StorageContext);
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a project identifier to its actual ID.
|
|
13
|
+
* Tries multiple strategies:
|
|
14
|
+
* 1. Exact ID match
|
|
15
|
+
* 2. Case-insensitive ID match
|
|
16
|
+
* 3. Exact name match
|
|
17
|
+
* 4. Case-insensitive name match
|
|
18
|
+
* 5. Slugified name match
|
|
19
|
+
*/
|
|
20
|
+
private resolveProjectId;
|
|
11
21
|
/**
|
|
12
22
|
* Create a new ticket.
|
|
13
23
|
* Gets default status from the project's workflow.
|
|
@@ -45,10 +55,10 @@ export declare class TicketStorage {
|
|
|
45
55
|
deleteTicket(id: string): Promise<void>;
|
|
46
56
|
/**
|
|
47
57
|
* List tickets with optional filters.
|
|
48
|
-
* @param
|
|
58
|
+
* @param projectIdOrName - The project ID, name, or slug to filter by. Pass undefined to list all tickets across all projects.
|
|
49
59
|
* @param filter - Additional filters to apply.
|
|
50
60
|
*/
|
|
51
|
-
listTickets(
|
|
61
|
+
listTickets(projectIdOrName: string | undefined, filter?: TicketFilter): Promise<Ticket[]>;
|
|
52
62
|
/**
|
|
53
63
|
* Move a ticket to a different project.
|
|
54
64
|
* The ticket will get the default status from the target project's workflow.
|