@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
|
@@ -13,6 +13,55 @@ export class TicketStorage {
|
|
|
13
13
|
constructor(ctx) {
|
|
14
14
|
this.ctx = ctx;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a project identifier to its actual ID.
|
|
18
|
+
* Tries multiple strategies:
|
|
19
|
+
* 1. Exact ID match
|
|
20
|
+
* 2. Case-insensitive ID match
|
|
21
|
+
* 3. Exact name match
|
|
22
|
+
* 4. Case-insensitive name match
|
|
23
|
+
* 5. Slugified name match
|
|
24
|
+
*/
|
|
25
|
+
resolveProjectId(identifier) {
|
|
26
|
+
if (!identifier)
|
|
27
|
+
return null;
|
|
28
|
+
// 1. Exact ID match
|
|
29
|
+
const exactMatch = this.ctx.db.prepare(`
|
|
30
|
+
SELECT id FROM ${T.projects} WHERE id = ?
|
|
31
|
+
`).get(identifier);
|
|
32
|
+
if (exactMatch)
|
|
33
|
+
return exactMatch.id;
|
|
34
|
+
// 2. Case-insensitive ID match
|
|
35
|
+
const caseInsensitiveId = this.ctx.db.prepare(`
|
|
36
|
+
SELECT id FROM ${T.projects} WHERE LOWER(id) = LOWER(?)
|
|
37
|
+
`).get(identifier);
|
|
38
|
+
if (caseInsensitiveId)
|
|
39
|
+
return caseInsensitiveId.id;
|
|
40
|
+
// 3. Exact name match
|
|
41
|
+
const nameMatch = this.ctx.db.prepare(`
|
|
42
|
+
SELECT id FROM ${T.projects} WHERE name = ?
|
|
43
|
+
`).get(identifier);
|
|
44
|
+
if (nameMatch)
|
|
45
|
+
return nameMatch.id;
|
|
46
|
+
// 4. Case-insensitive name match
|
|
47
|
+
const caseInsensitiveName = this.ctx.db.prepare(`
|
|
48
|
+
SELECT id FROM ${T.projects} WHERE LOWER(name) = LOWER(?)
|
|
49
|
+
`).get(identifier);
|
|
50
|
+
if (caseInsensitiveName)
|
|
51
|
+
return caseInsensitiveName.id;
|
|
52
|
+
// 5. Slugified name match
|
|
53
|
+
const allProjects = this.ctx.db.prepare(`
|
|
54
|
+
SELECT id, name FROM ${T.projects}
|
|
55
|
+
`).all();
|
|
56
|
+
const identifierLower = identifier.toLowerCase();
|
|
57
|
+
for (const project of allProjects) {
|
|
58
|
+
const projectSlug = slugify(project.name);
|
|
59
|
+
if (projectSlug === identifierLower || projectSlug === identifier) {
|
|
60
|
+
return project.id;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
16
65
|
/**
|
|
17
66
|
* Create a new ticket.
|
|
18
67
|
* Gets default status from the project's workflow.
|
|
@@ -298,11 +347,20 @@ export class TicketStorage {
|
|
|
298
347
|
}
|
|
299
348
|
/**
|
|
300
349
|
* List tickets with optional filters.
|
|
301
|
-
* @param
|
|
350
|
+
* @param projectIdOrName - The project ID, name, or slug to filter by. Pass undefined to list all tickets across all projects.
|
|
302
351
|
* @param filter - Additional filters to apply.
|
|
303
352
|
*/
|
|
304
|
-
async listTickets(
|
|
353
|
+
async listTickets(projectIdOrName, filter) {
|
|
305
354
|
const params = [];
|
|
355
|
+
// Resolve project identifier to actual ID if provided
|
|
356
|
+
let resolvedProjectId;
|
|
357
|
+
if (projectIdOrName !== undefined) {
|
|
358
|
+
resolvedProjectId = this.resolveProjectId(projectIdOrName) || undefined;
|
|
359
|
+
// If resolution fails, use original value to allow normal "not found" behavior
|
|
360
|
+
if (!resolvedProjectId) {
|
|
361
|
+
resolvedProjectId = projectIdOrName;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
306
364
|
// Build the base query using workflow_statuses
|
|
307
365
|
let query = `
|
|
308
366
|
SELECT t.*,
|
|
@@ -316,9 +374,9 @@ export class TicketStorage {
|
|
|
316
374
|
WHERE 1=1
|
|
317
375
|
`;
|
|
318
376
|
// Apply project scoping
|
|
319
|
-
if (
|
|
377
|
+
if (resolvedProjectId !== undefined) {
|
|
320
378
|
query += ' AND t.project_id = ?';
|
|
321
|
-
params.push(
|
|
379
|
+
params.push(resolvedProjectId);
|
|
322
380
|
}
|
|
323
381
|
// If projectId is undefined, list all tickets across all projects
|
|
324
382
|
if (filter?.statusId) {
|
|
@@ -363,7 +421,7 @@ export class TicketStorage {
|
|
|
363
421
|
params.push(filter.column);
|
|
364
422
|
}
|
|
365
423
|
// Order by project, then status position, then priority, then created_at
|
|
366
|
-
if (
|
|
424
|
+
if (projectIdOrName === undefined) {
|
|
367
425
|
query += ` ORDER BY p.name, ws.position,
|
|
368
426
|
CASE t.priority
|
|
369
427
|
WHEN 'P0' THEN 0
|
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
* These types are shared between storage modules but not exported publicly.
|
|
4
4
|
*/
|
|
5
5
|
import Database from 'better-sqlite3';
|
|
6
|
+
import { DrizzleDB } from '../../database/drizzle.js';
|
|
6
7
|
/**
|
|
7
8
|
* Base context passed to all storage modules.
|
|
8
|
-
* Contains
|
|
9
|
+
* Contains both raw SQLite and Drizzle connections for gradual migration.
|
|
10
|
+
* Project ID is passed explicitly to operations.
|
|
9
11
|
*/
|
|
10
12
|
export interface StorageContext {
|
|
11
|
-
/**
|
|
13
|
+
/** Raw better-sqlite3 database connection (for legacy queries) */
|
|
12
14
|
db: Database.Database;
|
|
15
|
+
/** Drizzle ORM database connection (for type-safe queries) */
|
|
16
|
+
drizzle: DrizzleDB;
|
|
13
17
|
/** Update the board timestamp for a project */
|
|
14
18
|
updateBoardTimestamp: (projectId: string) => void;
|
|
15
19
|
}
|
|
@@ -57,7 +61,7 @@ export interface SpecRow {
|
|
|
57
61
|
status: string;
|
|
58
62
|
type: string | null;
|
|
59
63
|
tags: string | null;
|
|
60
|
-
depends_on
|
|
64
|
+
depends_on?: string | null;
|
|
61
65
|
problem: string | null;
|
|
62
66
|
solution: string | null;
|
|
63
67
|
decisions: string | null;
|
|
@@ -6,6 +6,16 @@ import { StorageContext } from './types.js';
|
|
|
6
6
|
export declare class ViewStorage {
|
|
7
7
|
private ctx;
|
|
8
8
|
constructor(ctx: StorageContext);
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a project identifier to its actual ID.
|
|
11
|
+
* Tries multiple strategies:
|
|
12
|
+
* 1. Exact ID match
|
|
13
|
+
* 2. Case-insensitive ID match
|
|
14
|
+
* 3. Exact name match
|
|
15
|
+
* 4. Case-insensitive name match
|
|
16
|
+
* 5. Slugified name match
|
|
17
|
+
*/
|
|
18
|
+
private resolveProjectId;
|
|
9
19
|
/**
|
|
10
20
|
* List board views.
|
|
11
21
|
*/
|
|
@@ -32,8 +42,9 @@ export declare class ViewStorage {
|
|
|
32
42
|
getDefaultBoardView(projectId: string): Promise<BoardView | null>;
|
|
33
43
|
/**
|
|
34
44
|
* Get board with optional filters applied.
|
|
45
|
+
* @param projectIdOrName - Project ID, name, or slug. Will be resolved to actual ID.
|
|
35
46
|
*/
|
|
36
|
-
getBoardWithView(
|
|
47
|
+
getBoardWithView(projectIdOrName: string, viewId?: string, filters?: BoardViewFilters): Promise<Board>;
|
|
37
48
|
/**
|
|
38
49
|
* Get tickets for a column (workflow status) with filters applied.
|
|
39
50
|
*/
|
|
@@ -11,6 +11,55 @@ export class ViewStorage {
|
|
|
11
11
|
constructor(ctx) {
|
|
12
12
|
this.ctx = ctx;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a project identifier to its actual ID.
|
|
16
|
+
* Tries multiple strategies:
|
|
17
|
+
* 1. Exact ID match
|
|
18
|
+
* 2. Case-insensitive ID match
|
|
19
|
+
* 3. Exact name match
|
|
20
|
+
* 4. Case-insensitive name match
|
|
21
|
+
* 5. Slugified name match
|
|
22
|
+
*/
|
|
23
|
+
resolveProjectId(identifier) {
|
|
24
|
+
if (!identifier)
|
|
25
|
+
return null;
|
|
26
|
+
// 1. Exact ID match
|
|
27
|
+
const exactMatch = this.ctx.db.prepare(`
|
|
28
|
+
SELECT id FROM ${T.projects} WHERE id = ?
|
|
29
|
+
`).get(identifier);
|
|
30
|
+
if (exactMatch)
|
|
31
|
+
return exactMatch.id;
|
|
32
|
+
// 2. Case-insensitive ID match
|
|
33
|
+
const caseInsensitiveId = this.ctx.db.prepare(`
|
|
34
|
+
SELECT id FROM ${T.projects} WHERE LOWER(id) = LOWER(?)
|
|
35
|
+
`).get(identifier);
|
|
36
|
+
if (caseInsensitiveId)
|
|
37
|
+
return caseInsensitiveId.id;
|
|
38
|
+
// 3. Exact name match
|
|
39
|
+
const nameMatch = this.ctx.db.prepare(`
|
|
40
|
+
SELECT id FROM ${T.projects} WHERE name = ?
|
|
41
|
+
`).get(identifier);
|
|
42
|
+
if (nameMatch)
|
|
43
|
+
return nameMatch.id;
|
|
44
|
+
// 4. Case-insensitive name match
|
|
45
|
+
const caseInsensitiveName = this.ctx.db.prepare(`
|
|
46
|
+
SELECT id FROM ${T.projects} WHERE LOWER(name) = LOWER(?)
|
|
47
|
+
`).get(identifier);
|
|
48
|
+
if (caseInsensitiveName)
|
|
49
|
+
return caseInsensitiveName.id;
|
|
50
|
+
// 5. Slugified name match
|
|
51
|
+
const allProjects = this.ctx.db.prepare(`
|
|
52
|
+
SELECT id, name FROM ${T.projects}
|
|
53
|
+
`).all();
|
|
54
|
+
const identifierLower = identifier.toLowerCase();
|
|
55
|
+
for (const project of allProjects) {
|
|
56
|
+
const projectSlug = slugify(project.name);
|
|
57
|
+
if (projectSlug === identifierLower || projectSlug === identifier) {
|
|
58
|
+
return project.id;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
14
63
|
/**
|
|
15
64
|
* List board views.
|
|
16
65
|
*/
|
|
@@ -138,8 +187,14 @@ export class ViewStorage {
|
|
|
138
187
|
}
|
|
139
188
|
/**
|
|
140
189
|
* Get board with optional filters applied.
|
|
190
|
+
* @param projectIdOrName - Project ID, name, or slug. Will be resolved to actual ID.
|
|
141
191
|
*/
|
|
142
|
-
async getBoardWithView(
|
|
192
|
+
async getBoardWithView(projectIdOrName, viewId, filters) {
|
|
193
|
+
// Resolve project identifier to actual ID
|
|
194
|
+
const resolvedId = this.resolveProjectId(projectIdOrName);
|
|
195
|
+
if (!resolvedId) {
|
|
196
|
+
throw new PMOError('NOT_FOUND', `Project not found: ${projectIdOrName}. Run init() first.`);
|
|
197
|
+
}
|
|
143
198
|
let viewFilters = {};
|
|
144
199
|
let viewSortBy;
|
|
145
200
|
// Load view if specified
|
|
@@ -152,15 +207,15 @@ export class ViewStorage {
|
|
|
152
207
|
}
|
|
153
208
|
// Override with explicit filters if provided
|
|
154
209
|
const effectiveFilters = { ...viewFilters, ...filters };
|
|
155
|
-
// Get project metadata
|
|
156
|
-
const projectRow = this.ctx.db.prepare(`SELECT * FROM ${T.projects} WHERE id = ?`).get(
|
|
210
|
+
// Get project metadata using resolved ID
|
|
211
|
+
const projectRow = this.ctx.db.prepare(`SELECT * FROM ${T.projects} WHERE id = ?`).get(resolvedId);
|
|
157
212
|
if (!projectRow) {
|
|
158
|
-
throw new PMOError('NOT_FOUND', `Project not found: ${
|
|
213
|
+
throw new PMOError('NOT_FOUND', `Project not found: ${projectIdOrName}. Run init() first.`);
|
|
159
214
|
}
|
|
160
215
|
// Get the project's workflow
|
|
161
216
|
const projectMeta = this.ctx.db.prepare(`
|
|
162
217
|
SELECT workflow_id FROM ${T.projects} WHERE id = ?
|
|
163
|
-
`).get(
|
|
218
|
+
`).get(resolvedId);
|
|
164
219
|
const workflowId = projectMeta?.workflow_id || 'default';
|
|
165
220
|
// Get workflow statuses as columns
|
|
166
221
|
const columnRows = this.ctx.db.prepare(`
|
|
@@ -175,7 +230,7 @@ export class ViewStorage {
|
|
|
175
230
|
: columnRows;
|
|
176
231
|
// Get tickets with filters applied
|
|
177
232
|
const columns = await Promise.all(filteredColumnRows.map(async (col) => {
|
|
178
|
-
const tickets = await this.getTicketsForColumnWithFilters(col.id,
|
|
233
|
+
const tickets = await this.getTicketsForColumnWithFilters(col.id, resolvedId, effectiveFilters);
|
|
179
234
|
// Apply sorting if specified
|
|
180
235
|
const sortedTickets = viewSortBy ? this.sortTickets(tickets, viewSortBy) : tickets;
|
|
181
236
|
return {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { type JsonFlags } from './prompt-json.js';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight base command with prompt() method for JSON mode support.
|
|
5
|
+
*
|
|
6
|
+
* Extends oclif Command with a drop-in replacement for inquirer.prompt()
|
|
7
|
+
* that works in both interactive and JSON/agent modes. Use this as the
|
|
8
|
+
* base class for commands that need interactive prompts but don't require
|
|
9
|
+
* PMO database context (for PMO commands, use PMOCommand which extends this).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { PromptCommand } from '../../lib/prompt-command.js';
|
|
14
|
+
*
|
|
15
|
+
* export default class MyCommand extends PromptCommand {
|
|
16
|
+
* async run(): Promise<void> {
|
|
17
|
+
* const { flags } = await this.parse(MyCommand);
|
|
18
|
+
* const { selection } = await this.prompt([{
|
|
19
|
+
* type: 'list',
|
|
20
|
+
* name: 'selection',
|
|
21
|
+
* message: 'Pick one:',
|
|
22
|
+
* choices: items.map(i => ({
|
|
23
|
+
* name: i.name,
|
|
24
|
+
* value: i.id,
|
|
25
|
+
* command: `prlt my-command --selection "${i.id}" --json`,
|
|
26
|
+
* })),
|
|
27
|
+
* }], { flags, commandName: 'my-command' });
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare abstract class PromptCommand extends Command {
|
|
33
|
+
/**
|
|
34
|
+
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
35
|
+
*
|
|
36
|
+
* Works in BOTH modes:
|
|
37
|
+
* - Interactive mode: calls inquirer.prompt normally (human sees menu)
|
|
38
|
+
* - JSON/Agent mode: outputs prompt as structured JSON and exits
|
|
39
|
+
*
|
|
40
|
+
* This is the simplest way to make any inquirer.prompt call work for both humans and agents.
|
|
41
|
+
* Just replace `await inquirer.prompt(questions)` with `await this.prompt(questions, jsonModeConfig)`
|
|
42
|
+
*
|
|
43
|
+
* @param questions - Inquirer question config(s)
|
|
44
|
+
* @param jsonModeConfig - JSON mode configuration (null to disable JSON mode handling)
|
|
45
|
+
* @returns Answers object (only in interactive mode)
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Before (breaks in JSON mode):
|
|
50
|
+
* const { column } = await inquirer.prompt([{
|
|
51
|
+
* type: 'list',
|
|
52
|
+
* name: 'column',
|
|
53
|
+
* message: 'Select column:',
|
|
54
|
+
* choices: columns.map(c => ({ name: c, value: c })),
|
|
55
|
+
* }]);
|
|
56
|
+
*
|
|
57
|
+
* // After (works in both modes):
|
|
58
|
+
* const { column } = await this.prompt([{
|
|
59
|
+
* type: 'list',
|
|
60
|
+
* name: 'column',
|
|
61
|
+
* message: 'Select column:',
|
|
62
|
+
* choices: columns.map(c => ({
|
|
63
|
+
* name: c,
|
|
64
|
+
* value: c,
|
|
65
|
+
* command: `prlt ticket move --column "${c}" --json`,
|
|
66
|
+
* })),
|
|
67
|
+
* }], {
|
|
68
|
+
* flags,
|
|
69
|
+
* commandName: 'ticket move',
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
protected prompt<T extends Record<string, unknown>>(questions: Array<{
|
|
74
|
+
type: string;
|
|
75
|
+
name: string;
|
|
76
|
+
message: string;
|
|
77
|
+
choices?: Array<string | {
|
|
78
|
+
name: string;
|
|
79
|
+
value: unknown;
|
|
80
|
+
disabled?: boolean | string;
|
|
81
|
+
command?: string;
|
|
82
|
+
} | unknown>;
|
|
83
|
+
default?: unknown;
|
|
84
|
+
validate?: (input: unknown) => boolean | string;
|
|
85
|
+
when?: boolean | ((answers: Record<string, unknown>) => boolean);
|
|
86
|
+
}>, jsonModeConfig?: {
|
|
87
|
+
flags: JsonFlags & Record<string, unknown>;
|
|
88
|
+
commandName: string;
|
|
89
|
+
} | null): Promise<T>;
|
|
90
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { isAgentMode, outputPromptAsJson, createMetadata, normalizeChoices, } from './prompt-json.js';
|
|
4
|
+
/**
|
|
5
|
+
* Lightweight base command with prompt() method for JSON mode support.
|
|
6
|
+
*
|
|
7
|
+
* Extends oclif Command with a drop-in replacement for inquirer.prompt()
|
|
8
|
+
* that works in both interactive and JSON/agent modes. Use this as the
|
|
9
|
+
* base class for commands that need interactive prompts but don't require
|
|
10
|
+
* PMO database context (for PMO commands, use PMOCommand which extends this).
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { PromptCommand } from '../../lib/prompt-command.js';
|
|
15
|
+
*
|
|
16
|
+
* export default class MyCommand extends PromptCommand {
|
|
17
|
+
* async run(): Promise<void> {
|
|
18
|
+
* const { flags } = await this.parse(MyCommand);
|
|
19
|
+
* const { selection } = await this.prompt([{
|
|
20
|
+
* type: 'list',
|
|
21
|
+
* name: 'selection',
|
|
22
|
+
* message: 'Pick one:',
|
|
23
|
+
* choices: items.map(i => ({
|
|
24
|
+
* name: i.name,
|
|
25
|
+
* value: i.id,
|
|
26
|
+
* command: `prlt my-command --selection "${i.id}" --json`,
|
|
27
|
+
* })),
|
|
28
|
+
* }], { flags, commandName: 'my-command' });
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class PromptCommand extends Command {
|
|
34
|
+
/**
|
|
35
|
+
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
36
|
+
*
|
|
37
|
+
* Works in BOTH modes:
|
|
38
|
+
* - Interactive mode: calls inquirer.prompt normally (human sees menu)
|
|
39
|
+
* - JSON/Agent mode: outputs prompt as structured JSON and exits
|
|
40
|
+
*
|
|
41
|
+
* This is the simplest way to make any inquirer.prompt call work for both humans and agents.
|
|
42
|
+
* Just replace `await inquirer.prompt(questions)` with `await this.prompt(questions, jsonModeConfig)`
|
|
43
|
+
*
|
|
44
|
+
* @param questions - Inquirer question config(s)
|
|
45
|
+
* @param jsonModeConfig - JSON mode configuration (null to disable JSON mode handling)
|
|
46
|
+
* @returns Answers object (only in interactive mode)
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // Before (breaks in JSON mode):
|
|
51
|
+
* const { column } = await inquirer.prompt([{
|
|
52
|
+
* type: 'list',
|
|
53
|
+
* name: 'column',
|
|
54
|
+
* message: 'Select column:',
|
|
55
|
+
* choices: columns.map(c => ({ name: c, value: c })),
|
|
56
|
+
* }]);
|
|
57
|
+
*
|
|
58
|
+
* // After (works in both modes):
|
|
59
|
+
* const { column } = await this.prompt([{
|
|
60
|
+
* type: 'list',
|
|
61
|
+
* name: 'column',
|
|
62
|
+
* message: 'Select column:',
|
|
63
|
+
* choices: columns.map(c => ({
|
|
64
|
+
* name: c,
|
|
65
|
+
* value: c,
|
|
66
|
+
* command: `prlt ticket move --column "${c}" --json`,
|
|
67
|
+
* })),
|
|
68
|
+
* }], {
|
|
69
|
+
* flags,
|
|
70
|
+
* commandName: 'ticket move',
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
async prompt(questions, jsonModeConfig) {
|
|
75
|
+
// Auto-detect non-TTY: switch to JSON mode when no TTY present
|
|
76
|
+
if (!jsonModeConfig && !process.stdin.isTTY) {
|
|
77
|
+
jsonModeConfig = { flags: { json: true }, commandName: this.id ?? 'unknown' };
|
|
78
|
+
}
|
|
79
|
+
// Check for JSON/agent mode
|
|
80
|
+
if (jsonModeConfig && isAgentMode(jsonModeConfig.flags)) {
|
|
81
|
+
// Find first question that should be shown (respecting 'when' conditions)
|
|
82
|
+
const firstQuestion = questions[0];
|
|
83
|
+
if (firstQuestion) {
|
|
84
|
+
// Convert choices to agent-compatible format
|
|
85
|
+
const choices = firstQuestion.choices
|
|
86
|
+
? normalizeChoices(firstQuestion.choices)
|
|
87
|
+
: undefined;
|
|
88
|
+
outputPromptAsJson({
|
|
89
|
+
type: firstQuestion.type,
|
|
90
|
+
name: firstQuestion.name,
|
|
91
|
+
message: firstQuestion.message,
|
|
92
|
+
choices,
|
|
93
|
+
default: firstQuestion.default,
|
|
94
|
+
}, createMetadata(jsonModeConfig.commandName, jsonModeConfig.flags));
|
|
95
|
+
// outputPromptAsJson calls process.exit, never returns
|
|
96
|
+
}
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
// Interactive mode: just call inquirer
|
|
100
|
+
return inquirer.prompt(questions);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -128,11 +128,22 @@ export interface ErrorJsonOutput {
|
|
|
128
128
|
*/
|
|
129
129
|
export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput;
|
|
130
130
|
/**
|
|
131
|
-
* Flags interface for
|
|
131
|
+
* Flags interface for JSON mode detection (legacy)
|
|
132
|
+
* @deprecated Use MachineOutputFlags instead
|
|
132
133
|
*/
|
|
133
134
|
export interface JsonFlags {
|
|
134
135
|
json?: boolean;
|
|
135
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Flags interface for machine-readable output mode detection
|
|
139
|
+
* Supports both new --machine format and legacy --json boolean
|
|
140
|
+
*/
|
|
141
|
+
export interface MachineOutputFlags {
|
|
142
|
+
/** New format: --machine (enables machine-readable JSON output for AI agents) */
|
|
143
|
+
machine?: boolean;
|
|
144
|
+
/** Legacy format: --json (deprecated, use --machine instead) */
|
|
145
|
+
json?: boolean;
|
|
146
|
+
}
|
|
136
147
|
/**
|
|
137
148
|
* Check if the current environment is non-TTY (piped output)
|
|
138
149
|
*
|
|
@@ -140,16 +151,35 @@ export interface JsonFlags {
|
|
|
140
151
|
*/
|
|
141
152
|
export declare function isNonTTY(): boolean;
|
|
142
153
|
/**
|
|
143
|
-
* Determine if JSON output
|
|
154
|
+
* Determine if JSON output mode is active (for AI agents)
|
|
144
155
|
*
|
|
145
156
|
* Returns true if:
|
|
157
|
+
* - The --machine flag is explicitly set
|
|
146
158
|
* - The --json flag is explicitly set
|
|
147
159
|
* - The environment is non-TTY (piped output)
|
|
148
160
|
*
|
|
149
161
|
* @param flags - Command flags object
|
|
150
|
-
* @returns true if JSON
|
|
162
|
+
* @returns true if JSON mode should be used
|
|
163
|
+
*/
|
|
164
|
+
export declare function shouldOutputJson(flags: JsonFlags & {
|
|
165
|
+
machine?: boolean;
|
|
166
|
+
}): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Alias for shouldOutputJson - clearer name for agent-focused code
|
|
169
|
+
*/
|
|
170
|
+
export declare const isAgentMode: typeof shouldOutputJson;
|
|
171
|
+
/**
|
|
172
|
+
* Determine if machine-readable output mode is active (for AI agents/scripts)
|
|
173
|
+
*
|
|
174
|
+
* Returns true if:
|
|
175
|
+
* - The --machine flag is set
|
|
176
|
+
* - The --json flag is explicitly set (legacy support)
|
|
177
|
+
* - The environment is non-TTY (piped output)
|
|
178
|
+
*
|
|
179
|
+
* @param flags - Command flags object
|
|
180
|
+
* @returns true if machine-readable output mode should be used
|
|
151
181
|
*/
|
|
152
|
-
export declare function
|
|
182
|
+
export declare function isMachineOutput(flags: MachineOutputFlags): boolean;
|
|
153
183
|
/**
|
|
154
184
|
* Create metadata object for JSON output
|
|
155
185
|
*
|
package/dist/lib/prompt-json.js
CHANGED
|
@@ -38,17 +38,49 @@ export function isNonTTY() {
|
|
|
38
38
|
return !process.stdout.isTTY;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
* Determine if JSON output
|
|
41
|
+
* Determine if JSON output mode is active (for AI agents)
|
|
42
42
|
*
|
|
43
43
|
* Returns true if:
|
|
44
|
+
* - The --machine flag is explicitly set
|
|
44
45
|
* - The --json flag is explicitly set
|
|
45
46
|
* - The environment is non-TTY (piped output)
|
|
46
47
|
*
|
|
47
48
|
* @param flags - Command flags object
|
|
48
|
-
* @returns true if JSON
|
|
49
|
+
* @returns true if JSON mode should be used
|
|
49
50
|
*/
|
|
50
51
|
export function shouldOutputJson(flags) {
|
|
51
|
-
//
|
|
52
|
+
// New --machine flag
|
|
53
|
+
if (flags.machine === true) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// Legacy --json flag
|
|
57
|
+
if (flags.json === true) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// Automatic detection for non-TTY environments
|
|
61
|
+
return isNonTTY();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Alias for shouldOutputJson - clearer name for agent-focused code
|
|
65
|
+
*/
|
|
66
|
+
export const isAgentMode = shouldOutputJson;
|
|
67
|
+
/**
|
|
68
|
+
* Determine if machine-readable output mode is active (for AI agents/scripts)
|
|
69
|
+
*
|
|
70
|
+
* Returns true if:
|
|
71
|
+
* - The --machine flag is set
|
|
72
|
+
* - The --json flag is explicitly set (legacy support)
|
|
73
|
+
* - The environment is non-TTY (piped output)
|
|
74
|
+
*
|
|
75
|
+
* @param flags - Command flags object
|
|
76
|
+
* @returns true if machine-readable output mode should be used
|
|
77
|
+
*/
|
|
78
|
+
export function isMachineOutput(flags) {
|
|
79
|
+
// New --machine flag takes precedence
|
|
80
|
+
if (flags.machine === true) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Legacy --json flag support
|
|
52
84
|
if (flags.json === true) {
|
|
53
85
|
return true;
|
|
54
86
|
}
|
package/dist/lib/repos/index.js
CHANGED
|
@@ -497,21 +497,21 @@ export async function removeRepository(hqPath, repoName, keepFiles = false) {
|
|
|
497
497
|
*/
|
|
498
498
|
async function createWorktreesForRepo(hqPath, repoName, repoPath) {
|
|
499
499
|
const db = openWorkspaceDatabase(hqPath);
|
|
500
|
-
// Get
|
|
501
|
-
const
|
|
502
|
-
const theme = db.prepare('SELECT workspace_dir FROM themes WHERE name = ?').get(workspace.theme);
|
|
503
|
-
// Get all agents
|
|
504
|
-
const agents = db.prepare('SELECT name FROM agents').all();
|
|
500
|
+
// Get all agents with their worktree paths
|
|
501
|
+
const agents = db.prepare('SELECT name, worktree_path FROM agents WHERE status = \'active\' OR status IS NULL').all();
|
|
505
502
|
if (agents.length === 0) {
|
|
506
503
|
db.close();
|
|
507
504
|
return;
|
|
508
505
|
}
|
|
509
|
-
const agentsBasePath = path.join(hqPath, 'agents', theme.workspace_dir);
|
|
510
506
|
for (const agent of agents) {
|
|
507
|
+
// Skip agents without a worktree path
|
|
508
|
+
if (!agent.worktree_path) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
511
|
// Name worktree directory as {repoName}-{agentName} so git creates unique worktree entries
|
|
512
512
|
// e.g., proletariat-branson instead of proletariat (which causes proletariat1, proletariat2, etc.)
|
|
513
513
|
const worktreeDirName = `${repoName}-${agent.name}`;
|
|
514
|
-
const agentRepoPath = path.join(
|
|
514
|
+
const agentRepoPath = path.join(hqPath, agent.worktree_path, worktreeDirName);
|
|
515
515
|
const branchName = `agent-${agent.name}`;
|
|
516
516
|
try {
|
|
517
517
|
// Create worktree
|
|
@@ -524,7 +524,7 @@ async function createWorktreesForRepo(hqPath, repoName, repoPath) {
|
|
|
524
524
|
INSERT OR REPLACE INTO agent_worktrees
|
|
525
525
|
(agent_name, repo_name, worktree_path, branch, created_at, commits_ahead, is_clean)
|
|
526
526
|
VALUES (?, ?, ?, ?, ?, 0, 1)
|
|
527
|
-
`).run(agent.name, repoName,
|
|
527
|
+
`).run(agent.name, repoName, `${agent.worktree_path}/${worktreeDirName}`, branchName, new Date().toISOString());
|
|
528
528
|
}
|
|
529
529
|
catch {
|
|
530
530
|
console.log(chalk.yellow(`Warning: Could not create worktree for ${agent.name}/${repoName}`));
|
|
@@ -537,15 +537,15 @@ async function createWorktreesForRepo(hqPath, repoName, repoPath) {
|
|
|
537
537
|
*/
|
|
538
538
|
async function removeWorktreesForRepo(hqPath, repoName) {
|
|
539
539
|
const db = openWorkspaceDatabase(hqPath);
|
|
540
|
-
// Get
|
|
541
|
-
const
|
|
542
|
-
const theme = db.prepare('SELECT workspace_dir FROM themes WHERE name = ?').get(workspace.theme);
|
|
543
|
-
// Get all agents
|
|
544
|
-
const agents = db.prepare('SELECT name FROM agents').all();
|
|
540
|
+
// Get all agents with their worktree paths
|
|
541
|
+
const agents = db.prepare('SELECT name, worktree_path FROM agents').all();
|
|
545
542
|
const repoPath = path.join(hqPath, 'repos', repoName);
|
|
546
|
-
const agentsBasePath = path.join(hqPath, 'agents', theme.workspace_dir);
|
|
547
543
|
for (const agent of agents) {
|
|
548
|
-
|
|
544
|
+
// Skip agents without a worktree path
|
|
545
|
+
if (!agent.worktree_path) {
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const agentRepoPath = path.join(hqPath, agent.worktree_path, repoName);
|
|
549
549
|
try {
|
|
550
550
|
// Remove worktree from git
|
|
551
551
|
if (fs.existsSync(repoPath)) {
|
package/dist/lib/workspace.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Central headquarters resolution utilities.
|
|
3
3
|
*
|
|
4
4
|
* Search priority:
|
|
5
|
-
* 1. PRLT_HQ_PATH env var (ONLY when DEVCONTAINER=true
|
|
5
|
+
* 1. PRLT_HQ_PATH env var (ONLY when DEVCONTAINER=true or PRLT_TEST_ENV=true)
|
|
6
6
|
* 2. Walk up directory tree looking for .proletariat/config.json with type='hq'
|
|
7
7
|
* - If found but not registered, emit warning to register
|
|
8
8
|
* 3. ~/.proletariat/config.json activeHeadquarters (fallback when NOT in any HQ)
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* working in different HQs simultaneously. Each agent uses the HQ
|
|
13
13
|
* they're physically in, not a global env var that could cause conflicts.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Environment variables:
|
|
16
|
+
* - DEVCONTAINER=true: Enables PRLT_HQ_PATH (for devcontainer mounts)
|
|
17
|
+
* - PRLT_TEST_ENV=true: Enables PRLT_HQ_PATH (for E2E test isolation)
|
|
17
18
|
*/
|
|
18
19
|
export interface WorkspaceLocation {
|
|
19
20
|
path: string;
|
package/dist/lib/workspace.js
CHANGED
|
@@ -22,11 +22,11 @@ export function findHQRoot(startDir = process.cwd()) {
|
|
|
22
22
|
* Useful for debugging and logging where the workspace was resolved from.
|
|
23
23
|
*/
|
|
24
24
|
export function findHQRootWithSource(startDir = process.cwd()) {
|
|
25
|
-
// 1. Check PRLT_HQ_PATH environment variable (only in devcontainers)
|
|
25
|
+
// 1. Check PRLT_HQ_PATH environment variable (only in devcontainers or test environments)
|
|
26
26
|
// On host machines, we use directory walk to support multiple workspaces/agents
|
|
27
27
|
const envHqPath = process.env.PRLT_HQ_PATH;
|
|
28
|
-
const
|
|
29
|
-
if (envHqPath &&
|
|
28
|
+
const allowEnvHqPath = process.env.DEVCONTAINER === 'true' || process.env.PRLT_TEST_ENV === 'true';
|
|
29
|
+
if (envHqPath && allowEnvHqPath) {
|
|
30
30
|
const resolvedPath = path.resolve(envHqPath);
|
|
31
31
|
// Validate it's actually an HQ
|
|
32
32
|
if (isValidHQ(resolvedPath)) {
|