@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.
Files changed (148) hide show
  1. package/README.md +37 -2
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/agent/auth.d.ts +12 -2
  4. package/dist/commands/agent/auth.js +128 -4
  5. package/dist/commands/agent/list.js +16 -7
  6. package/dist/commands/agent/status.js +32 -4
  7. package/dist/commands/board/watch.js +6 -0
  8. package/dist/commands/branch/list.d.ts +1 -0
  9. package/dist/commands/branch/list.js +43 -12
  10. package/dist/commands/branch/where.js +9 -19
  11. package/dist/commands/category/list.d.ts +2 -1
  12. package/dist/commands/category/list.js +38 -13
  13. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  14. package/dist/commands/{claude.js → claude/index.js} +12 -12
  15. package/dist/commands/claude/open.d.ts +13 -0
  16. package/dist/commands/claude/open.js +175 -0
  17. package/dist/commands/diet.js +18 -2
  18. package/dist/commands/docker/logs.js +7 -3
  19. package/dist/commands/docker/shell.js +6 -0
  20. package/dist/commands/docker/start.js +20 -4
  21. package/dist/commands/docker/sync.d.ts +4 -0
  22. package/dist/commands/docker/sync.js +30 -2
  23. package/dist/commands/epic/show.d.ts +13 -0
  24. package/dist/commands/epic/show.js +16 -0
  25. package/dist/commands/epic/ticket.js +7 -24
  26. package/dist/commands/epic/view.js +27 -0
  27. package/dist/commands/execution/config.d.ts +0 -4
  28. package/dist/commands/execution/config.js +14 -46
  29. package/dist/commands/execution/index.js +2 -1
  30. package/dist/commands/execution/logs.js +7 -1
  31. package/dist/commands/execution/stop.js +2 -1
  32. package/dist/commands/execution/view.js +30 -26
  33. package/dist/commands/init.js +2 -19
  34. package/dist/commands/label/create.js +2 -1
  35. package/dist/commands/label/delete.js +2 -1
  36. package/dist/commands/label/group/create.js +2 -1
  37. package/dist/commands/label/group/list.js +2 -1
  38. package/dist/commands/label/list.js +2 -1
  39. package/dist/commands/mcp-server.js +27 -1
  40. package/dist/commands/phase/template/list.js +2 -1
  41. package/dist/commands/pmo/init.js +12 -40
  42. package/dist/commands/project/create.js +3 -4
  43. package/dist/commands/project/update.js +5 -6
  44. package/dist/commands/pull.js +24 -0
  45. package/dist/commands/qa/index.d.ts +54 -0
  46. package/dist/commands/qa/index.js +762 -0
  47. package/dist/commands/repo/view.js +2 -8
  48. package/dist/commands/session/attach.js +4 -4
  49. package/dist/commands/session/create.d.ts +19 -0
  50. package/dist/commands/session/create.js +102 -0
  51. package/dist/commands/session/health.js +4 -23
  52. package/dist/commands/session/index.js +14 -1
  53. package/dist/commands/session/list.js +9 -8
  54. package/dist/commands/session/peek.d.ts +38 -0
  55. package/dist/commands/session/peek.js +316 -0
  56. package/dist/commands/session/poke.d.ts +27 -0
  57. package/dist/commands/session/poke.js +219 -0
  58. package/dist/commands/spec/view.js +29 -0
  59. package/dist/commands/template/list.js +2 -1
  60. package/dist/commands/theme/add-names.d.ts +4 -0
  61. package/dist/commands/theme/add-names.js +11 -1
  62. package/dist/commands/theme/create.d.ts +2 -0
  63. package/dist/commands/theme/create.js +8 -0
  64. package/dist/commands/ticket/bulk.js +2 -2
  65. package/dist/commands/ticket/complete.js +2 -2
  66. package/dist/commands/ticket/create.js +21 -0
  67. package/dist/commands/ticket/delete.js +8 -0
  68. package/dist/commands/ticket/edit.js +25 -0
  69. package/dist/commands/ticket/epic.js +17 -43
  70. package/dist/commands/ticket/index.js +2 -2
  71. package/dist/commands/ticket/move.js +25 -2
  72. package/dist/commands/ticket/resolve.js +3 -4
  73. package/dist/commands/ticket/show.d.ts +13 -0
  74. package/dist/commands/ticket/show.js +16 -0
  75. package/dist/commands/ticket/template/list.js +2 -1
  76. package/dist/commands/ticket/view.d.ts +0 -1
  77. package/dist/commands/ticket/view.js +30 -1
  78. package/dist/commands/work/index.js +4 -0
  79. package/dist/commands/work/spawn-all.js +1 -1
  80. package/dist/commands/work/spawn.js +15 -4
  81. package/dist/commands/work/start.js +186 -103
  82. package/dist/commands/work/status.d.ts +14 -0
  83. package/dist/commands/work/status.js +60 -0
  84. package/dist/commands/work/watch.js +1 -1
  85. package/dist/commands/workflow/index.js +2 -1
  86. package/dist/commands/workflow/show.d.ts +13 -0
  87. package/dist/commands/workflow/show.js +16 -0
  88. package/dist/commands/workspace/add.js +15 -0
  89. package/dist/commands/workspace/list.js +2 -1
  90. package/dist/commands/workspace/prune.js +7 -7
  91. package/dist/hooks/init.js +10 -2
  92. package/dist/lib/agents/commands.d.ts +5 -0
  93. package/dist/lib/agents/commands.js +143 -97
  94. package/dist/lib/branch/index.d.ts +1 -0
  95. package/dist/lib/database/drizzle-schema.d.ts +465 -0
  96. package/dist/lib/database/drizzle-schema.js +53 -0
  97. package/dist/lib/database/index.d.ts +47 -1
  98. package/dist/lib/database/index.js +138 -20
  99. package/dist/lib/execution/config.d.ts +15 -1
  100. package/dist/lib/execution/config.js +28 -0
  101. package/dist/lib/execution/runners.d.ts +45 -0
  102. package/dist/lib/execution/runners.js +187 -26
  103. package/dist/lib/execution/session-utils.d.ts +16 -1
  104. package/dist/lib/execution/session-utils.js +71 -4
  105. package/dist/lib/execution/spawner.js +15 -2
  106. package/dist/lib/execution/storage.d.ts +6 -1
  107. package/dist/lib/execution/storage.js +35 -5
  108. package/dist/lib/execution/types.d.ts +3 -0
  109. package/dist/lib/mcp/tools/board.js +4 -6
  110. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  111. package/dist/lib/mcp/tools/epic.js +8 -3
  112. package/dist/lib/mcp/tools/index.d.ts +1 -0
  113. package/dist/lib/mcp/tools/index.js +1 -0
  114. package/dist/lib/mcp/tools/spec.js +1 -1
  115. package/dist/lib/mcp/tools/ticket.js +11 -9
  116. package/dist/lib/mcp/tools/tmux.d.ts +16 -0
  117. package/dist/lib/mcp/tools/tmux.js +182 -0
  118. package/dist/lib/mcp/tools/work.js +148 -6
  119. package/dist/lib/mcp/types.d.ts +10 -0
  120. package/dist/lib/multiline-input.js +2 -1
  121. package/dist/lib/pmo/base-command.js +4 -4
  122. package/dist/lib/pmo/schema.d.ts +1 -1
  123. package/dist/lib/pmo/schema.js +1 -0
  124. package/dist/lib/pmo/storage/actions.js +1 -1
  125. package/dist/lib/pmo/storage/base.js +402 -50
  126. package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
  127. package/dist/lib/pmo/storage/dependencies.js +11 -3
  128. package/dist/lib/pmo/storage/epics.js +1 -1
  129. package/dist/lib/pmo/storage/helpers.d.ts +4 -4
  130. package/dist/lib/pmo/storage/helpers.js +36 -26
  131. package/dist/lib/pmo/storage/projects.d.ts +2 -0
  132. package/dist/lib/pmo/storage/projects.js +207 -119
  133. package/dist/lib/pmo/storage/specs.d.ts +2 -0
  134. package/dist/lib/pmo/storage/specs.js +274 -188
  135. package/dist/lib/pmo/storage/tickets.d.ts +2 -0
  136. package/dist/lib/pmo/storage/tickets.js +350 -290
  137. package/dist/lib/pmo/storage/types.d.ts +1 -0
  138. package/dist/lib/pmo/storage/views.d.ts +2 -0
  139. package/dist/lib/pmo/storage/views.js +183 -130
  140. package/dist/lib/prompt-command.d.ts +20 -0
  141. package/dist/lib/prompt-command.js +38 -2
  142. package/dist/lib/prompt-json.d.ts +41 -4
  143. package/dist/lib/prompt-json.js +138 -7
  144. package/dist/lib/styles.d.ts +37 -0
  145. package/dist/lib/styles.js +73 -0
  146. package/oclif.manifest.json +4046 -3385
  147. package/package.json +11 -6
  148. package/LICENSE +0 -190
