@proletariat/cli 0.3.9 → 0.3.11
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 +25 -0
- package/bin/dev.js +0 -0
- package/dist/commands/action/index.js +1 -1
- package/dist/commands/action/run.js +8 -12
- package/dist/commands/agent/auth.d.ts +30 -0
- package/dist/commands/agent/auth.js +172 -0
- package/dist/commands/agent/discover.d.ts +9 -0
- package/dist/commands/agent/discover.js +67 -0
- package/dist/commands/agent/index.js +47 -12
- package/dist/commands/agent/list.d.ts +4 -1
- package/dist/commands/agent/list.js +78 -16
- package/dist/commands/agent/login.js +35 -31
- package/dist/commands/agent/restart.js +2 -0
- package/dist/commands/agent/shell.js +78 -19
- package/dist/commands/agent/staff/add.js +1 -12
- package/dist/commands/agent/staff/remove.js +9 -7
- package/dist/commands/agent/status.js +17 -4
- package/dist/commands/agent/temp/cleanup.js +7 -3
- package/dist/commands/agent/themes/index.js +4 -5
- package/dist/commands/agent/themes/list.js +5 -5
- package/dist/commands/agent/visit.js +17 -4
- package/dist/commands/branch/create.d.ts +4 -0
- package/dist/commands/branch/create.js +16 -8
- package/dist/commands/branch/index.js +1 -1
- package/dist/commands/branch/where.js +1 -0
- package/dist/commands/claude.d.ts +38 -0
- package/dist/commands/claude.js +899 -0
- package/dist/commands/commit.js +1 -1
- package/dist/commands/config/index.d.ts +12 -0
- package/dist/commands/config/index.js +271 -0
- package/dist/commands/docker/clean.js +2 -2
- package/dist/commands/docker/index.js +2 -2
- package/dist/commands/docker/list.js +3 -8
- package/dist/commands/docker/logs.js +2 -2
- package/dist/commands/docker/prune.js +1 -1
- package/dist/commands/docker/restart.js +2 -2
- package/dist/commands/docker/shell.js +2 -2
- package/dist/commands/docker/start.js +2 -2
- package/dist/commands/docker/status.js +1 -1
- package/dist/commands/docker/stop.js +2 -2
- package/dist/commands/docker/sync.js +2 -2
- package/dist/commands/epic/index.js +1 -1
- package/dist/commands/epic/link/index.js +25 -14
- package/dist/commands/epic/link/remove.js +2 -0
- package/dist/commands/epic/list.js +5 -5
- package/dist/commands/epic/progress.js +10 -4
- package/dist/commands/epic/spec.js +2 -0
- package/dist/commands/epic/ticket.js +3 -0
- package/dist/commands/execution/stop.js +1 -0
- package/dist/commands/init.js +4 -4
- package/dist/commands/project/index.js +1 -1
- package/dist/commands/project/spec.js +7 -0
- package/dist/commands/repo/add.js +1 -0
- package/dist/commands/repo/remove.js +1 -0
- package/dist/commands/roadmap/add-project.d.ts +18 -0
- package/dist/commands/roadmap/add-project.js +135 -0
- package/dist/commands/roadmap/create.d.ts +22 -0
- package/dist/commands/roadmap/create.js +156 -0
- package/dist/commands/roadmap/delete.d.ts +17 -0
- package/dist/commands/roadmap/delete.js +104 -0
- package/dist/commands/roadmap/generate.d.ts +22 -0
- package/dist/commands/roadmap/generate.js +201 -0
- package/dist/commands/roadmap/index.d.ts +13 -0
- package/dist/commands/roadmap/index.js +61 -0
- package/dist/commands/roadmap/list.d.ts +12 -0
- package/dist/commands/roadmap/list.js +42 -0
- package/dist/commands/roadmap/remove-project.d.ts +18 -0
- package/dist/commands/roadmap/remove-project.js +147 -0
- package/dist/commands/roadmap/reorder.d.ts +17 -0
- package/dist/commands/roadmap/reorder.js +157 -0
- package/dist/commands/roadmap/update.d.ts +19 -0
- package/dist/commands/roadmap/update.js +136 -0
- package/dist/commands/roadmap/view.d.ts +16 -0
- package/dist/commands/roadmap/view.js +103 -0
- package/dist/commands/spec/index.js +1 -1
- package/dist/commands/spec/link/index.js +24 -13
- package/dist/commands/spec/link/remove.js +2 -0
- package/dist/commands/status/index.js +1 -1
- package/dist/commands/status/list.js +0 -8
- package/dist/commands/template/delete.js +2 -0
- package/dist/commands/terminal/title.d.ts +12 -0
- package/dist/commands/terminal/title.js +48 -0
- package/dist/commands/ticket/complete.js +2 -0
- package/dist/commands/ticket/create.js +4 -2
- package/dist/commands/ticket/delete.js +2 -0
- package/dist/commands/ticket/edit.js +8 -2
- package/dist/commands/ticket/link/index.js +17 -3
- package/dist/commands/ticket/link/remove.js +2 -0
- package/dist/commands/ticket/list.js +1 -2
- package/dist/commands/ticket/move.js +2 -0
- package/dist/commands/ticket/project.js +3 -1
- package/dist/commands/ticket/reassign.js +2 -0
- package/dist/commands/ticket/spec.js +4 -2
- package/dist/commands/ticket/template/apply.js +4 -3
- package/dist/commands/ticket/template/create.js +2 -0
- package/dist/commands/ticket/template/index.js +1 -1
- package/dist/commands/ticket/update.js +2 -0
- package/dist/commands/work/index.js +1 -1
- package/dist/commands/work/revise.js +7 -1
- package/dist/commands/work/spawn.d.ts +2 -1
- package/dist/commands/work/spawn.js +131 -36
- package/dist/commands/work/start.d.ts +2 -1
- package/dist/commands/work/start.js +349 -69
- package/dist/commands/work/watch.js +10 -2
- package/dist/commands/workflow/create.js +3 -3
- package/dist/commands/workflow/switch.js +2 -1
- package/dist/commands/workspace/remove.js +0 -8
- package/dist/commands/workspace/use.js +1 -9
- package/dist/lib/agents/commands.js +18 -13
- package/dist/lib/database/index.d.ts +19 -12
- package/dist/lib/database/index.js +158 -42
- package/dist/lib/docker/resolve.js +1 -1
- package/dist/lib/execution/config.d.ts +6 -0
- package/dist/lib/execution/config.js +15 -2
- package/dist/lib/execution/devcontainer.d.ts +2 -0
- package/dist/lib/execution/devcontainer.js +41 -9
- package/dist/lib/execution/runners.d.ts +85 -3
- package/dist/lib/execution/runners.js +925 -228
- package/dist/lib/execution/spawner.d.ts +2 -2
- package/dist/lib/execution/spawner.js +4 -3
- package/dist/lib/execution/storage.d.ts +2 -1
- package/dist/lib/execution/storage.js +9 -13
- package/dist/lib/execution/types.d.ts +10 -1
- package/dist/lib/execution/types.js +3 -1
- package/dist/lib/init/index.js +1 -0
- package/dist/lib/machine-config.js +1 -1
- package/dist/lib/pmo/base-command.js +5 -9
- package/dist/lib/pmo/index.js +2 -0
- package/dist/lib/pmo/schema.d.ts +6 -0
- package/dist/lib/pmo/schema.js +36 -0
- package/dist/lib/pmo/storage/base.js +3 -3
- package/dist/lib/pmo/storage/index.d.ts +16 -1
- package/dist/lib/pmo/storage/index.js +45 -0
- package/dist/lib/pmo/storage/roadmaps.d.ts +62 -0
- package/dist/lib/pmo/storage/roadmaps.js +301 -0
- package/dist/lib/pmo/storage/specs.js +2 -0
- package/dist/lib/pmo/storage/types.d.ts +14 -0
- package/dist/lib/pmo/sync-manager.d.ts +1 -1
- package/dist/lib/pmo/sync-manager.js +1 -1
- package/dist/lib/pmo/types.d.ts +41 -0
- package/dist/lib/pmo/utils.d.ts +2 -0
- package/dist/lib/pmo/utils.js +22 -1
- package/dist/lib/repos/index.js +7 -1
- package/dist/lib/terminal.d.ts +31 -0
- package/dist/lib/terminal.js +48 -0
- package/dist/lib/themes.d.ts +21 -3
- package/dist/lib/themes.js +80 -23
- package/dist/lib/workspace-config.d.ts +80 -0
- package/dist/lib/workspace-config.js +100 -0
- package/oclif.manifest.json +4065 -3225
- package/package.json +10 -6
- package/LICENSE +0 -21
|
@@ -8,7 +8,7 @@ import Database from 'better-sqlite3';
|
|
|
8
8
|
import { SQLiteStorage } from '../pmo/storage-sqlite.js';
|
|
9
9
|
import { WorkspaceInfo } from '../agents/commands.js';
|
|
10
10
|
import { ExecutionStorage } from './storage.js';
|
|
11
|
-
import { isDockerRunning } from './runners.js';
|
|
11
|
+
import { isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled } from './runners.js';
|
|
12
12
|
import { DisplayMode, SessionManager, ExecutorType, ExecutionEnvironment, ExecutionConfig } from './types.js';
|
|
13
13
|
import { Ticket } from '../pmo/types.js';
|
|
14
14
|
export type AgentStrategy = 'round-robin' | 'least-busy' | 'random';
|
|
@@ -84,4 +84,4 @@ export declare function spawnForColumn(projectId: string, columnName: string, st
|
|
|
84
84
|
ticketIds?: string[];
|
|
85
85
|
log?: (msg: string) => void;
|
|
86
86
|
} & SpawnOptions): Promise<BatchSpawnResult>;
|
|
87
|
-
export { isDockerRunning };
|
|
87
|
+
export { isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled };
|
|
@@ -12,7 +12,7 @@ import { getWorkColumnSetting, findColumnByName } from '../pmo/utils.js';
|
|
|
12
12
|
import { findHQRoot } from '../repos/index.js';
|
|
13
13
|
import { hasDevcontainerConfig } from './devcontainer.js';
|
|
14
14
|
import { loadExecutionConfig, getOrPromptCoderName } from './config.js';
|
|
15
|
-
import { runExecution, isDockerRunning } from './runners.js';
|
|
15
|
+
import { runExecution, isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled } from './runners.js';
|
|
16
16
|
import { generateBranchName, DEFAULT_EXECUTION_CONFIG, } from './types.js';
|
|
17
17
|
// =============================================================================
|
|
18
18
|
// Git Utilities
|
|
@@ -514,6 +514,7 @@ export async function spawnForColumn(projectId, columnName, storage, executionSt
|
|
|
514
514
|
continue;
|
|
515
515
|
}
|
|
516
516
|
log(`Spawning ${agentName} for ${ticket.id}: ${ticket.title}`);
|
|
517
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential spawning with user feedback
|
|
517
518
|
const spawnResult = await spawnAgentForTicket(ticket, agentName, storage, executionStorage, workspaceInfo, db, pmoPath, options);
|
|
518
519
|
if (spawnResult.success) {
|
|
519
520
|
result.spawned.push(spawnResult);
|
|
@@ -550,6 +551,6 @@ export async function spawnForColumn(projectId, columnName, storage, executionSt
|
|
|
550
551
|
return result;
|
|
551
552
|
}
|
|
552
553
|
// =============================================================================
|
|
553
|
-
// Docker
|
|
554
|
+
// Docker & GitHub Token Checks
|
|
554
555
|
// =============================================================================
|
|
555
|
-
export { isDockerRunning };
|
|
556
|
+
export { isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled };
|
|
@@ -9,7 +9,8 @@ export declare class ExecutionStorage {
|
|
|
9
9
|
private db;
|
|
10
10
|
constructor(db: Database.Database);
|
|
11
11
|
/**
|
|
12
|
-
* Create a new execution record
|
|
12
|
+
* Create a new execution record.
|
|
13
|
+
* Uses UUID-based IDs to guarantee uniqueness without race conditions.
|
|
13
14
|
*/
|
|
14
15
|
createExecution(params: {
|
|
15
16
|
ticketId: string;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Database operations for agent_work table.
|
|
5
5
|
*/
|
|
6
6
|
import { execSync } from 'node:child_process';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
7
8
|
import { PMO_TABLES } from '../pmo/schema.js';
|
|
8
9
|
const T = PMO_TABLES;
|
|
9
10
|
// =============================================================================
|
|
@@ -31,16 +32,6 @@ function rowToAgentWork(row) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
// =============================================================================
|
|
34
|
-
// ID Generation
|
|
35
|
-
// =============================================================================
|
|
36
|
-
function generateWorkId(db) {
|
|
37
|
-
const result = db
|
|
38
|
-
.prepare(`SELECT COUNT(*) as count FROM ${T.agent_work}`)
|
|
39
|
-
.get();
|
|
40
|
-
const num = (result?.count || 0) + 1;
|
|
41
|
-
return `WORK-${String(num).padStart(3, '0')}`;
|
|
42
|
-
}
|
|
43
|
-
// =============================================================================
|
|
44
35
|
// Execution Storage Class
|
|
45
36
|
// =============================================================================
|
|
46
37
|
export class ExecutionStorage {
|
|
@@ -49,11 +40,14 @@ export class ExecutionStorage {
|
|
|
49
40
|
this.db = db;
|
|
50
41
|
}
|
|
51
42
|
/**
|
|
52
|
-
* Create a new execution record
|
|
43
|
+
* Create a new execution record.
|
|
44
|
+
* Uses UUID-based IDs to guarantee uniqueness without race conditions.
|
|
53
45
|
*/
|
|
54
46
|
createExecution(params) {
|
|
55
|
-
const id = generateWorkId(this.db);
|
|
56
47
|
const now = Date.now();
|
|
48
|
+
// Generate a unique ID using UUID (first 8 chars, uppercase)
|
|
49
|
+
// Format: WORK-A1B2C3D4 - guaranteed unique, no race conditions
|
|
50
|
+
const id = `WORK-${randomUUID().substring(0, 8).toUpperCase()}`;
|
|
57
51
|
this.db.prepare(`
|
|
58
52
|
INSERT INTO ${T.agent_work} (
|
|
59
53
|
id, ticket_id, agent_name, executor, environment, display_mode, sandboxed,
|
|
@@ -468,7 +462,9 @@ export class ContainerStorage {
|
|
|
468
462
|
*/
|
|
469
463
|
syncFromDocker(dockerContainers) {
|
|
470
464
|
const now = Date.now();
|
|
471
|
-
let added = 0
|
|
465
|
+
let added = 0;
|
|
466
|
+
let updated = 0;
|
|
467
|
+
let removed = 0;
|
|
472
468
|
// Create a set of docker IDs currently running
|
|
473
469
|
const activeDockerIds = new Set(dockerContainers.map(c => c.id.substring(0, 12)));
|
|
474
470
|
// Wrap all operations in a transaction for atomicity
|
|
@@ -19,14 +19,21 @@ export type SessionManager = 'tmux' | 'direct';
|
|
|
19
19
|
* DisplayMode - How output is presented to the user.
|
|
20
20
|
* - terminal: Opens a new terminal tab attached to the tmux session
|
|
21
21
|
* - background: Runs detached, reattach later with `prlt session attach`
|
|
22
|
+
* - foreground: Attaches tmux in current terminal (blocking)
|
|
22
23
|
*/
|
|
23
|
-
export type DisplayMode = 'terminal' | 'background';
|
|
24
|
+
export type DisplayMode = 'terminal' | 'background' | 'foreground';
|
|
24
25
|
/**
|
|
25
26
|
* OutputMode - How Claude Code displays its output.
|
|
26
27
|
* - interactive: Shows streaming UI with real-time tool calls, file reads, etc.
|
|
27
28
|
* - print: Outputs final result only (uses -p flag), better for logs/automation
|
|
28
29
|
*/
|
|
29
30
|
export type OutputMode = 'interactive' | 'print';
|
|
31
|
+
/**
|
|
32
|
+
* PermissionMode - How Claude Code handles permission checks.
|
|
33
|
+
* - danger: Skip permission checks (faster, relies on container/environment isolation)
|
|
34
|
+
* - safe: Requires approval for dangerous operations
|
|
35
|
+
*/
|
|
36
|
+
export type PermissionMode = 'danger' | 'safe';
|
|
30
37
|
export type ExecutorType = 'claude-code' | 'codex' | 'aider' | 'custom';
|
|
31
38
|
export type TerminalApp = 'Terminal' | 'iTerm' | 'Alacritty' | 'Ghostty' | 'Kitty' | 'tmux' | 'Warp' | 'WezTerm';
|
|
32
39
|
export type Shell = 'bash' | 'zsh' | 'fish';
|
|
@@ -113,9 +120,11 @@ export interface ExecutionConfig {
|
|
|
113
120
|
session: string;
|
|
114
121
|
layout: 'split' | 'window';
|
|
115
122
|
controlMode: boolean;
|
|
123
|
+
windowMode: 'tab' | 'window';
|
|
116
124
|
};
|
|
117
125
|
terminal: {
|
|
118
126
|
app: TerminalApp;
|
|
127
|
+
openInBackground: boolean;
|
|
119
128
|
};
|
|
120
129
|
devcontainer: {
|
|
121
130
|
defaultImage: string;
|
|
@@ -135,10 +135,12 @@ export const DEFAULT_EXECUTION_CONFIG = {
|
|
|
135
135
|
tmux: {
|
|
136
136
|
session: 'proletariat',
|
|
137
137
|
layout: 'window',
|
|
138
|
-
controlMode: true, //
|
|
138
|
+
controlMode: true, // Use -u -CC for native iTerm scrolling/selection
|
|
139
|
+
windowMode: 'tab', // Open tmux windows as tabs in current window by default
|
|
139
140
|
},
|
|
140
141
|
terminal: {
|
|
141
142
|
app: 'Terminal',
|
|
143
|
+
openInBackground: true, // Don't steal focus when opening new tabs
|
|
142
144
|
},
|
|
143
145
|
devcontainer: {
|
|
144
146
|
defaultImage: 'mcr.microsoft.com/devcontainers/base:ubuntu',
|
package/dist/lib/init/index.js
CHANGED
|
@@ -121,6 +121,7 @@ export async function promptForHQLocation(hqName) {
|
|
|
121
121
|
defaultPath = `./${folderName}`;
|
|
122
122
|
}
|
|
123
123
|
while (true) {
|
|
124
|
+
// eslint-disable-next-line no-await-in-loop -- Interactive validation loop
|
|
124
125
|
const { location } = await inquirer.prompt([{
|
|
125
126
|
type: 'input',
|
|
126
127
|
name: 'location',
|
|
@@ -151,7 +151,7 @@ export function registerHeadquarters(hqPath, name, setActive = true, orgName) {
|
|
|
151
151
|
registeredAt: new Date().toISOString(),
|
|
152
152
|
orgName,
|
|
153
153
|
};
|
|
154
|
-
if (existingIndex
|
|
154
|
+
if (existingIndex !== -1) {
|
|
155
155
|
// Update existing entry
|
|
156
156
|
config.headquarters[existingIndex] = entry;
|
|
157
157
|
}
|
|
@@ -78,15 +78,10 @@ export class PMOCommand extends Command {
|
|
|
78
78
|
// Parse flags to get project ID if provided
|
|
79
79
|
const { flags } = await this.parse(this.constructor);
|
|
80
80
|
this.projectFlag = flags.project;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.contextInitialized = true;
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
81
|
+
this.pmoContext = await getPMOContext({
|
|
82
|
+
logger: (msg) => this.pmoLogger(msg),
|
|
83
|
+
});
|
|
84
|
+
this.contextInitialized = true;
|
|
90
85
|
}
|
|
91
86
|
/**
|
|
92
87
|
* Require a project to be selected.
|
|
@@ -116,6 +111,7 @@ export class PMOCommand extends Command {
|
|
|
116
111
|
if (options?.filterEmptyProjects) {
|
|
117
112
|
const projectsWithTickets = [];
|
|
118
113
|
for (const p of projects) {
|
|
114
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential filtering for project selection
|
|
119
115
|
const tickets = await this.storage.listTickets(p.id);
|
|
120
116
|
if (tickets.length > 0) {
|
|
121
117
|
projectsWithTickets.push(p);
|
package/dist/lib/pmo/index.js
CHANGED
|
@@ -284,6 +284,7 @@ export async function createPMO(options) {
|
|
|
284
284
|
category = 'canceled';
|
|
285
285
|
}
|
|
286
286
|
// Default: backlog (for first columns, custom backlogs, etc.)
|
|
287
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential status creation in order
|
|
287
288
|
await storage.createStatus(projectId, {
|
|
288
289
|
name,
|
|
289
290
|
category,
|
|
@@ -410,6 +411,7 @@ export function hasPMO(hqPath) {
|
|
|
410
411
|
return false;
|
|
411
412
|
}
|
|
412
413
|
try {
|
|
414
|
+
// eslint-disable-next-line unicorn/prefer-module
|
|
413
415
|
const Database = require('better-sqlite3');
|
|
414
416
|
const db = new Database(dbPath);
|
|
415
417
|
const result = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='pmo_projects'").get();
|
package/dist/lib/pmo/schema.d.ts
CHANGED
|
@@ -25,12 +25,15 @@ export declare const PMO_TABLES: {
|
|
|
25
25
|
readonly settings: "pmo_settings";
|
|
26
26
|
readonly agent_work: "agent_work";
|
|
27
27
|
readonly containers: "containers";
|
|
28
|
+
readonly id_sequences: "id_sequences";
|
|
28
29
|
readonly workflows: "pmo_workflows";
|
|
29
30
|
readonly workflow_statuses: "pmo_workflow_statuses";
|
|
30
31
|
readonly phases: "pmo_phases";
|
|
31
32
|
readonly phase_templates: "pmo_phase_templates";
|
|
32
33
|
readonly actions: "pmo_actions";
|
|
33
34
|
readonly ticket_templates: "pmo_ticket_templates";
|
|
35
|
+
readonly roadmaps: "pmo_roadmaps";
|
|
36
|
+
readonly roadmap_projects: "pmo_roadmap_projects";
|
|
34
37
|
readonly columns: "pmo_columns";
|
|
35
38
|
readonly board_tickets: "pmo_board_tickets";
|
|
36
39
|
readonly statuses: "pmo_statuses";
|
|
@@ -60,11 +63,14 @@ export declare const PMO_TABLE_SCHEMAS: {
|
|
|
60
63
|
readonly settings: "\n CREATE TABLE IF NOT EXISTS pmo_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )";
|
|
61
64
|
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 )";
|
|
62
65
|
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 )";
|
|
66
|
+
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 )";
|
|
63
67
|
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 )";
|
|
64
68
|
readonly phases: "\n CREATE TABLE IF NOT EXISTS pmo_phases (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\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 )";
|
|
65
69
|
readonly phase_templates: "\n CREATE TABLE IF NOT EXISTS pmo_phase_templates (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n is_builtin INTEGER NOT NULL DEFAULT 0,\n phases TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
|
|
66
70
|
readonly actions: "\n CREATE TABLE IF NOT EXISTS pmo_actions (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n prompt TEXT NOT NULL,\n end_prompt TEXT,\n suggested_for_categories TEXT,\n default_move_to_category TEXT,\n modifies_code INTEGER NOT NULL DEFAULT 1,\n is_builtin INTEGER NOT NULL DEFAULT 0,\n position INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
|
|
67
71
|
readonly ticket_templates: "\n CREATE TABLE IF NOT EXISTS pmo_ticket_templates (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n is_builtin INTEGER NOT NULL DEFAULT 0,\n title_pattern TEXT,\n description_template TEXT,\n default_priority TEXT,\n default_category TEXT,\n default_status_id TEXT,\n default_assignee TEXT,\n default_owner TEXT,\n default_labels TEXT NOT NULL DEFAULT '[]',\n suggested_subtasks TEXT NOT NULL DEFAULT '[]',\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
|
|
72
|
+
readonly roadmaps: "\n CREATE TABLE IF NOT EXISTS pmo_roadmaps (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n is_default INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
|
|
73
|
+
readonly roadmap_projects: "\n CREATE TABLE IF NOT EXISTS pmo_roadmap_projects (\n roadmap_id TEXT NOT NULL REFERENCES pmo_roadmaps(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL REFERENCES pmo_projects(id) ON DELETE CASCADE,\n position INTEGER NOT NULL DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (roadmap_id, project_id)\n )";
|
|
68
74
|
};
|
|
69
75
|
export declare const PMO_INDEXES: string;
|
|
70
76
|
/**
|
package/dist/lib/pmo/schema.js
CHANGED
|
@@ -28,6 +28,7 @@ export const PMO_TABLES = {
|
|
|
28
28
|
settings: 'pmo_settings',
|
|
29
29
|
agent_work: 'agent_work',
|
|
30
30
|
containers: 'containers', // Docker containers per agent
|
|
31
|
+
id_sequences: 'id_sequences', // Sequence counters for ID generation
|
|
31
32
|
// Workflow tables (consolidated - workflows are the single source of truth for board columns)
|
|
32
33
|
workflows: 'pmo_workflows', // Shared workflow definitions
|
|
33
34
|
workflow_statuses: 'pmo_workflow_statuses', // Statuses belonging to workflows (= board columns)
|
|
@@ -36,6 +37,9 @@ export const PMO_TABLES = {
|
|
|
36
37
|
phase_templates: 'pmo_phase_templates', // Phase configuration templates
|
|
37
38
|
actions: 'pmo_actions', // Work actions (reusable agent prompts)
|
|
38
39
|
ticket_templates: 'pmo_ticket_templates', // Ticket templates for quick creation
|
|
40
|
+
// Roadmap tables (ordered collections of projects for documentation)
|
|
41
|
+
roadmaps: 'pmo_roadmaps', // Named roadmap definitions
|
|
42
|
+
roadmap_projects: 'pmo_roadmap_projects', // Many-to-many: roadmaps ↔ projects with ordering
|
|
39
43
|
// Legacy tables (deprecated, kept for migration)
|
|
40
44
|
columns: 'pmo_columns', // DEPRECATED: use workflow_statuses
|
|
41
45
|
board_tickets: 'pmo_board_tickets', // DEPRECATED: tickets now use status_id directly
|
|
@@ -331,6 +335,12 @@ export const PMO_TABLE_SCHEMAS = {
|
|
|
331
335
|
FOREIGN KEY (agent_name) REFERENCES agents(name) ON DELETE CASCADE,
|
|
332
336
|
FOREIGN KEY (current_execution_id) REFERENCES ${PMO_TABLES.agent_work}(id) ON DELETE SET NULL
|
|
333
337
|
)`,
|
|
338
|
+
// Sequence counters for ID generation (avoids collisions after deletions)
|
|
339
|
+
id_sequences: `
|
|
340
|
+
CREATE TABLE IF NOT EXISTS ${PMO_TABLES.id_sequences} (
|
|
341
|
+
table_name TEXT PRIMARY KEY,
|
|
342
|
+
next_id INTEGER NOT NULL DEFAULT 1
|
|
343
|
+
)`,
|
|
334
344
|
// DEPRECATED: Legacy per-project statuses - use workflow_statuses instead
|
|
335
345
|
// Kept for migration from old schema
|
|
336
346
|
statuses: `
|
|
@@ -402,6 +412,25 @@ export const PMO_TABLE_SCHEMAS = {
|
|
|
402
412
|
suggested_subtasks TEXT NOT NULL DEFAULT '[]',
|
|
403
413
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
404
414
|
)`,
|
|
415
|
+
// Roadmap definitions (named collections of projects for documentation)
|
|
416
|
+
roadmaps: `
|
|
417
|
+
CREATE TABLE IF NOT EXISTS ${PMO_TABLES.roadmaps} (
|
|
418
|
+
id TEXT PRIMARY KEY,
|
|
419
|
+
name TEXT NOT NULL UNIQUE,
|
|
420
|
+
description TEXT,
|
|
421
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
422
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
423
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
424
|
+
)`,
|
|
425
|
+
// Roadmap-to-project associations with ordering
|
|
426
|
+
roadmap_projects: `
|
|
427
|
+
CREATE TABLE IF NOT EXISTS ${PMO_TABLES.roadmap_projects} (
|
|
428
|
+
roadmap_id TEXT NOT NULL REFERENCES ${PMO_TABLES.roadmaps}(id) ON DELETE CASCADE,
|
|
429
|
+
project_id TEXT NOT NULL REFERENCES ${PMO_TABLES.projects}(id) ON DELETE CASCADE,
|
|
430
|
+
position INTEGER NOT NULL DEFAULT 0,
|
|
431
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
432
|
+
PRIMARY KEY (roadmap_id, project_id)
|
|
433
|
+
)`,
|
|
405
434
|
};
|
|
406
435
|
// =============================================================================
|
|
407
436
|
// Indexes
|
|
@@ -451,6 +480,10 @@ export const PMO_INDEXES = `
|
|
|
451
480
|
CREATE INDEX IF NOT EXISTS idx_pmo_workflow_statuses_category ON ${PMO_TABLES.workflow_statuses}(workflow_id, category);
|
|
452
481
|
CREATE INDEX IF NOT EXISTS idx_pmo_workflow_statuses_position ON ${PMO_TABLES.workflow_statuses}(workflow_id, position);
|
|
453
482
|
CREATE INDEX IF NOT EXISTS idx_pmo_workflows_builtin ON ${PMO_TABLES.workflows}(is_builtin);
|
|
483
|
+
CREATE INDEX IF NOT EXISTS idx_pmo_roadmaps_default ON ${PMO_TABLES.roadmaps}(is_default);
|
|
484
|
+
CREATE INDEX IF NOT EXISTS idx_pmo_roadmap_projects_roadmap ON ${PMO_TABLES.roadmap_projects}(roadmap_id);
|
|
485
|
+
CREATE INDEX IF NOT EXISTS idx_pmo_roadmap_projects_project ON ${PMO_TABLES.roadmap_projects}(project_id);
|
|
486
|
+
CREATE INDEX IF NOT EXISTS idx_pmo_roadmap_projects_position ON ${PMO_TABLES.roadmap_projects}(roadmap_id, position);
|
|
454
487
|
`;
|
|
455
488
|
// =============================================================================
|
|
456
489
|
// Combined Schema
|
|
@@ -485,8 +518,11 @@ export const PMO_SCHEMA_SQL = [
|
|
|
485
518
|
PMO_TABLE_SCHEMAS.settings,
|
|
486
519
|
PMO_TABLE_SCHEMAS.agent_work, // Execution tracking
|
|
487
520
|
PMO_TABLE_SCHEMAS.containers, // Docker containers per agent
|
|
521
|
+
PMO_TABLE_SCHEMAS.id_sequences, // Sequence counters for ID generation
|
|
488
522
|
PMO_TABLE_SCHEMAS.actions, // Work actions (reusable agent prompts)
|
|
489
523
|
PMO_TABLE_SCHEMAS.ticket_templates, // Ticket templates for quick creation
|
|
524
|
+
PMO_TABLE_SCHEMAS.roadmaps, // Named roadmap definitions
|
|
525
|
+
PMO_TABLE_SCHEMAS.roadmap_projects, // Roadmap-to-project associations
|
|
490
526
|
// Legacy tables (kept for migration, will be dropped after data migrated)
|
|
491
527
|
PMO_TABLE_SCHEMAS.columns, // DEPRECATED
|
|
492
528
|
PMO_TABLE_SCHEMAS.board_tickets, // DEPRECATED
|
|
@@ -120,8 +120,8 @@ export function runMigrations(db) {
|
|
|
120
120
|
// Migration: Add position column to actions table
|
|
121
121
|
if (tableExists(T.actions)) {
|
|
122
122
|
const actionsColumns = db.pragma(`table_info(${T.actions})`);
|
|
123
|
-
const actionsColumnNames = actionsColumns.map(c => c.name);
|
|
124
|
-
if (!actionsColumnNames.
|
|
123
|
+
const actionsColumnNames = new Set(actionsColumns.map(c => c.name));
|
|
124
|
+
if (!actionsColumnNames.has('position')) {
|
|
125
125
|
try {
|
|
126
126
|
db.exec(`ALTER TABLE ${T.actions} ADD COLUMN position INTEGER NOT NULL DEFAULT 0`);
|
|
127
127
|
const positionMap = {
|
|
@@ -135,7 +135,7 @@ export function runMigrations(db) {
|
|
|
135
135
|
// Column may already exist
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
if (!actionsColumnNames.
|
|
138
|
+
if (!actionsColumnNames.has('end_prompt')) {
|
|
139
139
|
try {
|
|
140
140
|
db.exec(`ALTER TABLE ${T.actions} ADD COLUMN end_prompt TEXT`);
|
|
141
141
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses the unified workspace.db database with pmo_ prefixed tables.
|
|
6
6
|
*/
|
|
7
7
|
import Database from 'better-sqlite3';
|
|
8
|
-
import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
|
|
8
|
+
import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Roadmap, RoadmapFilter, RoadmapProject, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
|
|
9
9
|
export declare class SQLiteStorage implements PMOStorage {
|
|
10
10
|
readonly type: "sqlite";
|
|
11
11
|
private db;
|
|
@@ -22,6 +22,7 @@ export declare class SQLiteStorage implements PMOStorage {
|
|
|
22
22
|
private phaseStorage;
|
|
23
23
|
private actionStorage;
|
|
24
24
|
private viewStorage;
|
|
25
|
+
private roadmapStorage;
|
|
25
26
|
constructor(dbPath: string);
|
|
26
27
|
/**
|
|
27
28
|
* Get the underlying database connection.
|
|
@@ -162,6 +163,20 @@ export declare class SQLiteStorage implements PMOStorage {
|
|
|
162
163
|
deleteBoardView(id: string): Promise<void>;
|
|
163
164
|
getDefaultBoardView(projectId: string): Promise<BoardView | null>;
|
|
164
165
|
getBoardWithView(projectId: string, viewId?: string, filters?: BoardViewFilters): Promise<Board>;
|
|
166
|
+
listRoadmaps(filter?: RoadmapFilter): Promise<Roadmap[]>;
|
|
167
|
+
getRoadmap(id: string): Promise<Roadmap | null>;
|
|
168
|
+
createRoadmap(roadmap: Partial<Roadmap> & {
|
|
169
|
+
name: string;
|
|
170
|
+
}): Promise<Roadmap>;
|
|
171
|
+
updateRoadmap(id: string, changes: Partial<Roadmap>): Promise<Roadmap>;
|
|
172
|
+
deleteRoadmap(id: string): Promise<void>;
|
|
173
|
+
getDefaultRoadmap(): Promise<Roadmap | null>;
|
|
174
|
+
setDefaultRoadmap(id: string): Promise<Roadmap>;
|
|
175
|
+
listRoadmapProjects(roadmapId: string): Promise<Project[]>;
|
|
176
|
+
addProjectToRoadmap(roadmapId: string, projectId: string, position?: number): Promise<RoadmapProject>;
|
|
177
|
+
removeProjectFromRoadmap(roadmapId: string, projectId: string): Promise<void>;
|
|
178
|
+
reorderRoadmapProject(roadmapId: string, projectId: string, newPosition: number): Promise<RoadmapProject>;
|
|
179
|
+
getRoadmapsForProject(projectId: string): Promise<Roadmap[]>;
|
|
165
180
|
pull(): Promise<SyncResult>;
|
|
166
181
|
push(): Promise<SyncResult>;
|
|
167
182
|
status(): Promise<SyncStatus>;
|
|
@@ -18,6 +18,7 @@ import { TemplateStorage } from './templates.js';
|
|
|
18
18
|
import { PhaseStorage } from './phases.js';
|
|
19
19
|
import { ActionStorage } from './actions.js';
|
|
20
20
|
import { ViewStorage } from './views.js';
|
|
21
|
+
import { RoadmapStorage } from './roadmaps.js';
|
|
21
22
|
const T = PMO_TABLES;
|
|
22
23
|
export class SQLiteStorage {
|
|
23
24
|
type = 'sqlite';
|
|
@@ -36,6 +37,7 @@ export class SQLiteStorage {
|
|
|
36
37
|
phaseStorage;
|
|
37
38
|
actionStorage;
|
|
38
39
|
viewStorage;
|
|
40
|
+
roadmapStorage;
|
|
39
41
|
constructor(dbPath) {
|
|
40
42
|
this.dbPath = dbPath;
|
|
41
43
|
// Open database (creates if doesn't exist)
|
|
@@ -60,6 +62,7 @@ export class SQLiteStorage {
|
|
|
60
62
|
this.phaseStorage = new PhaseStorage(ctx);
|
|
61
63
|
this.actionStorage = new ActionStorage(ctx);
|
|
62
64
|
this.viewStorage = new ViewStorage(ctx);
|
|
65
|
+
this.roadmapStorage = new RoadmapStorage(ctx);
|
|
63
66
|
// Ensure PMO tables exist
|
|
64
67
|
this.ensurePMOTables();
|
|
65
68
|
}
|
|
@@ -520,6 +523,48 @@ export class SQLiteStorage {
|
|
|
520
523
|
return this.viewStorage.getBoardWithView(projectId, viewId, filters);
|
|
521
524
|
}
|
|
522
525
|
// ===========================================================================
|
|
526
|
+
// Roadmap Operations
|
|
527
|
+
// ===========================================================================
|
|
528
|
+
async listRoadmaps(filter) {
|
|
529
|
+
return this.roadmapStorage.listRoadmaps(filter);
|
|
530
|
+
}
|
|
531
|
+
async getRoadmap(id) {
|
|
532
|
+
return this.roadmapStorage.getRoadmap(id);
|
|
533
|
+
}
|
|
534
|
+
async createRoadmap(roadmap) {
|
|
535
|
+
return this.roadmapStorage.createRoadmap(roadmap);
|
|
536
|
+
}
|
|
537
|
+
async updateRoadmap(id, changes) {
|
|
538
|
+
return this.roadmapStorage.updateRoadmap(id, changes);
|
|
539
|
+
}
|
|
540
|
+
async deleteRoadmap(id) {
|
|
541
|
+
return this.roadmapStorage.deleteRoadmap(id);
|
|
542
|
+
}
|
|
543
|
+
async getDefaultRoadmap() {
|
|
544
|
+
return this.roadmapStorage.getDefaultRoadmap();
|
|
545
|
+
}
|
|
546
|
+
async setDefaultRoadmap(id) {
|
|
547
|
+
return this.roadmapStorage.setDefaultRoadmap(id);
|
|
548
|
+
}
|
|
549
|
+
// ===========================================================================
|
|
550
|
+
// Roadmap Project Operations
|
|
551
|
+
// ===========================================================================
|
|
552
|
+
async listRoadmapProjects(roadmapId) {
|
|
553
|
+
return this.roadmapStorage.listRoadmapProjects(roadmapId);
|
|
554
|
+
}
|
|
555
|
+
async addProjectToRoadmap(roadmapId, projectId, position) {
|
|
556
|
+
return this.roadmapStorage.addProjectToRoadmap(roadmapId, projectId, position);
|
|
557
|
+
}
|
|
558
|
+
async removeProjectFromRoadmap(roadmapId, projectId) {
|
|
559
|
+
return this.roadmapStorage.removeProjectFromRoadmap(roadmapId, projectId);
|
|
560
|
+
}
|
|
561
|
+
async reorderRoadmapProject(roadmapId, projectId, newPosition) {
|
|
562
|
+
return this.roadmapStorage.reorderRoadmapProject(roadmapId, projectId, newPosition);
|
|
563
|
+
}
|
|
564
|
+
async getRoadmapsForProject(projectId) {
|
|
565
|
+
return this.roadmapStorage.getRoadmapsForProject(projectId);
|
|
566
|
+
}
|
|
567
|
+
// ===========================================================================
|
|
523
568
|
// Sync Operations (no-op for pure SQLite)
|
|
524
569
|
// ===========================================================================
|
|
525
570
|
async pull() {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roadmap operations for PMO.
|
|
3
|
+
* Roadmaps are curated collections of projects for documentation/visualization.
|
|
4
|
+
*/
|
|
5
|
+
import { Roadmap, RoadmapProject, RoadmapFilter, Project } from '../types.js';
|
|
6
|
+
import { StorageContext } from './types.js';
|
|
7
|
+
export declare class RoadmapStorage {
|
|
8
|
+
private ctx;
|
|
9
|
+
constructor(ctx: StorageContext);
|
|
10
|
+
/**
|
|
11
|
+
* Create a new roadmap.
|
|
12
|
+
*/
|
|
13
|
+
createRoadmap(roadmap: Partial<Roadmap> & {
|
|
14
|
+
name: string;
|
|
15
|
+
}): Promise<Roadmap>;
|
|
16
|
+
/**
|
|
17
|
+
* Get a roadmap by ID.
|
|
18
|
+
*/
|
|
19
|
+
getRoadmap(id: string): Promise<Roadmap | null>;
|
|
20
|
+
/**
|
|
21
|
+
* List roadmaps with optional filters.
|
|
22
|
+
*/
|
|
23
|
+
listRoadmaps(filter?: RoadmapFilter): Promise<Roadmap[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Update a roadmap.
|
|
26
|
+
*/
|
|
27
|
+
updateRoadmap(id: string, changes: Partial<Roadmap>): Promise<Roadmap>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete a roadmap.
|
|
30
|
+
*/
|
|
31
|
+
deleteRoadmap(id: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Get the default roadmap.
|
|
34
|
+
*/
|
|
35
|
+
getDefaultRoadmap(): Promise<Roadmap | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Set a roadmap as the default.
|
|
38
|
+
*/
|
|
39
|
+
setDefaultRoadmap(id: string): Promise<Roadmap>;
|
|
40
|
+
/**
|
|
41
|
+
* List projects in a roadmap, ordered by position.
|
|
42
|
+
*/
|
|
43
|
+
listRoadmapProjects(roadmapId: string): Promise<Project[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Add a project to a roadmap.
|
|
46
|
+
*/
|
|
47
|
+
addProjectToRoadmap(roadmapId: string, projectId: string, position?: number): Promise<RoadmapProject>;
|
|
48
|
+
/**
|
|
49
|
+
* Remove a project from a roadmap.
|
|
50
|
+
*/
|
|
51
|
+
removeProjectFromRoadmap(roadmapId: string, projectId: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Reorder a project within a roadmap.
|
|
54
|
+
*/
|
|
55
|
+
reorderRoadmapProject(roadmapId: string, projectId: string, newPosition: number): Promise<RoadmapProject>;
|
|
56
|
+
/**
|
|
57
|
+
* Get all roadmaps that contain a project.
|
|
58
|
+
*/
|
|
59
|
+
getRoadmapsForProject(projectId: string): Promise<Roadmap[]>;
|
|
60
|
+
private rowToRoadmap;
|
|
61
|
+
private rowToProject;
|
|
62
|
+
}
|