@proletariat/cli 0.3.26 → 0.3.28
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/bin/dev.js +7 -0
- package/bin/run.js +7 -0
- package/dist/commands/action/show.js +7 -1
- package/dist/commands/agent/shell.js +24 -10
- package/dist/commands/branch/list.js +14 -11
- package/dist/commands/branch/validate.js +10 -1
- package/dist/commands/claude.js +12 -40
- package/dist/commands/docker/clean.js +7 -9
- package/dist/commands/docker/index.js +5 -4
- package/dist/commands/docker/list.d.ts +1 -0
- package/dist/commands/docker/list.js +31 -17
- package/dist/commands/docker/status.d.ts +3 -1
- package/dist/commands/docker/status.js +28 -2
- package/dist/commands/docker/sync.js +7 -6
- package/dist/commands/epic/list.js +17 -2
- package/dist/commands/execution/list.js +25 -17
- package/dist/commands/pmo/init.js +22 -3
- package/dist/commands/repo/list.js +14 -8
- package/dist/commands/repo/view.js +2 -1
- package/dist/commands/roadmap/list.js +16 -1
- package/dist/commands/session/health.js +11 -10
- package/dist/commands/session/list.js +15 -8
- package/dist/commands/staff/list.d.ts +3 -1
- package/dist/commands/staff/list.js +15 -1
- package/dist/commands/theme/list.d.ts +3 -0
- package/dist/commands/theme/list.js +25 -0
- package/dist/commands/ticket/complete.js +4 -1
- package/dist/commands/ticket/create.d.ts +1 -0
- package/dist/commands/ticket/create.js +30 -0
- package/dist/commands/ticket/delete.js +3 -3
- package/dist/commands/ticket/edit.js +2 -2
- package/dist/commands/ticket/list.js +24 -5
- package/dist/commands/ticket/move.js +4 -1
- package/dist/commands/ticket/view.js +4 -2
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +22 -5
- package/dist/commands/work/complete.js +2 -2
- package/dist/commands/work/ready.js +2 -2
- package/dist/commands/work/revise.js +2 -2
- package/dist/commands/work/spawn.js +6 -21
- package/dist/commands/work/start.js +10 -25
- package/dist/commands/work/watch.js +57 -33
- package/dist/commands/workspace/prune.d.ts +3 -2
- package/dist/commands/workspace/prune.js +70 -10
- package/dist/lib/agents/commands.js +4 -0
- package/dist/lib/agents/index.js +12 -0
- package/dist/lib/execution/devcontainer.d.ts +4 -0
- package/dist/lib/execution/devcontainer.js +88 -3
- package/dist/lib/mcp/helpers.d.ts +15 -0
- package/dist/lib/mcp/helpers.js +15 -0
- package/dist/lib/mcp/tools/action.js +5 -5
- package/dist/lib/mcp/tools/board.js +7 -7
- package/dist/lib/mcp/tools/category.js +5 -5
- package/dist/lib/mcp/tools/cli-passthrough.js +30 -30
- package/dist/lib/mcp/tools/epic.js +8 -8
- package/dist/lib/mcp/tools/phase.js +7 -7
- package/dist/lib/mcp/tools/project.js +10 -10
- package/dist/lib/mcp/tools/roadmap.js +7 -7
- package/dist/lib/mcp/tools/spec.js +9 -9
- package/dist/lib/mcp/tools/status.js +6 -6
- package/dist/lib/mcp/tools/template.js +6 -6
- package/dist/lib/mcp/tools/ticket.js +19 -19
- package/dist/lib/mcp/tools/view.js +4 -4
- package/dist/lib/mcp/tools/work.js +6 -6
- package/dist/lib/mcp/tools/workflow.js +5 -5
- package/dist/lib/pmo/index.js +4 -0
- package/dist/lib/pmo/storage/base.js +49 -0
- package/dist/lib/pmo/types.d.ts +1 -1
- package/dist/lib/pmo/types.js +1 -0
- package/dist/lib/pr/index.d.ts +5 -0
- package/dist/lib/pr/index.js +69 -0
- package/dist/lib/repos/index.js +4 -0
- package/dist/lib/string-utils.d.ts +10 -0
- package/dist/lib/string-utils.js +16 -0
- package/oclif.manifest.json +3331 -3253
- package/package.json +3 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Work Tools (Agent workflow)
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { formatTicket, errorResponse } from '../helpers.js';
|
|
5
|
+
import { formatTicket, errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerWorkTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'work_status', 'Get current work status (in-progress tickets)', {}, async () => {
|
|
8
8
|
try {
|
|
9
9
|
const tickets = await ctx.storage.listTickets(undefined, { allProjects: true });
|
|
10
10
|
const inProgress = tickets.filter((t) => t.statusCategory === 'started' && t.assignee);
|
|
@@ -31,7 +31,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
31
31
|
return errorResponse(error);
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
-
server
|
|
34
|
+
strictTool(server, 'work_start', 'Start working on a ticket (moves to In Progress)', {
|
|
35
35
|
ticket_id: z.string().describe('Ticket ID'),
|
|
36
36
|
assignee: z.string().optional().describe('Who is working'),
|
|
37
37
|
}, async (params) => {
|
|
@@ -60,7 +60,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
60
60
|
return errorResponse(error);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
|
-
server
|
|
63
|
+
strictTool(server, 'work_complete', 'Mark ticket complete (moves to Done)', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
64
64
|
try {
|
|
65
65
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
66
66
|
if (!ticket)
|
|
@@ -83,7 +83,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
83
83
|
return errorResponse(error);
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
|
-
server
|
|
86
|
+
strictTool(server, 'work_ready', 'Mark ticket ready for review', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
87
87
|
try {
|
|
88
88
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
89
89
|
if (!ticket)
|
|
@@ -110,7 +110,7 @@ export function registerWorkTools(server, ctx) {
|
|
|
110
110
|
return errorResponse(error);
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
|
-
server
|
|
113
|
+
strictTool(server, 'work_revise', 'Send ticket back for revision', { ticket_id: z.string().describe('Ticket ID') }, async (params) => {
|
|
114
114
|
try {
|
|
115
115
|
const ticket = await ctx.storage.getTicket(params.ticket_id);
|
|
116
116
|
if (!ticket)
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Workflow Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { errorResponse } from '../helpers.js';
|
|
5
|
+
import { errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerWorkflowTools(server, ctx) {
|
|
7
|
-
server
|
|
7
|
+
strictTool(server, 'workflow_list', 'List all workflows', { include_builtin: z.boolean().optional() }, async (params) => {
|
|
8
8
|
try {
|
|
9
9
|
const workflows = await ctx.storage.listWorkflows({
|
|
10
10
|
isBuiltin: params.include_builtin ? undefined : false,
|
|
@@ -28,7 +28,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
28
28
|
return errorResponse(error);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
|
-
server
|
|
31
|
+
strictTool(server, 'workflow_show', 'Get workflow details with statuses', { id: z.string().describe('Workflow ID') }, async (params) => {
|
|
32
32
|
try {
|
|
33
33
|
const workflow = await ctx.storage.getWorkflow(params.id);
|
|
34
34
|
if (!workflow)
|
|
@@ -57,7 +57,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
57
57
|
return errorResponse(error);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
-
server
|
|
60
|
+
strictTool(server, 'workflow_create', 'Create a new workflow', {
|
|
61
61
|
name: z.string().describe('Workflow name'),
|
|
62
62
|
description: z.string().optional(),
|
|
63
63
|
statuses: z.array(z.string()).optional().describe('Status names'),
|
|
@@ -78,7 +78,7 @@ export function registerWorkflowTools(server, ctx) {
|
|
|
78
78
|
return errorResponse(error);
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
-
server
|
|
81
|
+
strictTool(server, 'workflow_delete', 'Delete a workflow', { id: z.string().describe('Workflow ID') }, async (params) => {
|
|
82
82
|
try {
|
|
83
83
|
await ctx.storage.deleteWorkflow(params.id);
|
|
84
84
|
return {
|
package/dist/lib/pmo/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SQLiteStorage } from './storage-sqlite.js';
|
|
|
6
6
|
import { createSpecFolders } from './create-spec-folders.js';
|
|
7
7
|
import { slugify } from './utils.js';
|
|
8
8
|
import { createDevcontainerConfig } from '../execution/devcontainer.js';
|
|
9
|
+
import { getGitIdentity } from '../pr/index.js';
|
|
9
10
|
// Re-export new PMO modules
|
|
10
11
|
export * from './types.js';
|
|
11
12
|
export * from './utils.js';
|
|
@@ -383,10 +384,13 @@ ${columns.join(', ')}
|
|
|
383
384
|
// Create devcontainer for separate PMO (it's its own repo)
|
|
384
385
|
if (location === 'separate') {
|
|
385
386
|
console.log(chalk.blue('Creating devcontainer config for PMO...'));
|
|
387
|
+
const gitIdentity = getGitIdentity();
|
|
386
388
|
createDevcontainerConfig({
|
|
387
389
|
agentName: 'pmo',
|
|
388
390
|
agentDir: pmoPath,
|
|
389
391
|
repoWorktrees: [], // PMO is the repo itself, no nested worktrees
|
|
392
|
+
gitUserName: gitIdentity.name || undefined,
|
|
393
|
+
gitUserEmail: gitIdentity.email || undefined,
|
|
390
394
|
});
|
|
391
395
|
console.log(chalk.green(' ✓ Devcontainer config created'));
|
|
392
396
|
}
|
|
@@ -218,6 +218,55 @@ export function runMigrations(db) {
|
|
|
218
218
|
// Table may already be dropped
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
+
// Migration: Reassign orphaned tickets (TKT-940)
|
|
222
|
+
// Tickets with project_id that doesn't match any existing project are "orphaned".
|
|
223
|
+
// This can happen when a 'default' project never existed or was deleted.
|
|
224
|
+
// NOTE: This runs on every init but is idempotent — if no orphaned tickets exist, it's a no-op.
|
|
225
|
+
// Kept as a runtime check rather than a one-time migration since orphaned tickets could
|
|
226
|
+
// reappear if projects are deleted in the future.
|
|
227
|
+
if (tableExists(T.tickets) && tableExists(T.projects)) {
|
|
228
|
+
try {
|
|
229
|
+
// Find orphaned tickets (project_id doesn't match any project)
|
|
230
|
+
const orphanedTickets = db.prepare(`
|
|
231
|
+
SELECT t.id, t.project_id
|
|
232
|
+
FROM ${T.tickets} t
|
|
233
|
+
LEFT JOIN ${T.projects} p ON t.project_id = p.id
|
|
234
|
+
WHERE p.id IS NULL
|
|
235
|
+
`).all();
|
|
236
|
+
if (orphanedTickets.length > 0) {
|
|
237
|
+
// Get the first available project to reassign to
|
|
238
|
+
const firstProject = db.prepare(`
|
|
239
|
+
SELECT id FROM ${T.projects} ORDER BY created_at ASC LIMIT 1
|
|
240
|
+
`).get();
|
|
241
|
+
if (firstProject) {
|
|
242
|
+
// Get the default status for the target project's workflow
|
|
243
|
+
const project = db.prepare(`
|
|
244
|
+
SELECT workflow_id FROM ${T.projects} WHERE id = ?
|
|
245
|
+
`).get(firstProject.id);
|
|
246
|
+
const workflowId = project?.workflow_id || 'default';
|
|
247
|
+
const defaultStatus = db.prepare(`
|
|
248
|
+
SELECT id FROM ${T.workflow_statuses}
|
|
249
|
+
WHERE workflow_id = ? AND is_default = 1
|
|
250
|
+
`).get(workflowId);
|
|
251
|
+
// Reassign orphaned tickets to the first project
|
|
252
|
+
const updateStmt = defaultStatus
|
|
253
|
+
? db.prepare(`UPDATE ${T.tickets} SET project_id = ?, status_id = COALESCE(status_id, ?) WHERE id = ?`)
|
|
254
|
+
: db.prepare(`UPDATE ${T.tickets} SET project_id = ? WHERE id = ?`);
|
|
255
|
+
for (const ticket of orphanedTickets) {
|
|
256
|
+
if (defaultStatus) {
|
|
257
|
+
updateStmt.run(firstProject.id, defaultStatus.id, ticket.id);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
updateStmt.run(firstProject.id, ticket.id);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Non-critical migration - don't fail initialization
|
|
268
|
+
}
|
|
269
|
+
}
|
|
221
270
|
}
|
|
222
271
|
/**
|
|
223
272
|
* Seed built-in workflows from BUILTIN_TEMPLATES (single source of truth).
|
package/dist/lib/pmo/types.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ export declare const LEGACY_PRIORITY_MAP: Record<LegacyPriority, Priority>;
|
|
|
81
81
|
/**
|
|
82
82
|
* Ticket categories for classification.
|
|
83
83
|
*/
|
|
84
|
-
export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release"];
|
|
84
|
+
export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release", "adhoc"];
|
|
85
85
|
export type TicketCategory = typeof TICKET_CATEGORIES[number];
|
|
86
86
|
/**
|
|
87
87
|
* Category types (ticket vs status).
|
package/dist/lib/pmo/types.js
CHANGED
package/dist/lib/pr/index.d.ts
CHANGED
|
@@ -44,6 +44,11 @@ export declare function getGHUsername(): string | null;
|
|
|
44
44
|
* Check if GH_TOKEN or GITHUB_TOKEN is set in environment.
|
|
45
45
|
*/
|
|
46
46
|
export declare function isGHTokenInEnv(): boolean;
|
|
47
|
+
export interface GitIdentity {
|
|
48
|
+
name: string | null;
|
|
49
|
+
email: string | null;
|
|
50
|
+
}
|
|
51
|
+
export declare function getGitIdentity(cwd?: string): GitIdentity;
|
|
47
52
|
/**
|
|
48
53
|
* Get the GitHub repository from git remote.
|
|
49
54
|
* Returns format: owner/repo
|
package/dist/lib/pr/index.js
CHANGED
|
@@ -53,6 +53,75 @@ export function getGHUsername() {
|
|
|
53
53
|
export function isGHTokenInEnv() {
|
|
54
54
|
return !!(process.env.GH_TOKEN || process.env.GITHUB_TOKEN);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Detect the user's git identity for commit attribution.
|
|
58
|
+
* Tries GitHub CLI first (gh api user), falls back to git config.
|
|
59
|
+
* Results are memoized so subprocess calls only run once per process.
|
|
60
|
+
*/
|
|
61
|
+
let _cachedGitIdentity;
|
|
62
|
+
export function getGitIdentity(cwd) {
|
|
63
|
+
if (_cachedGitIdentity)
|
|
64
|
+
return _cachedGitIdentity;
|
|
65
|
+
let name = null;
|
|
66
|
+
let email = null;
|
|
67
|
+
// Method 1: Try gh api user (most reliable for GitHub identity)
|
|
68
|
+
try {
|
|
69
|
+
const userJson = execSync('gh api user', {
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
72
|
+
});
|
|
73
|
+
const user = JSON.parse(userJson);
|
|
74
|
+
name = user.name || user.login || null;
|
|
75
|
+
email = user.email || null;
|
|
76
|
+
// If no public email, try GitHub emails API for primary email
|
|
77
|
+
if (!email) {
|
|
78
|
+
try {
|
|
79
|
+
const emailsJson = execSync('gh api user/emails', {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
+
});
|
|
83
|
+
const emails = JSON.parse(emailsJson);
|
|
84
|
+
const primary = emails.find(e => e.primary);
|
|
85
|
+
if (primary) {
|
|
86
|
+
email = primary.email;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// emails API may not be accessible with current token scope
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// gh not available or not authenticated
|
|
96
|
+
}
|
|
97
|
+
// Method 2: Fall back to git config
|
|
98
|
+
if (!name) {
|
|
99
|
+
try {
|
|
100
|
+
name = execSync('git config user.name', {
|
|
101
|
+
cwd,
|
|
102
|
+
encoding: 'utf-8',
|
|
103
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
|
+
}).trim() || null;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// git config not set
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!email) {
|
|
111
|
+
try {
|
|
112
|
+
email = execSync('git config user.email', {
|
|
113
|
+
cwd,
|
|
114
|
+
encoding: 'utf-8',
|
|
115
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
116
|
+
}).trim() || null;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// git config not set
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
_cachedGitIdentity = { name, email };
|
|
123
|
+
return _cachedGitIdentity;
|
|
124
|
+
}
|
|
56
125
|
// =============================================================================
|
|
57
126
|
// Git Remote Detection
|
|
58
127
|
// =============================================================================
|
package/dist/lib/repos/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { styles } from '../styles.js';
|
|
|
7
7
|
import { colors } from '../colors.js';
|
|
8
8
|
import { openWorkspaceDatabase, addRepositoriesToDatabase, getWorkspaceRepositories } from '../database/index.js';
|
|
9
9
|
import { createDevcontainerConfig } from '../execution/devcontainer.js';
|
|
10
|
+
import { getGitIdentity } from '../pr/index.js';
|
|
10
11
|
import { findHQRoot } from '../workspace.js';
|
|
11
12
|
/**
|
|
12
13
|
* Check if we're currently in a git repository
|
|
@@ -452,10 +453,13 @@ export async function addRepository(hqPath, repoPath, action) {
|
|
|
452
453
|
await createWorktreesForRepo(hqPath, repoName, targetPath);
|
|
453
454
|
// Create devcontainer config for sandboxed execution in the central repo
|
|
454
455
|
console.log(styles.muted(`Creating devcontainer config for ${repoName}...`));
|
|
456
|
+
const gitIdentity = getGitIdentity();
|
|
455
457
|
createDevcontainerConfig({
|
|
456
458
|
agentName: repoName, // Use repo name as identifier
|
|
457
459
|
agentDir: targetPath,
|
|
458
460
|
repoWorktrees: [], // No nested worktrees - this is the repo itself
|
|
461
|
+
gitUserName: gitIdentity.name || undefined,
|
|
462
|
+
gitUserEmail: gitIdentity.email || undefined,
|
|
459
463
|
});
|
|
460
464
|
return { success: true, name: repoName };
|
|
461
465
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String utilities for CLI table formatting.
|
|
3
|
+
*
|
|
4
|
+
* Handles emoji and wide characters that occupy more than one terminal column.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Pad a string to the given visual width, accounting for emoji and wide characters.
|
|
8
|
+
* Unlike String.padEnd(), this measures visual column width rather than character count.
|
|
9
|
+
*/
|
|
10
|
+
export declare function visualPadEnd(str: string, width: number): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String utilities for CLI table formatting.
|
|
3
|
+
*
|
|
4
|
+
* Handles emoji and wide characters that occupy more than one terminal column.
|
|
5
|
+
*/
|
|
6
|
+
import stringWidth from 'string-width';
|
|
7
|
+
/**
|
|
8
|
+
* Pad a string to the given visual width, accounting for emoji and wide characters.
|
|
9
|
+
* Unlike String.padEnd(), this measures visual column width rather than character count.
|
|
10
|
+
*/
|
|
11
|
+
export function visualPadEnd(str, width) {
|
|
12
|
+
const visWidth = stringWidth(str);
|
|
13
|
+
if (visWidth >= width)
|
|
14
|
+
return str;
|
|
15
|
+
return str + ' '.repeat(width - visWidth);
|
|
16
|
+
}
|