@@ -3,6 +3,8 @@
3
3
  */
4
4
  import { z } from 'zod';
5
5
  import { formatTicket, errorResponse, strictTool } from '../helpers.js';
6
+ import { spawnAgentForTicket } from '../../execution/spawner.js';
7
+ import { createEphemeralAgent } from '../../agents/commands.js';
6
8
  export function registerWorkTools(server, ctx) {
7
9
  strictTool(server, 'work_status', 'Get current work status (in-progress tickets)', {}, async () => {
8
10
  try {
@@ -31,7 +33,7 @@ export function registerWorkTools(server, ctx) {
31
33
  return errorResponse(error);
32
34
  }
33
35
  });
34
- strictTool(server, 'work_start', 'Start working on a ticket (moves to In Progress)', {
36
+ strictTool(server, 'work_assign', 'Assign a ticket to someone for work preparation (does NOT move to In Progress — use the CLI "work start" command to actually start work with an agent/session)', {
35
37
  ticket_id: z.string().describe('Ticket ID'),
36
38
  assignee: z.string().optional().describe('Who is working'),
37
39
  }, async (params) => {
@@ -42,16 +44,14 @@ export function registerWorkTools(server, ctx) {
42
44
  if (params.assignee) {
43
45
  await ctx.storage.updateTicket(params.ticket_id, { assignee: params.assignee });
44
46
  }
45
- const columns = ctx.storage.getColumnNames(ticket.projectId);
46
- const progressCol = columns.find((c) => c.toLowerCase().includes('progress') || c.toLowerCase().includes('doing')) || columns[Math.min(1, columns.length - 1)];
47
- const moved = await ctx.storage.moveTicket(ticket.projectId, params.ticket_id, progressCol);
47
+ const updated = await ctx.storage.getTicket(params.ticket_id);
48
48
  return {
49
49
  content: [{
50
50
  type: 'text',
51
51
  text: JSON.stringify({
52
52
  success: true,
53
- ticket: formatTicket(moved),
54
- message: `Started ${moved.id}: ${moved.title}`,
53
+ ticket: formatTicket(updated),
54
+ message: `Assigned ${updated.id}: ${updated.title}${params.assignee ? ` to ${params.assignee}` : ''}`,
55
55
  }, null, 2),
56
56
  }],
57
57
  };
@@ -129,4 +129,146 @@ export function registerWorkTools(server, ctx) {
129
129
  return errorResponse(error);
130
130
  }
131
131
  });
132
+ strictTool(server, 'work_start', 'Start work on a ticket — spawns an agent with a running container/session, creates a git branch, and moves ticket to In Progress only after successful spawn', {
133
+ ticket_id: z.string().describe('Ticket ID to start work on'),
134
+ agent: z.string().optional().describe('Agent name to assign (defaults to creating an ephemeral agent)'),
135
+ environment: z.enum(['devcontainer', 'host']).optional().describe('Execution environment (default: devcontainer if available)'),
136
+ display_mode: z.enum(['background', 'terminal']).optional().describe('Display mode (default: background — MCP runs headless)'),
137
+ skip_permissions: z.boolean().optional().describe('Skip permission prompts (danger mode, default: false)'),
138
+ create_pr: z.boolean().optional().describe('Create PR when work is ready (default: false)'),
139
+ }, async (params) => {
140
+ try {
141
+ // Require workspace context for spawn operations
142
+ if (!ctx.getWorkspaceContext) {
143
+ throw new Error('Workspace not initialized. work_start requires a workspace with agents. Run "prlt init" first.');
144
+ }
145
+ const { workspaceInfo, executionStorage, db, pmoPath } = ctx.getWorkspaceContext();
146
+ // Validate ticket exists
147
+ const ticket = await ctx.storage.getTicket(params.ticket_id);
148
+ if (!ticket)
149
+ throw new Error(`Ticket not found: ${params.ticket_id}`);
150
+ // Check ticket is not blocked
151
+ if (ticket.blockedBy && ticket.blockedBy.length > 0) {
152
+ throw new Error(`Ticket ${ticket.id} is blocked by: ${ticket.blockedBy.join(', ')}. Resolve blockers first.`);
153
+ }
154
+ // Check for existing running execution
155
+ const runningExec = executionStorage.getRunningExecution(ticket.id);
156
+ if (runningExec) {
157
+ throw new Error(`Ticket ${ticket.id} already has a running execution: ${runningExec.id} (agent: ${runningExec.agentName})`);
158
+ }
159
+ // Select or create agent
160
+ let agentName;
161
+ if (params.agent) {
162
+ // Use specified agent — verify it exists
163
+ const agentExists = workspaceInfo.agents.some(a => a.name === params.agent);
164
+ if (!agentExists) {
165
+ throw new Error(`Agent "${params.agent}" not found. Available agents: ${workspaceInfo.agents.map(a => a.name).join(', ') || 'none'}`);
166
+ }
167
+ agentName = params.agent;
168
+ }
169
+ else {
170
+ // Create an ephemeral agent (default for MCP)
171
+ const ephemeralResult = await createEphemeralAgent(workspaceInfo);
172
+ agentName = ephemeralResult.name;
173
+ }
174
+ // Spawn the agent with background mode (MCP is headless)
175
+ const displayMode = params.display_mode || 'background';
176
+ const environment = params.environment;
177
+ const result = await spawnAgentForTicket(ticket, agentName, ctx.storage, executionStorage, workspaceInfo, db, pmoPath, {
178
+ environment,
179
+ displayMode,
180
+ skipPermissions: params.skip_permissions ?? false,
181
+ createPR: params.create_pr ?? false,
182
+ skipRemoteCheck: false,
183
+ });
184
+ if (result.success) {
185
+ // Fetch updated ticket to return current state
186
+ const updatedTicket = await ctx.storage.getTicket(params.ticket_id);
187
+ return {
188
+ content: [{
189
+ type: 'text',
190
+ text: JSON.stringify({
191
+ success: true,
192
+ executionId: result.executionId,
193
+ ticketId: result.ticketId,
194
+ agent: result.agentName,
195
+ status: 'running',
196
+ ticket: updatedTicket ? formatTicket(updatedTicket) : undefined,
197
+ message: `Started work on ${ticket.id}: ${ticket.title} (agent: ${agentName})`,
198
+ }, null, 2),
199
+ }],
200
+ };
201
+ }
202
+ else {
203
+ return {
204
+ content: [{
205
+ type: 'text',
206
+ text: JSON.stringify({
207
+ success: false,
208
+ ticketId: result.ticketId,
209
+ agent: result.agentName,
210
+ error: result.error || 'Spawn failed',
211
+ message: `Failed to start work on ${ticket.id}: ${result.error}`,
212
+ }, null, 2),
213
+ }],
214
+ isError: true,
215
+ };
216
+ }
217
+ }
218
+ catch (error) {
219
+ return errorResponse(error);
220
+ }
221
+ });
222
+ strictTool(server, 'work_spawn', 'Spawn work on a ticket using the full CLI pipeline (agent selection, Docker build, container creation, branch setup, tmux session). Shells out to "prlt work spawn" — works whenever prlt is installed, no workspace context needed in-process.', {
223
+ ticket_id: z.string().describe('Ticket ID to spawn work for'),
224
+ action: z.string().optional().describe('Action to perform (e.g., implement, groom, review, custom). Defaults to implement.'),
225
+ display_mode: z.enum(['terminal', 'background']).optional().describe('Display mode (default: background)'),
226
+ skip_permissions: z.boolean().optional().describe('Skip permission prompts — danger mode (default: false)'),
227
+ create_pr: z.boolean().optional().describe('Create PR when work is ready (default: false)'),
228
+ agent: z.string().optional().describe('Agent name to use (default: ephemeral agent created on-demand)'),
229
+ environment: z.enum(['devcontainer', 'host']).optional().describe('Execution environment (default: devcontainer if available)'),
230
+ }, async (params) => {
231
+ try {
232
+ // Build the prlt work spawn command
233
+ const args = [params.ticket_id];
234
+ // Add flags
235
+ if (params.action) {
236
+ args.push('--action', params.action);
237
+ }
238
+ if (params.display_mode) {
239
+ args.push('--display', params.display_mode);
240
+ }
241
+ else {
242
+ args.push('--display', 'background');
243
+ }
244
+ if (params.skip_permissions) {
245
+ args.push('--skip-permissions');
246
+ }
247
+ if (params.create_pr) {
248
+ args.push('--create-pr');
249
+ }
250
+ if (params.environment === 'host') {
251
+ args.push('--run-on-host');
252
+ }
253
+ // Always skip confirmation in MCP context
254
+ args.push('--yes');
255
+ const cmd = `prlt work spawn ${args.join(' ')}`;
256
+ const output = ctx.runCommand(cmd);
257
+ return {
258
+ content: [{
259
+ type: 'text',
260
+ text: JSON.stringify({
261
+ success: true,
262
+ ticketId: params.ticket_id,
263
+ command: cmd,
264
+ output,
265
+ message: `Spawned work for ${params.ticket_id} via CLI pipeline`,
266
+ }, null, 2),
267
+ }],
268
+ };
269
+ }
270
+ catch (error) {
271
+ return errorResponse(error);
272
+ }
273
+ });
132
274
  }
@@ -3,9 +3,19 @@
3
3
  */
4
4
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
5
  import type { SQLiteStorage } from '../pmo/storage/index.js';
6
+ import type Database from 'better-sqlite3';
7
+ import type { WorkspaceInfo } from '../agents/commands.js';
8
+ import type { ExecutionStorage } from '../execution/storage.js';
6
9
  export interface McpToolContext {
7
10
  storage: SQLiteStorage;
8
11
  runCommand: (cmd: string) => string;
12
+ /** Workspace info — available when running in a workspace with agents */
13
+ getWorkspaceContext?: () => {
14
+ workspaceInfo: WorkspaceInfo;
15
+ executionStorage: ExecutionStorage;
16
+ db: Database.Database;
17
+ pmoPath: string;
18
+ };
9
19
  }
10
20
  export type McpToolResult = {
11
21
  content: Array<{
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import * as readline from 'node:readline';
17
17
  import chalk from 'chalk';
18
+ import { isNonTTY } from './prompt-json.js';
18
19
  // ANSI escape codes for terminal control
19
20
  const ESC = '\u001B';
20
21
  const CSI = `${ESC}[`;
@@ -83,7 +84,7 @@ function showCursor() {
83
84
  export async function multiLineInput(options) {
84
85
  const { message, default: defaultValue = '', hint = 'Ctrl+D to finish, Ctrl+C to cancel', required = false, validate, } = options;
85
86
  // If not a TTY, return the default value
86
- if (!process.stdin.isTTY) {
87
+ if (isNonTTY()) {
87
88
  return { value: defaultValue, cancelled: false };
88
89
  }
89
90
  return new Promise((resolve) => {
@@ -3,7 +3,7 @@ import inquirer from 'inquirer';
3
3
  import { getPMOContext } from './pmo-context.js';
4
4
  import { styles } from '../styles.js';
5
5
  import { PromptCommand } from '../prompt-command.js';
6
- import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, } from '../prompt-json.js';
6
+ import { shouldOutputJson, isNonTTY, outputPromptAsJson, outputErrorAsJson, createMetadata, } from '../prompt-json.js';
7
7
  /**
8
8
  * Base flags for JSON/agent mode support
9
9
  * Include these in your command's flags by spreading: ...jsonModeFlags
@@ -168,7 +168,7 @@ export class PMOCommand extends PromptCommand {
168
168
  return numA - numB;
169
169
  });
170
170
  // Auto-detect non-TTY: switch to JSON mode when no TTY present
171
- const effectiveJsonMode = options?.jsonMode ?? (!process.stdin.isTTY
171
+ const effectiveJsonMode = options?.jsonMode ?? (isNonTTY()
172
172
  ? {
173
173
  flags: { json: true },
174
174
  commandName: this.id ?? 'unknown',
@@ -234,7 +234,7 @@ export class PMOCommand extends PromptCommand {
234
234
  async selectFromList(options) {
235
235
  const { message, items, getName, getValue, getCommand, jsonMode, allowCancel = false, cancelValue, } = options;
236
236
  // Auto-detect non-TTY: switch to JSON mode when no TTY present
237
- const effectiveJsonMode = jsonMode ?? (!process.stdin.isTTY
237
+ const effectiveJsonMode = jsonMode ?? (isNonTTY()
238
238
  ? { flags: { json: true }, commandName: this.id ?? 'unknown' }
239
239
  : null);
240
240
  // Build choices with command field
@@ -285,7 +285,7 @@ export class PMOCommand extends PromptCommand {
285
285
  async promptForInput(options) {
286
286
  const { message, fieldName, defaultValue, validate, jsonMode } = options;
287
287
  // Auto-detect non-TTY: switch to JSON mode when no TTY present
288
- const effectiveJsonMode = jsonMode ?? (!process.stdin.isTTY
288
+ const effectiveJsonMode = jsonMode ?? (isNonTTY()
289
289
  ? { flags: { json: true }, commandName: this.id ?? 'unknown', commandHint: '', example: undefined }
290
290
  : null);
291
291
  // Check for JSON mode
@@ -66,7 +66,7 @@ export declare const PMO_TABLE_SCHEMAS: {
66
66
  readonly project_specs: "\n CREATE TABLE IF NOT EXISTS pmo_project_specs (\n project_id TEXT NOT NULL REFERENCES pmo_projects(id) ON DELETE CASCADE,\n spec_id TEXT NOT NULL REFERENCES pmo_specs(id) ON DELETE CASCADE,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (project_id, spec_id)\n )";
67
67
  readonly cache_metadata: "\n CREATE TABLE IF NOT EXISTS pmo_cache_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )";
68
68
  readonly settings: "\n CREATE TABLE IF NOT EXISTS pmo_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )";
69
- readonly agent_work: "\n CREATE TABLE IF NOT EXISTS agent_work (\n id TEXT PRIMARY KEY,\n ticket_id TEXT NOT NULL,\n agent_name TEXT NOT NULL,\n executor TEXT NOT NULL,\n environment TEXT NOT NULL DEFAULT 'host',\n display_mode TEXT NOT NULL DEFAULT 'terminal',\n sandboxed INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'starting',\n branch TEXT,\n pid TEXT,\n container_id TEXT,\n session_id TEXT,\n host TEXT,\n log_path TEXT,\n started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n completed_at TIMESTAMP,\n exit_code INTEGER,\n FOREIGN KEY (ticket_id) REFERENCES pmo_tickets(id) ON DELETE CASCADE\n )";
69
+ readonly agent_work: "\n CREATE TABLE IF NOT EXISTS agent_work (\n id TEXT PRIMARY KEY,\n ticket_id TEXT NOT NULL,\n agent_name TEXT NOT NULL,\n executor TEXT NOT NULL,\n environment TEXT NOT NULL DEFAULT 'host',\n display_mode TEXT NOT NULL DEFAULT 'terminal',\n sandboxed INTEGER NOT NULL DEFAULT 0,\n status TEXT NOT NULL DEFAULT 'starting',\n branch TEXT,\n pid TEXT,\n container_id TEXT,\n session_id TEXT,\n host TEXT,\n log_path TEXT,\n started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n completed_at TIMESTAMP,\n exit_code INTEGER,\n error_message TEXT,\n FOREIGN KEY (ticket_id) REFERENCES pmo_tickets(id) ON DELETE CASCADE\n )";
70
70
  readonly containers: "\n CREATE TABLE IF NOT EXISTS containers (\n id TEXT PRIMARY KEY,\n agent_name TEXT NOT NULL,\n docker_id TEXT NOT NULL,\n docker_name TEXT,\n image TEXT,\n status TEXT NOT NULL DEFAULT 'unknown',\n current_execution_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n last_seen_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (agent_name) REFERENCES agents(name) ON DELETE CASCADE,\n FOREIGN KEY (current_execution_id) REFERENCES agent_work(id) ON DELETE SET NULL\n )";
71
71
  readonly id_sequences: "\n CREATE TABLE IF NOT EXISTS id_sequences (\n table_name TEXT PRIMARY KEY,\n next_id INTEGER NOT NULL DEFAULT 1\n )";
72
72
  readonly statuses: "\n CREATE TABLE IF NOT EXISTS pmo_statuses (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL,\n name TEXT NOT NULL,\n category TEXT NOT NULL,\n position INTEGER NOT NULL DEFAULT 0,\n color TEXT,\n description TEXT,\n is_default INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (project_id) REFERENCES pmo_projects(id) ON DELETE CASCADE,\n UNIQUE(project_id, name)\n )";
@@ -337,6 +337,7 @@ export const PMO_TABLE_SCHEMAS = {
337
337
  started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
338
338
  completed_at TIMESTAMP,
339
339
  exit_code INTEGER,
340
+ error_message TEXT,
340
341
  FOREIGN KEY (ticket_id) REFERENCES ${PMO_TABLES.tickets}(id) ON DELETE CASCADE
341
342
  )`,
342
343
  // Docker containers (per-agent, reused across executions)
@@ -169,7 +169,7 @@ export class ActionStorage {
169
169
  ? JSON.parse(row.default_category)
170
170
  : undefined,
171
171
  defaultMoveToCategory: row.default_category,
172
- modifiesCode: row.is_builtin === 1,
172
+ modifiesCode: row.modifies_code === 1,
173
173
  isBuiltin: row.is_builtin === 1,
174
174
  createdAt: new Date(row.created_at),
175
175
  };