@proletariat/cli 0.3.35 → 0.3.40
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 +37 -2
- package/bin/dev.js +0 -0
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +9 -19
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/ticket.js +7 -24
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +14 -46
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +7 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +30 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +27 -1
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/pmo/init.js +12 -40
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/qa/index.d.ts +54 -0
- package/dist/commands/qa/index.js +762 -0
- package/dist/commands/repo/view.js +2 -8
- package/dist/commands/session/attach.js +4 -4
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +4 -23
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +9 -8
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/epic.js +17 -43
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/spawn-all.js +1 -1
- package/dist/commands/work/spawn.js +15 -4
- package/dist/commands/work/start.js +186 -103
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/work/watch.js +1 -1
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +7 -7
- package/dist/hooks/init.js +10 -2
- package/dist/lib/agents/commands.d.ts +5 -0
- package/dist/lib/agents/commands.js +143 -97
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/database/drizzle-schema.d.ts +465 -0
- package/dist/lib/database/drizzle-schema.js +53 -0
- package/dist/lib/database/index.d.ts +47 -1
- package/dist/lib/database/index.js +138 -20
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +45 -0
- package/dist/lib/execution/runners.js +187 -26
- package/dist/lib/execution/session-utils.d.ts +16 -1
- package/dist/lib/execution/session-utils.js +71 -4
- package/dist/lib/execution/spawner.js +15 -2
- package/dist/lib/execution/storage.d.ts +6 -1
- package/dist/lib/execution/storage.js +35 -5
- package/dist/lib/execution/types.d.ts +3 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/tmux.d.ts +16 -0
- package/dist/lib/mcp/tools/tmux.js +182 -0
- package/dist/lib/mcp/tools/work.js +148 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -0
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +402 -50
- package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
- package/dist/lib/pmo/storage/dependencies.js +11 -3
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/helpers.d.ts +4 -4
- package/dist/lib/pmo/storage/helpers.js +36 -26
- package/dist/lib/pmo/storage/projects.d.ts +2 -0
- package/dist/lib/pmo/storage/projects.js +207 -119
- package/dist/lib/pmo/storage/specs.d.ts +2 -0
- package/dist/lib/pmo/storage/specs.js +274 -188
- package/dist/lib/pmo/storage/tickets.d.ts +2 -0
- package/dist/lib/pmo/storage/tickets.js +350 -290
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/pmo/storage/views.d.ts +2 -0
- package/dist/lib/pmo/storage/views.js +183 -130
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +41 -4
- package/dist/lib/prompt-json.js +138 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +4046 -3385
- package/package.json +11 -6
- package/LICENSE +0 -190
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Board view operations.
|
|
3
|
+
*
|
|
4
|
+
* This module uses Drizzle ORM for type-safe database queries.
|
|
3
5
|
*/
|
|
4
|
-
import {
|
|
6
|
+
import { eq, and, like, or, asc, desc, sql } from 'drizzle-orm';
|
|
7
|
+
import { pmoBoardViews, pmoProjects, pmoTickets, pmoWorkflowStatuses, pmoSubtasks, pmoTicketMetadata, } from '../../database/drizzle-schema.js';
|
|
5
8
|
import { PMOError, } from '../types.js';
|
|
6
9
|
import { slugify } from '../utils.js';
|
|
7
10
|
import { getAcceptanceCriteriaSync } from './helpers.js';
|
|
8
|
-
const T = PMO_TABLES;
|
|
9
11
|
export class ViewStorage {
|
|
10
12
|
ctx;
|
|
11
13
|
constructor(ctx) {
|
|
@@ -24,33 +26,42 @@ export class ViewStorage {
|
|
|
24
26
|
if (!identifier)
|
|
25
27
|
return null;
|
|
26
28
|
// 1. Exact ID match
|
|
27
|
-
const exactMatch = this.ctx.
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const exactMatch = this.ctx.drizzle
|
|
30
|
+
.select({ id: pmoProjects.id })
|
|
31
|
+
.from(pmoProjects)
|
|
32
|
+
.where(eq(pmoProjects.id, identifier))
|
|
33
|
+
.get();
|
|
30
34
|
if (exactMatch)
|
|
31
35
|
return exactMatch.id;
|
|
32
36
|
// 2. Case-insensitive ID match
|
|
33
|
-
const caseInsensitiveId = this.ctx.
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
const caseInsensitiveId = this.ctx.drizzle
|
|
38
|
+
.select({ id: pmoProjects.id })
|
|
39
|
+
.from(pmoProjects)
|
|
40
|
+
.where(sql `LOWER(${pmoProjects.id}) = LOWER(${identifier})`)
|
|
41
|
+
.get();
|
|
36
42
|
if (caseInsensitiveId)
|
|
37
43
|
return caseInsensitiveId.id;
|
|
38
44
|
// 3. Exact name match
|
|
39
|
-
const nameMatch = this.ctx.
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const nameMatch = this.ctx.drizzle
|
|
46
|
+
.select({ id: pmoProjects.id })
|
|
47
|
+
.from(pmoProjects)
|
|
48
|
+
.where(eq(pmoProjects.name, identifier))
|
|
49
|
+
.get();
|
|
42
50
|
if (nameMatch)
|
|
43
51
|
return nameMatch.id;
|
|
44
52
|
// 4. Case-insensitive name match
|
|
45
|
-
const caseInsensitiveName = this.ctx.
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
const caseInsensitiveName = this.ctx.drizzle
|
|
54
|
+
.select({ id: pmoProjects.id })
|
|
55
|
+
.from(pmoProjects)
|
|
56
|
+
.where(sql `LOWER(${pmoProjects.name}) = LOWER(${identifier})`)
|
|
57
|
+
.get();
|
|
48
58
|
if (caseInsensitiveName)
|
|
49
59
|
return caseInsensitiveName.id;
|
|
50
60
|
// 5. Slugified name match
|
|
51
|
-
const allProjects = this.ctx.
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
const allProjects = this.ctx.drizzle
|
|
62
|
+
.select({ id: pmoProjects.id, name: pmoProjects.name })
|
|
63
|
+
.from(pmoProjects)
|
|
64
|
+
.all();
|
|
54
65
|
const identifierLower = identifier.toLowerCase();
|
|
55
66
|
for (const project of allProjects) {
|
|
56
67
|
const projectSlug = slugify(project.name);
|
|
@@ -64,34 +75,37 @@ export class ViewStorage {
|
|
|
64
75
|
* List board views.
|
|
65
76
|
*/
|
|
66
77
|
async listBoardViews(filter) {
|
|
67
|
-
let
|
|
78
|
+
let query = this.ctx.drizzle
|
|
79
|
+
.select()
|
|
80
|
+
.from(pmoBoardViews)
|
|
81
|
+
.$dynamic();
|
|
68
82
|
const conditions = [];
|
|
69
|
-
const params = [];
|
|
70
83
|
if (filter?.projectId) {
|
|
71
|
-
conditions.push(
|
|
72
|
-
params.push(filter.projectId);
|
|
84
|
+
conditions.push(eq(pmoBoardViews.projectId, filter.projectId));
|
|
73
85
|
}
|
|
74
86
|
if (filter?.isDefault !== undefined) {
|
|
75
|
-
conditions.push(
|
|
76
|
-
params.push(filter.isDefault ? 1 : 0);
|
|
87
|
+
conditions.push(eq(pmoBoardViews.isDefault, filter.isDefault));
|
|
77
88
|
}
|
|
78
89
|
if (filter?.search) {
|
|
79
|
-
conditions.push(
|
|
80
|
-
const searchTerm = `%${filter.search}%`;
|
|
81
|
-
params.push(searchTerm, searchTerm);
|
|
90
|
+
conditions.push(or(like(pmoBoardViews.name, `%${filter.search}%`), like(pmoBoardViews.description, `%${filter.search}%`)));
|
|
82
91
|
}
|
|
83
92
|
if (conditions.length > 0) {
|
|
84
|
-
|
|
93
|
+
query = query.where(and(...conditions));
|
|
85
94
|
}
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
const rows = query
|
|
96
|
+
.orderBy(desc(pmoBoardViews.isDefault), asc(pmoBoardViews.name))
|
|
97
|
+
.all();
|
|
88
98
|
return rows.map((row) => this.rowToBoardView(row));
|
|
89
99
|
}
|
|
90
100
|
/**
|
|
91
101
|
* Get a board view by ID.
|
|
92
102
|
*/
|
|
93
103
|
async getBoardView(id) {
|
|
94
|
-
const row = this.ctx.
|
|
104
|
+
const row = this.ctx.drizzle
|
|
105
|
+
.select()
|
|
106
|
+
.from(pmoBoardViews)
|
|
107
|
+
.where(eq(pmoBoardViews.id, id))
|
|
108
|
+
.get();
|
|
95
109
|
if (!row)
|
|
96
110
|
return null;
|
|
97
111
|
return this.rowToBoardView(row);
|
|
@@ -111,14 +125,24 @@ export class ViewStorage {
|
|
|
111
125
|
const filters = JSON.stringify(view.filters || {});
|
|
112
126
|
// If this is set as default, unset other defaults for this project
|
|
113
127
|
if (view.isDefault) {
|
|
114
|
-
this.ctx.
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
this.ctx.drizzle
|
|
129
|
+
.update(pmoBoardViews)
|
|
130
|
+
.set({ isDefault: false })
|
|
131
|
+
.where(eq(pmoBoardViews.projectId, view.projectId))
|
|
132
|
+
.run();
|
|
117
133
|
}
|
|
118
|
-
this.ctx.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
this.ctx.drizzle.insert(pmoBoardViews).values({
|
|
135
|
+
id,
|
|
136
|
+
projectId: view.projectId,
|
|
137
|
+
name: view.name,
|
|
138
|
+
description: view.description || null,
|
|
139
|
+
isDefault: view.isDefault || false,
|
|
140
|
+
filters,
|
|
141
|
+
groupBy: view.groupBy || null,
|
|
142
|
+
sortBy: view.sortBy || null,
|
|
143
|
+
createdAt: String(now),
|
|
144
|
+
updatedAt: String(now),
|
|
145
|
+
}).run();
|
|
122
146
|
return (await this.getBoardView(id));
|
|
123
147
|
}
|
|
124
148
|
/**
|
|
@@ -129,47 +153,45 @@ export class ViewStorage {
|
|
|
129
153
|
if (!existing) {
|
|
130
154
|
throw new PMOError('NOT_FOUND', `Board view not found: ${id}`);
|
|
131
155
|
}
|
|
132
|
-
const updates =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
updates.push('description = ?');
|
|
140
|
-
params.push(changes.description || null);
|
|
141
|
-
}
|
|
156
|
+
const updates = {
|
|
157
|
+
updatedAt: String(Date.now()),
|
|
158
|
+
};
|
|
159
|
+
if (changes.name !== undefined)
|
|
160
|
+
updates.name = changes.name;
|
|
161
|
+
if (changes.description !== undefined)
|
|
162
|
+
updates.description = changes.description || null;
|
|
142
163
|
if (changes.isDefault !== undefined) {
|
|
143
164
|
// If setting as default, unset other defaults for this project
|
|
144
165
|
if (changes.isDefault) {
|
|
145
|
-
this.ctx.
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
this.ctx.drizzle
|
|
167
|
+
.update(pmoBoardViews)
|
|
168
|
+
.set({ isDefault: false })
|
|
169
|
+
.where(eq(pmoBoardViews.projectId, existing.projectId))
|
|
170
|
+
.run();
|
|
148
171
|
}
|
|
149
|
-
updates.
|
|
150
|
-
params.push(changes.isDefault ? 1 : 0);
|
|
151
|
-
}
|
|
152
|
-
if (changes.filters !== undefined) {
|
|
153
|
-
updates.push('filters = ?');
|
|
154
|
-
params.push(JSON.stringify(changes.filters));
|
|
155
|
-
}
|
|
156
|
-
if (changes.groupBy !== undefined) {
|
|
157
|
-
updates.push('group_by = ?');
|
|
158
|
-
params.push(changes.groupBy || null);
|
|
159
|
-
}
|
|
160
|
-
if (changes.sortBy !== undefined) {
|
|
161
|
-
updates.push('sort_by = ?');
|
|
162
|
-
params.push(changes.sortBy || null);
|
|
172
|
+
updates.isDefault = changes.isDefault;
|
|
163
173
|
}
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
if (changes.filters !== undefined)
|
|
175
|
+
updates.filters = JSON.stringify(changes.filters);
|
|
176
|
+
if (changes.groupBy !== undefined)
|
|
177
|
+
updates.groupBy = changes.groupBy || null;
|
|
178
|
+
if (changes.sortBy !== undefined)
|
|
179
|
+
updates.sortBy = changes.sortBy || null;
|
|
180
|
+
this.ctx.drizzle
|
|
181
|
+
.update(pmoBoardViews)
|
|
182
|
+
.set(updates)
|
|
183
|
+
.where(eq(pmoBoardViews.id, id))
|
|
184
|
+
.run();
|
|
166
185
|
return (await this.getBoardView(id));
|
|
167
186
|
}
|
|
168
187
|
/**
|
|
169
188
|
* Delete a board view.
|
|
170
189
|
*/
|
|
171
190
|
async deleteBoardView(id) {
|
|
172
|
-
const result = this.ctx.
|
|
191
|
+
const result = this.ctx.drizzle
|
|
192
|
+
.delete(pmoBoardViews)
|
|
193
|
+
.where(eq(pmoBoardViews.id, id))
|
|
194
|
+
.run();
|
|
173
195
|
if (result.changes === 0) {
|
|
174
196
|
throw new PMOError('NOT_FOUND', `Board view not found: ${id}`);
|
|
175
197
|
}
|
|
@@ -178,9 +200,11 @@ export class ViewStorage {
|
|
|
178
200
|
* Get the default board view for a project.
|
|
179
201
|
*/
|
|
180
202
|
async getDefaultBoardView(projectId) {
|
|
181
|
-
const row = this.ctx.
|
|
182
|
-
|
|
183
|
-
|
|
203
|
+
const row = this.ctx.drizzle
|
|
204
|
+
.select()
|
|
205
|
+
.from(pmoBoardViews)
|
|
206
|
+
.where(and(eq(pmoBoardViews.projectId, projectId), eq(pmoBoardViews.isDefault, true)))
|
|
207
|
+
.get();
|
|
184
208
|
if (!row)
|
|
185
209
|
return null;
|
|
186
210
|
return this.rowToBoardView(row);
|
|
@@ -208,22 +232,32 @@ export class ViewStorage {
|
|
|
208
232
|
// Override with explicit filters if provided
|
|
209
233
|
const effectiveFilters = { ...viewFilters, ...filters };
|
|
210
234
|
// Get project metadata using resolved ID
|
|
211
|
-
const projectRow = this.ctx.
|
|
235
|
+
const projectRow = this.ctx.drizzle
|
|
236
|
+
.select({
|
|
237
|
+
id: pmoProjects.id,
|
|
238
|
+
name: pmoProjects.name,
|
|
239
|
+
workflowId: pmoProjects.workflowId,
|
|
240
|
+
updatedAt: pmoProjects.updatedAt,
|
|
241
|
+
})
|
|
242
|
+
.from(pmoProjects)
|
|
243
|
+
.where(eq(pmoProjects.id, resolvedId))
|
|
244
|
+
.get();
|
|
212
245
|
if (!projectRow) {
|
|
213
246
|
throw new PMOError('NOT_FOUND', `Project not found: ${projectIdOrName}. Run init() first.`);
|
|
214
247
|
}
|
|
215
|
-
|
|
216
|
-
const projectMeta = this.ctx.db.prepare(`
|
|
217
|
-
SELECT workflow_id FROM ${T.projects} WHERE id = ?
|
|
218
|
-
`).get(resolvedId);
|
|
219
|
-
const workflowId = projectMeta?.workflow_id || 'default';
|
|
248
|
+
const workflowId = projectRow.workflowId || 'default';
|
|
220
249
|
// Get workflow statuses as columns
|
|
221
|
-
const columnRows = this.ctx.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
250
|
+
const columnRows = this.ctx.drizzle
|
|
251
|
+
.select({
|
|
252
|
+
id: pmoWorkflowStatuses.id,
|
|
253
|
+
workflowId: pmoWorkflowStatuses.workflowId,
|
|
254
|
+
name: pmoWorkflowStatuses.name,
|
|
255
|
+
position: pmoWorkflowStatuses.position,
|
|
256
|
+
})
|
|
257
|
+
.from(pmoWorkflowStatuses)
|
|
258
|
+
.where(eq(pmoWorkflowStatuses.workflowId, workflowId))
|
|
259
|
+
.orderBy(asc(pmoWorkflowStatuses.position))
|
|
260
|
+
.all();
|
|
227
261
|
// Filter columns if columnIds filter is set
|
|
228
262
|
const filteredColumnRows = effectiveFilters.columnIds?.length
|
|
229
263
|
? columnRows.filter((col) => effectiveFilters.columnIds.includes(col.id))
|
|
@@ -244,82 +278,101 @@ export class ViewStorage {
|
|
|
244
278
|
id: projectRow.id,
|
|
245
279
|
name: projectRow.name,
|
|
246
280
|
columns,
|
|
247
|
-
updatedAt: new Date(projectRow.
|
|
281
|
+
updatedAt: new Date(projectRow.updatedAt),
|
|
248
282
|
};
|
|
249
283
|
}
|
|
250
284
|
/**
|
|
251
285
|
* Get tickets for a column (workflow status) with filters applied.
|
|
252
286
|
*/
|
|
253
287
|
async getTicketsForColumnWithFilters(columnId, projectId, filters) {
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
ws.name as status_name,
|
|
260
|
-
ws.category as status_category
|
|
261
|
-
FROM ${T.tickets} t
|
|
262
|
-
LEFT JOIN ${T.workflow_statuses} ws ON t.status_id = ws.id
|
|
263
|
-
WHERE t.status_id = ? AND t.project_id = ?
|
|
264
|
-
`;
|
|
265
|
-
const params = [columnId, projectId];
|
|
288
|
+
// Build conditions array for dynamic WHERE clause
|
|
289
|
+
const conditions = [
|
|
290
|
+
eq(pmoTickets.statusId, columnId),
|
|
291
|
+
eq(pmoTickets.projectId, projectId),
|
|
292
|
+
];
|
|
266
293
|
// Apply filters
|
|
267
294
|
if (filters.assignee !== undefined) {
|
|
268
295
|
if (filters.assignee === 'unassigned') {
|
|
269
|
-
sql
|
|
296
|
+
conditions.push(or(sql `${pmoTickets.assignee} IS NULL`, eq(pmoTickets.assignee, '')));
|
|
270
297
|
}
|
|
271
298
|
else {
|
|
272
|
-
|
|
273
|
-
params.push(filters.assignee);
|
|
299
|
+
conditions.push(eq(pmoTickets.assignee, filters.assignee));
|
|
274
300
|
}
|
|
275
301
|
}
|
|
276
302
|
if (filters.owner !== undefined) {
|
|
277
|
-
|
|
278
|
-
params.push(filters.owner);
|
|
303
|
+
conditions.push(eq(pmoTickets.owner, filters.owner));
|
|
279
304
|
}
|
|
280
305
|
if (filters.priority !== undefined) {
|
|
281
|
-
sql
|
|
282
|
-
params.push(filters.priority);
|
|
306
|
+
conditions.push(sql `UPPER(${pmoTickets.priority}) = UPPER(${filters.priority})`);
|
|
283
307
|
}
|
|
284
308
|
if (filters.statusCategory !== undefined) {
|
|
285
|
-
|
|
286
|
-
params.push(filters.statusCategory);
|
|
309
|
+
conditions.push(eq(pmoWorkflowStatuses.category, filters.statusCategory));
|
|
287
310
|
}
|
|
288
311
|
if (filters.statusId !== undefined) {
|
|
289
|
-
|
|
290
|
-
params.push(filters.statusId);
|
|
312
|
+
conditions.push(eq(pmoTickets.statusId, filters.statusId));
|
|
291
313
|
}
|
|
292
314
|
if (filters.epicId !== undefined) {
|
|
293
|
-
|
|
294
|
-
params.push(filters.epicId);
|
|
315
|
+
conditions.push(eq(pmoTickets.epicId, filters.epicId));
|
|
295
316
|
}
|
|
296
317
|
if (filters.search !== undefined) {
|
|
297
|
-
sql += ' AND (t.title LIKE ? OR t.description LIKE ?)';
|
|
298
318
|
const searchTerm = `%${filters.search}%`;
|
|
299
|
-
|
|
319
|
+
conditions.push(or(like(pmoTickets.title, searchTerm), like(pmoTickets.description, searchTerm)));
|
|
300
320
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
const rows = this.ctx.drizzle
|
|
322
|
+
.select({
|
|
323
|
+
id: pmoTickets.id,
|
|
324
|
+
project_id: pmoTickets.projectId,
|
|
325
|
+
title: pmoTickets.title,
|
|
326
|
+
description: pmoTickets.description,
|
|
327
|
+
priority: pmoTickets.priority,
|
|
328
|
+
category: pmoTickets.category,
|
|
329
|
+
status: pmoTickets.status,
|
|
330
|
+
status_id: pmoTickets.statusId,
|
|
331
|
+
owner: pmoTickets.owner,
|
|
332
|
+
assignee: pmoTickets.assignee,
|
|
333
|
+
branch: pmoTickets.branch,
|
|
334
|
+
spec_id: pmoTickets.specId,
|
|
335
|
+
epic_id: pmoTickets.epicId,
|
|
336
|
+
labels: pmoTickets.labels,
|
|
337
|
+
position: pmoTickets.position,
|
|
338
|
+
created_at: pmoTickets.createdAt,
|
|
339
|
+
updated_at: pmoTickets.updatedAt,
|
|
340
|
+
last_synced_from_spec: pmoTickets.lastSyncedFromSpec,
|
|
341
|
+
last_synced_from_board: pmoTickets.lastSyncedFromBoard,
|
|
342
|
+
board_position: pmoWorkflowStatuses.position,
|
|
343
|
+
column_name: pmoWorkflowStatuses.name,
|
|
344
|
+
status_name: pmoWorkflowStatuses.name,
|
|
345
|
+
status_category: pmoWorkflowStatuses.category,
|
|
346
|
+
})
|
|
347
|
+
.from(pmoTickets)
|
|
348
|
+
.leftJoin(pmoWorkflowStatuses, eq(pmoTickets.statusId, pmoWorkflowStatuses.id))
|
|
349
|
+
.where(and(...conditions))
|
|
350
|
+
.orderBy(asc(pmoTickets.position), asc(pmoTickets.createdAt))
|
|
351
|
+
.all();
|
|
304
352
|
return Promise.all(rows.map((row) => this.rowToTicketWithColumn(row)));
|
|
305
353
|
}
|
|
306
354
|
async rowToTicketWithColumn(row) {
|
|
307
355
|
// Get subtasks
|
|
308
|
-
const subtaskRows = this.ctx.
|
|
309
|
-
|
|
310
|
-
|
|
356
|
+
const subtaskRows = this.ctx.drizzle
|
|
357
|
+
.select()
|
|
358
|
+
.from(pmoSubtasks)
|
|
359
|
+
.where(eq(pmoSubtasks.ticketId, row.id))
|
|
360
|
+
.orderBy(asc(pmoSubtasks.position))
|
|
361
|
+
.all();
|
|
311
362
|
const subtasks = subtaskRows.map((s) => ({
|
|
312
363
|
id: s.id,
|
|
313
364
|
title: s.title,
|
|
314
|
-
done: s.done
|
|
365
|
+
done: s.done ?? false,
|
|
315
366
|
}));
|
|
316
367
|
// Get metadata
|
|
317
|
-
const metadataRows = this.ctx.
|
|
318
|
-
|
|
319
|
-
|
|
368
|
+
const metadataRows = this.ctx.drizzle
|
|
369
|
+
.select({ key: pmoTicketMetadata.key, value: pmoTicketMetadata.value })
|
|
370
|
+
.from(pmoTicketMetadata)
|
|
371
|
+
.where(eq(pmoTicketMetadata.ticketId, row.id))
|
|
372
|
+
.all();
|
|
320
373
|
const metadata = {};
|
|
321
374
|
for (const m of metadataRows) {
|
|
322
|
-
metadata[m.key] = m.value;
|
|
375
|
+
metadata[m.key] = m.value || '';
|
|
323
376
|
}
|
|
324
377
|
// Parse labels from JSON
|
|
325
378
|
let labels = [];
|
|
@@ -347,9 +400,9 @@ export class ViewStorage {
|
|
|
347
400
|
subtasks,
|
|
348
401
|
labels,
|
|
349
402
|
metadata,
|
|
350
|
-
acceptanceCriteria: getAcceptanceCriteriaSync(this.ctx.
|
|
351
|
-
createdAt: new Date(row.created_at),
|
|
352
|
-
updatedAt: new Date(row.updated_at),
|
|
403
|
+
acceptanceCriteria: getAcceptanceCriteriaSync(this.ctx.drizzle, row.id),
|
|
404
|
+
createdAt: new Date(row.created_at || Date.now()),
|
|
405
|
+
updatedAt: new Date(row.updated_at || Date.now()),
|
|
353
406
|
lastSyncedFromSpec: row.last_synced_from_spec
|
|
354
407
|
? new Date(row.last_synced_from_spec)
|
|
355
408
|
: undefined,
|
|
@@ -388,15 +441,15 @@ export class ViewStorage {
|
|
|
388
441
|
rowToBoardView(row) {
|
|
389
442
|
return {
|
|
390
443
|
id: row.id,
|
|
391
|
-
projectId: row.
|
|
444
|
+
projectId: row.projectId,
|
|
392
445
|
name: row.name,
|
|
393
446
|
description: row.description || undefined,
|
|
394
|
-
isDefault: row.
|
|
447
|
+
isDefault: row.isDefault ?? false,
|
|
395
448
|
filters: row.filters ? JSON.parse(row.filters) : {},
|
|
396
|
-
groupBy: row.
|
|
397
|
-
sortBy: row.
|
|
398
|
-
createdAt: new Date(row.
|
|
399
|
-
updatedAt: new Date(row.
|
|
449
|
+
groupBy: row.groupBy,
|
|
450
|
+
sortBy: row.sortBy,
|
|
451
|
+
createdAt: new Date(row.createdAt || Date.now()),
|
|
452
|
+
updatedAt: new Date(row.updatedAt || Date.now()),
|
|
400
453
|
};
|
|
401
454
|
}
|
|
402
455
|
}
|
|
@@ -30,6 +30,26 @@ import { type JsonFlags } from './prompt-json.js';
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
export declare abstract class PromptCommand extends Command {
|
|
33
|
+
/**
|
|
34
|
+
* TTY-aware log method - strips ANSI codes and emoji in non-TTY mode.
|
|
35
|
+
*
|
|
36
|
+
* Use this instead of this.log() when outputting styled text (chalk colors, emoji prefixes).
|
|
37
|
+
* In TTY mode, outputs styled text as-is. In non-TTY mode, strips ANSI and emoji.
|
|
38
|
+
*
|
|
39
|
+
* @param message - The styled message (may contain ANSI codes and emoji)
|
|
40
|
+
* @param args - Additional arguments passed to this.log()
|
|
41
|
+
*/
|
|
42
|
+
protected logPlain(message?: string, ...args: string[]): void;
|
|
43
|
+
/**
|
|
44
|
+
* Check if plain output mode is active (non-TTY, PRLT_PLAIN, NO_COLOR).
|
|
45
|
+
* Convenience wrapper for use in commands.
|
|
46
|
+
*/
|
|
47
|
+
protected get isPlain(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Check if running in non-TTY environment.
|
|
50
|
+
* Convenience wrapper for use in commands.
|
|
51
|
+
*/
|
|
52
|
+
protected get isNonTTY(): boolean;
|
|
33
53
|
/**
|
|
34
54
|
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
35
55
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import { isAgentMode, outputPromptAsJson, createMetadata, normalizeChoices, } from './prompt-json.js';
|
|
3
|
+
import { isAgentMode, isNonTTY, outputPromptAsJson, createMetadata, normalizeChoices, } from './prompt-json.js';
|
|
4
|
+
import { isPlainOutput, plainText } from './styles.js';
|
|
4
5
|
/**
|
|
5
6
|
* Lightweight base command with prompt() method for JSON mode support.
|
|
6
7
|
*
|
|
@@ -31,6 +32,41 @@ import { isAgentMode, outputPromptAsJson, createMetadata, normalizeChoices, } fr
|
|
|
31
32
|
* ```
|
|
32
33
|
*/
|
|
33
34
|
export class PromptCommand extends Command {
|
|
35
|
+
/**
|
|
36
|
+
* TTY-aware log method - strips ANSI codes and emoji in non-TTY mode.
|
|
37
|
+
*
|
|
38
|
+
* Use this instead of this.log() when outputting styled text (chalk colors, emoji prefixes).
|
|
39
|
+
* In TTY mode, outputs styled text as-is. In non-TTY mode, strips ANSI and emoji.
|
|
40
|
+
*
|
|
41
|
+
* @param message - The styled message (may contain ANSI codes and emoji)
|
|
42
|
+
* @param args - Additional arguments passed to this.log()
|
|
43
|
+
*/
|
|
44
|
+
logPlain(message, ...args) {
|
|
45
|
+
if (message === undefined) {
|
|
46
|
+
this.log();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (isPlainOutput()) {
|
|
50
|
+
this.log(plainText(message), ...args);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
this.log(message, ...args);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if plain output mode is active (non-TTY, PRLT_PLAIN, NO_COLOR).
|
|
58
|
+
* Convenience wrapper for use in commands.
|
|
59
|
+
*/
|
|
60
|
+
get isPlain() {
|
|
61
|
+
return isPlainOutput();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if running in non-TTY environment.
|
|
65
|
+
* Convenience wrapper for use in commands.
|
|
66
|
+
*/
|
|
67
|
+
get isNonTTY() {
|
|
68
|
+
return isNonTTY();
|
|
69
|
+
}
|
|
34
70
|
/**
|
|
35
71
|
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
36
72
|
*
|
|
@@ -73,7 +109,7 @@ export class PromptCommand extends Command {
|
|
|
73
109
|
*/
|
|
74
110
|
async prompt(questions, jsonModeConfig) {
|
|
75
111
|
// Auto-detect non-TTY: switch to JSON mode when no TTY present
|
|
76
|
-
if (!jsonModeConfig &&
|
|
112
|
+
if (!jsonModeConfig && isNonTTY()) {
|
|
77
113
|
jsonModeConfig = { flags: { json: true }, commandName: this.id ?? 'unknown' };
|
|
78
114
|
}
|
|
79
115
|
// Check for JSON/agent mode
|
|
@@ -86,6 +86,8 @@ export interface OutputMetadata {
|
|
|
86
86
|
flags: Record<string, unknown>;
|
|
87
87
|
/** Timestamp of the output */
|
|
88
88
|
timestamp?: string;
|
|
89
|
+
/** Resolved PR mode after flag precedence ('create-pr' | 'no-pr') */
|
|
90
|
+
resolvedPRMode?: string;
|
|
89
91
|
}
|
|
90
92
|
/**
|
|
91
93
|
* JSON output when a prompt would be shown
|
|
@@ -193,6 +195,27 @@ export interface ExecutionResultJsonOutput {
|
|
|
193
195
|
* Union type for all JSON output types
|
|
194
196
|
*/
|
|
195
197
|
export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput | DryRunJsonOutput | ConfirmationNeededJsonOutput | ExecutionResultJsonOutput;
|
|
198
|
+
/**
|
|
199
|
+
* All valid JSON envelope type discriminators.
|
|
200
|
+
* Used for contract tests and schema validation.
|
|
201
|
+
*/
|
|
202
|
+
export declare const JSON_ENVELOPE_TYPES: readonly ["prompt", "success", "error", "dry-run", "confirmation_needed", "execution_result"];
|
|
203
|
+
export type JsonEnvelopeType = typeof JSON_ENVELOPE_TYPES[number];
|
|
204
|
+
/**
|
|
205
|
+
* Required fields per envelope type for contract validation.
|
|
206
|
+
* Tests use this to verify no fields are accidentally removed.
|
|
207
|
+
*/
|
|
208
|
+
export declare const JSON_ENVELOPE_REQUIRED_FIELDS: Record<JsonEnvelopeType, string[]>;
|
|
209
|
+
/**
|
|
210
|
+
* Validate that a parsed JSON object conforms to the machine-mode envelope schema.
|
|
211
|
+
*
|
|
212
|
+
* Returns an array of validation errors (empty = valid).
|
|
213
|
+
* Useful for contract tests and runtime validation of JSON output.
|
|
214
|
+
*
|
|
215
|
+
* @param obj - Parsed JSON object to validate
|
|
216
|
+
* @returns Array of validation error strings (empty if valid)
|
|
217
|
+
*/
|
|
218
|
+
export declare function validateJsonEnvelope(obj: unknown): string[];
|
|
196
219
|
/**
|
|
197
220
|
* Flags interface for JSON mode detection
|
|
198
221
|
*/
|
|
@@ -211,9 +234,21 @@ export interface MachineOutputFlags {
|
|
|
211
234
|
machine?: boolean;
|
|
212
235
|
}
|
|
213
236
|
/**
|
|
214
|
-
* Check if the current environment is non-TTY (piped output)
|
|
237
|
+
* Check if the current environment is non-TTY (piped input or output)
|
|
238
|
+
*
|
|
239
|
+
* Uses the "either" strategy: returns true if EITHER stdin OR stdout is non-TTY.
|
|
240
|
+
* This covers the primary use case of scripts/agents calling prlt as a subprocess,
|
|
241
|
+
* where both stdin and stdout are typically non-TTY.
|
|
242
|
+
*
|
|
243
|
+
* Returns true if:
|
|
244
|
+
* - stdin is not a TTY (e.g., piped input)
|
|
245
|
+
* - stdout is not a TTY (e.g., piped output)
|
|
246
|
+
* - PRLT_JSON=1 environment variable is set (overrides TTY detection)
|
|
247
|
+
*
|
|
248
|
+
* Returns false if:
|
|
249
|
+
* - PRLT_FORCE_TEXT=1 is set (forces text output in non-TTY environments, useful for testing)
|
|
215
250
|
*
|
|
216
|
-
* @returns true if stdout is not a TTY
|
|
251
|
+
* @returns true if either stdin or stdout is not a TTY, or PRLT_JSON=1 is set
|
|
217
252
|
*/
|
|
218
253
|
export declare function isNonTTY(): boolean;
|
|
219
254
|
/**
|
|
@@ -221,7 +256,8 @@ export declare function isNonTTY(): boolean;
|
|
|
221
256
|
*
|
|
222
257
|
* Returns true if:
|
|
223
258
|
* - The --json flag is set (or -m/--machine aliases)
|
|
224
|
-
* - The environment is
|
|
259
|
+
* - The PRLT_JSON=1 environment variable is set
|
|
260
|
+
* - Either stdin or stdout is non-TTY (piped input/output)
|
|
225
261
|
*
|
|
226
262
|
* @param flags - Command flags object
|
|
227
263
|
* @returns true if JSON mode should be used
|
|
@@ -236,7 +272,8 @@ export declare const isAgentMode: typeof shouldOutputJson;
|
|
|
236
272
|
*
|
|
237
273
|
* Returns true if:
|
|
238
274
|
* - The --json flag is set (or -m/--machine aliases)
|
|
239
|
-
* - The environment is
|
|
275
|
+
* - The PRLT_JSON=1 environment variable is set
|
|
276
|
+
* - Either stdin or stdout is non-TTY (piped input/output)
|
|
240
277
|
*
|
|
241
278
|
* @param flags - Command flags object
|
|
242
279
|
* @returns true if machine-readable output mode should be used
|