@proletariat/cli 0.3.58 → 0.3.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/{spec → dashboard}/index.d.ts +4 -4
- package/dist/commands/dashboard/index.js +117 -0
- package/dist/commands/dashboard/index.js.map +1 -0
- package/dist/commands/execution/config.js +5 -4
- package/dist/commands/execution/config.js.map +1 -1
- package/dist/commands/execution/stop.js +4 -2
- package/dist/commands/execution/stop.js.map +1 -1
- package/dist/commands/execution/view.js +3 -0
- package/dist/commands/execution/view.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +40 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp-server.js +1 -2
- package/dist/commands/mcp-server.js.map +1 -1
- package/dist/commands/{spec/link/index.d.ts → session/exec.d.ts} +5 -3
- package/dist/commands/session/exec.js +205 -0
- package/dist/commands/session/exec.js.map +1 -0
- package/dist/commands/session/index.js +12 -0
- package/dist/commands/session/index.js.map +1 -1
- package/dist/commands/{spec/delete.d.ts → session/inspect.d.ts} +3 -3
- package/dist/commands/session/inspect.js +316 -0
- package/dist/commands/session/inspect.js.map +1 -0
- package/dist/commands/session/peek.d.ts +15 -0
- package/dist/commands/session/peek.js +141 -8
- package/dist/commands/session/peek.js.map +1 -1
- package/dist/commands/session/poke.d.ts +4 -1
- package/dist/commands/session/poke.js +175 -20
- package/dist/commands/session/poke.js.map +1 -1
- package/dist/commands/{spec/link/depends.d.ts → session/restart.d.ts} +6 -4
- package/dist/commands/session/restart.js +320 -0
- package/dist/commands/session/restart.js.map +1 -0
- package/dist/commands/ticket/list.d.ts +10 -0
- package/dist/commands/ticket/list.js +126 -1
- package/dist/commands/ticket/list.js.map +1 -1
- package/dist/commands/tools/add.d.ts +20 -0
- package/dist/commands/tools/add.js +129 -0
- package/dist/commands/tools/add.js.map +1 -0
- package/dist/commands/tools/check.d.ts +10 -0
- package/dist/commands/tools/check.js +75 -0
- package/dist/commands/tools/check.js.map +1 -0
- package/dist/commands/tools/detect.d.ts +11 -0
- package/dist/commands/tools/detect.js +107 -0
- package/dist/commands/tools/detect.js.map +1 -0
- package/dist/commands/tools/index.d.ts +11 -0
- package/dist/commands/tools/index.js +87 -0
- package/dist/commands/tools/index.js.map +1 -0
- package/dist/commands/tools/remove.d.ts +13 -0
- package/dist/commands/tools/remove.js +55 -0
- package/dist/commands/tools/remove.js.map +1 -0
- package/dist/commands/{spec/view.d.ts → trello/configure.d.ts} +6 -6
- package/dist/commands/trello/configure.js +259 -0
- package/dist/commands/trello/configure.js.map +1 -0
- package/dist/commands/{spec/plan.d.ts → trello/import.d.ts} +3 -5
- package/dist/commands/trello/import.js +241 -0
- package/dist/commands/trello/import.js.map +1 -0
- package/dist/commands/{spec/ticket.d.ts → trello/sync.d.ts} +5 -6
- package/dist/commands/trello/sync.js +190 -0
- package/dist/commands/trello/sync.js.map +1 -0
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +17 -39
- package/dist/commands/work/start.js.map +1 -1
- package/dist/lib/dashboard/data.d.ts +64 -0
- package/dist/lib/dashboard/data.js +259 -0
- package/dist/lib/dashboard/data.js.map +1 -0
- package/dist/lib/dashboard/html.d.ts +7 -0
- package/dist/lib/dashboard/html.js +682 -0
- package/dist/lib/dashboard/html.js.map +1 -0
- package/dist/lib/dashboard/server.d.ts +20 -0
- package/dist/lib/dashboard/server.js +114 -0
- package/dist/lib/dashboard/server.js.map +1 -0
- package/dist/lib/events/events.d.ts +26 -2
- package/dist/lib/events/events.js +3 -2
- package/dist/lib/events/events.js.map +1 -1
- package/dist/lib/execution/config.d.ts +8 -0
- package/dist/lib/execution/config.js +83 -4
- package/dist/lib/execution/config.js.map +1 -1
- package/dist/lib/execution/runners.d.ts +36 -3
- package/dist/lib/execution/runners.js +216 -35
- package/dist/lib/execution/runners.js.map +1 -1
- package/dist/lib/execution/spawner.d.ts +2 -0
- package/dist/lib/execution/spawner.js +22 -31
- package/dist/lib/execution/spawner.js.map +1 -1
- package/dist/lib/execution/types.d.ts +26 -5
- package/dist/lib/execution/types.js +24 -0
- package/dist/lib/execution/types.js.map +1 -1
- package/dist/lib/external-issues/adapters.d.ts +17 -0
- package/dist/lib/external-issues/adapters.js +88 -0
- package/dist/lib/external-issues/adapters.js.map +1 -1
- package/dist/lib/external-issues/index.d.ts +1 -0
- package/dist/lib/external-issues/index.js +2 -0
- package/dist/lib/external-issues/index.js.map +1 -1
- package/dist/lib/external-issues/mapping-store.js +1 -1
- package/dist/lib/external-issues/outbound-sync.d.ts +79 -0
- package/dist/lib/external-issues/outbound-sync.js +296 -0
- package/dist/lib/external-issues/outbound-sync.js.map +1 -0
- package/dist/lib/external-issues/trello.d.ts +80 -0
- package/dist/lib/external-issues/trello.js +266 -0
- package/dist/lib/external-issues/trello.js.map +1 -0
- package/dist/lib/external-issues/types.d.ts +3 -3
- package/dist/lib/external-issues/types.js +1 -1
- package/dist/lib/external-issues/types.js.map +1 -1
- package/dist/lib/linear/client.d.ts +4 -3
- package/dist/lib/linear/client.js +185 -122
- package/dist/lib/linear/client.js.map +1 -1
- package/dist/lib/mcp/tools/cli-passthrough.js +77 -0
- package/dist/lib/mcp/tools/cli-passthrough.js.map +1 -1
- package/dist/lib/mcp/tools/index.d.ts +0 -1
- package/dist/lib/mcp/tools/index.js +0 -1
- package/dist/lib/mcp/tools/index.js.map +1 -1
- package/dist/lib/onboarding/detect-tools.d.ts +15 -0
- package/dist/lib/onboarding/detect-tools.js +44 -0
- package/dist/lib/onboarding/detect-tools.js.map +1 -0
- package/dist/lib/onboarding/index.d.ts +2 -0
- package/dist/lib/onboarding/index.js +3 -0
- package/dist/lib/onboarding/index.js.map +1 -0
- package/dist/lib/onboarding/wizard.d.ts +25 -0
- package/dist/lib/onboarding/wizard.js +156 -0
- package/dist/lib/onboarding/wizard.js.map +1 -0
- package/dist/lib/pmo/schema.d.ts +2 -1
- package/dist/lib/pmo/schema.js +3 -1
- package/dist/lib/pmo/schema.js.map +1 -1
- package/dist/lib/pmo/storage/tickets.d.ts +8 -0
- package/dist/lib/pmo/storage/tickets.js +56 -2
- package/dist/lib/pmo/storage/tickets.js.map +1 -1
- package/dist/lib/pmo/sync-manager.js +3 -0
- package/dist/lib/pmo/sync-manager.js.map +1 -1
- package/dist/lib/runners/claude-code-runner.js +6 -0
- package/dist/lib/runners/claude-code-runner.js.map +1 -1
- package/dist/lib/tool-registry/detect.d.ts +20 -0
- package/dist/lib/tool-registry/detect.js +95 -0
- package/dist/lib/tool-registry/detect.js.map +1 -0
- package/dist/lib/tool-registry/index.d.ts +10 -0
- package/dist/lib/tool-registry/index.js +13 -0
- package/dist/lib/tool-registry/index.js.map +1 -0
- package/dist/lib/tool-registry/policy.d.ts +32 -0
- package/dist/lib/tool-registry/policy.js +97 -0
- package/dist/lib/tool-registry/policy.js.map +1 -0
- package/dist/lib/tool-registry/registry.d.ts +42 -0
- package/dist/lib/tool-registry/registry.js +120 -0
- package/dist/lib/tool-registry/registry.js.map +1 -0
- package/dist/lib/tool-registry/spawn.d.ts +50 -0
- package/dist/lib/tool-registry/spawn.js +103 -0
- package/dist/lib/tool-registry/spawn.js.map +1 -0
- package/dist/lib/tool-registry/types.d.ts +56 -0
- package/dist/lib/tool-registry/types.js +109 -0
- package/dist/lib/tool-registry/types.js.map +1 -0
- package/dist/lib/trello/client.d.ts +23 -0
- package/dist/lib/trello/client.js +114 -0
- package/dist/lib/trello/client.js.map +1 -0
- package/dist/lib/trello/config.d.ts +55 -0
- package/dist/lib/trello/config.js +127 -0
- package/dist/lib/trello/config.js.map +1 -0
- package/dist/lib/trello/index.d.ts +5 -0
- package/dist/lib/trello/index.js +5 -0
- package/dist/lib/trello/index.js.map +1 -0
- package/dist/lib/trello/mapper.d.ts +13 -0
- package/dist/lib/trello/mapper.js +71 -0
- package/dist/lib/trello/mapper.js.map +1 -0
- package/dist/lib/trello/sync.d.ts +13 -0
- package/dist/lib/trello/sync.js +38 -0
- package/dist/lib/trello/sync.js.map +1 -0
- package/dist/lib/trello/types.d.ts +53 -0
- package/dist/lib/trello/types.js +2 -0
- package/dist/lib/trello/types.js.map +1 -0
- package/dist/lib/work-source/client.js +17 -0
- package/dist/lib/work-source/client.js.map +1 -1
- package/dist/lib/work-source/config.d.ts +1 -1
- package/dist/lib/work-source/config.js +23 -1
- package/dist/lib/work-source/config.js.map +1 -1
- package/dist/lib/work-source/index.d.ts +1 -0
- package/dist/lib/work-source/index.js +1 -0
- package/dist/lib/work-source/index.js.map +1 -1
- package/dist/lib/work-source/provider-sources.d.ts +113 -0
- package/dist/lib/work-source/provider-sources.js +227 -0
- package/dist/lib/work-source/provider-sources.js.map +1 -0
- package/oclif.manifest.json +4368 -4364
- package/package.json +6 -2
- package/dist/commands/spec/create.d.ts +0 -20
- package/dist/commands/spec/create.js +0 -171
- package/dist/commands/spec/create.js.map +0 -1
- package/dist/commands/spec/delete.js +0 -112
- package/dist/commands/spec/delete.js.map +0 -1
- package/dist/commands/spec/edit.d.ts +0 -23
- package/dist/commands/spec/edit.js +0 -262
- package/dist/commands/spec/edit.js.map +0 -1
- package/dist/commands/spec/index.js +0 -88
- package/dist/commands/spec/index.js.map +0 -1
- package/dist/commands/spec/link/depends.js +0 -87
- package/dist/commands/spec/link/depends.js.map +0 -1
- package/dist/commands/spec/link/index.js +0 -93
- package/dist/commands/spec/link/index.js.map +0 -1
- package/dist/commands/spec/link/remove.d.ts +0 -18
- package/dist/commands/spec/link/remove.js +0 -91
- package/dist/commands/spec/link/remove.js.map +0 -1
- package/dist/commands/spec/list.d.ts +0 -14
- package/dist/commands/spec/list.js +0 -101
- package/dist/commands/spec/list.js.map +0 -1
- package/dist/commands/spec/plan.js +0 -102
- package/dist/commands/spec/plan.js.map +0 -1
- package/dist/commands/spec/ticket.js +0 -144
- package/dist/commands/spec/ticket.js.map +0 -1
- package/dist/commands/spec/view.js +0 -202
- package/dist/commands/spec/view.js.map +0 -1
- package/dist/lib/mcp/tools/spec.d.ts +0 -6
- package/dist/lib/mcp/tools/spec.js +0 -197
- package/dist/lib/mcp/tools/spec.js.map +0 -1
- package/dist/lib/pmo/spec-parser.d.ts +0 -25
- package/dist/lib/pmo/spec-parser.js +0 -206
- package/dist/lib/pmo/spec-parser.js.map +0 -1
- package/dist/lib/pmo/spec-types.d.ts +0 -43
- package/dist/lib/pmo/spec-types.js +0 -8
- package/dist/lib/pmo/spec-types.js.map +0 -1
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Execution Runners
|
|
4
4
|
*
|
|
5
|
-
* Implementations for each execution environment (
|
|
5
|
+
* Implementations for each execution environment (host, sandbox, devcontainer, docker, cloud).
|
|
6
6
|
*/
|
|
7
7
|
import { spawn, execSync } from 'node:child_process';
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import * as os from 'node:os';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { DEFAULT_EXECUTION_CONFIG, } from './types.js';
|
|
12
|
+
import { DEFAULT_EXECUTION_CONFIG, normalizeEnvironment, } from './types.js';
|
|
13
13
|
import { getSetTitleCommands } from '../terminal.js';
|
|
14
14
|
import { readDevcontainerJson, generateOrchestratorDockerfile } from './devcontainer.js';
|
|
15
15
|
import { getCodexCommand, resolveCodexExecutionContext, validateCodexMode } from './codex-adapter.js';
|
|
16
|
+
import { resolveToolsForSpawn } from '../tool-registry/index.js';
|
|
16
17
|
// =============================================================================
|
|
17
18
|
// Terminal Title Helpers
|
|
18
19
|
// =============================================================================
|
|
@@ -380,10 +381,11 @@ export function checkExecutorInContainer(executor, containerId) {
|
|
|
380
381
|
* Run executor preflight checks for the target environment.
|
|
381
382
|
*/
|
|
382
383
|
export function runExecutorPreflight(environment, executor, options) {
|
|
383
|
-
|
|
384
|
+
const env = normalizeEnvironment(environment);
|
|
385
|
+
if (env === 'host' || env === 'sandbox') {
|
|
384
386
|
return checkExecutorOnHost(executor);
|
|
385
387
|
}
|
|
386
|
-
if (
|
|
388
|
+
if (env === 'devcontainer' && options?.containerId) {
|
|
387
389
|
return checkExecutorInContainer(executor, options.containerId);
|
|
388
390
|
}
|
|
389
391
|
return { ok: true };
|
|
@@ -555,6 +557,18 @@ function buildOrchestratorAntiPatterns() {
|
|
|
555
557
|
*/
|
|
556
558
|
function buildOrchestratorBody(hqName, context) {
|
|
557
559
|
let prompt = '';
|
|
560
|
+
// Dynamic workspace context
|
|
561
|
+
const prltVersion = getHostPrltVersion();
|
|
562
|
+
prompt += `## Environment\n`;
|
|
563
|
+
if (prltVersion) {
|
|
564
|
+
prompt += `- **prlt version**: ${prltVersion}\n`;
|
|
565
|
+
}
|
|
566
|
+
prompt += `- **Available executors**: claude-code, codex\n`;
|
|
567
|
+
prompt += `- **Agent worktrees**: \`agents/temp/<agent-name>/<repo>\` — each agent gets an isolated git worktree\n`;
|
|
568
|
+
if (context.hqPath) {
|
|
569
|
+
prompt += `- **HQ path**: \`${context.hqPath}\`\n`;
|
|
570
|
+
}
|
|
571
|
+
prompt += `\n`;
|
|
558
572
|
// Runtime declaration
|
|
559
573
|
prompt += `## prlt Is Your Orchestration Runtime\n\n`;
|
|
560
574
|
prompt += `prlt is your orchestration runtime. NEVER use raw docker exec, tmux send-keys, or direct container access. `;
|
|
@@ -563,10 +577,12 @@ function buildOrchestratorBody(hqName, context) {
|
|
|
563
577
|
prompt += `health monitoring, and creates orphaned processes.\n\n`;
|
|
564
578
|
// Role
|
|
565
579
|
prompt += `## Your Role\n`;
|
|
566
|
-
prompt += `-
|
|
580
|
+
prompt += `- Assess the current state of the board, running agents, and open PRs\n`;
|
|
581
|
+
prompt += `- Plan and prioritize work — decide what to tackle next and in what order\n`;
|
|
567
582
|
prompt += `- Delegate implementation to agents via \`prlt work start\`\n`;
|
|
568
|
-
prompt += `- Monitor agent progress and review completed work\n`;
|
|
569
|
-
prompt += `-
|
|
583
|
+
prompt += `- Monitor agent progress via sessions and review completed work\n`;
|
|
584
|
+
prompt += `- Review and merge completed PRs via \`gh pr merge --squash\`\n`;
|
|
585
|
+
prompt += `- Coordinate parallel agents — handle rebases after merges\n`;
|
|
570
586
|
prompt += `- Never write code or make changes to source files yourself\n\n`;
|
|
571
587
|
// Command reference (dynamically generated)
|
|
572
588
|
prompt += `## Command Reference\n\n`;
|
|
@@ -587,6 +603,13 @@ function buildOrchestratorBody(hqName, context) {
|
|
|
587
603
|
prompt += `- Squash merge only: \`gh pr merge --squash\`\n`;
|
|
588
604
|
prompt += `- After merging: subsequent PRs from parallel agents will need rebase\n`;
|
|
589
605
|
prompt += `- Kill stale sessions after their PRs are merged\n\n`;
|
|
606
|
+
// Tool registry (TKT-083): inject available tools into orchestrator prompt
|
|
607
|
+
if (context.hqPath) {
|
|
608
|
+
const toolsResult = resolveToolsForSpawn(context.hqPath, context.toolPolicy, path.join(context.hqPath, '.proletariat', 'scripts'));
|
|
609
|
+
if (toolsResult.promptSection) {
|
|
610
|
+
prompt += toolsResult.promptSection;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
590
613
|
// Load .orchestrator-context.md from HQ root if it exists
|
|
591
614
|
if (context.hqPath) {
|
|
592
615
|
const contextFilePath = path.join(context.hqPath, '.orchestrator-context.md');
|
|
@@ -611,9 +634,12 @@ function buildOrchestratorBody(hqName, context) {
|
|
|
611
634
|
*/
|
|
612
635
|
export function buildOrchestratorSystemPrompt(context) {
|
|
613
636
|
const hqName = context.hqName || 'workspace';
|
|
614
|
-
let prompt =
|
|
615
|
-
prompt += `
|
|
616
|
-
prompt +=
|
|
637
|
+
let prompt = `# Orchestrator: ${hqName}\n\n`;
|
|
638
|
+
prompt += `You are the orchestrator for the **${hqName}** headquarters — a technical project manager driving software delivery through delegated AI agents.\n\n`;
|
|
639
|
+
prompt += `**prlt** is an AI agent orchestration CLI. It manages software development by coordinating autonomous coding agents that work in isolated git worktrees. `;
|
|
640
|
+
prompt += `Your workspace (HQ) contains a PMO board for tracking tickets, agent worktrees under \`agents/temp/\`, and repo connections. `;
|
|
641
|
+
prompt += `Agents are spawned to implement, review, and fix code — you never write code yourself. `;
|
|
642
|
+
prompt += `Your job is to assess the state of the project, plan and prioritize work, delegate to agents, monitor their progress, review results, and merge completed PRs.\n\n`;
|
|
617
643
|
prompt += buildOrchestratorBody(hqName, context);
|
|
618
644
|
return prompt;
|
|
619
645
|
}
|
|
@@ -623,7 +649,10 @@ function buildOrchestratorPrompt(context) {
|
|
|
623
649
|
// a system prompt (role/tools) + a shorter user message.
|
|
624
650
|
const hqName = context.hqName || 'workspace';
|
|
625
651
|
let prompt = `# Orchestrator: ${hqName}\n\n`;
|
|
626
|
-
prompt += `You are the orchestrator for the **${hqName}**
|
|
652
|
+
prompt += `You are the orchestrator for the **${hqName}** headquarters — a technical project manager driving software delivery through delegated AI agents.\n\n`;
|
|
653
|
+
prompt += `**prlt** is an AI agent orchestration CLI. It manages software development by coordinating autonomous coding agents that work in isolated git worktrees. `;
|
|
654
|
+
prompt += `Your workspace (HQ) contains a PMO board for tracking tickets, agent worktrees under \`agents/temp/\`, and repo connections. `;
|
|
655
|
+
prompt += `Agents are spawned to implement, review, and fix code — you never write code yourself.\n\n`;
|
|
627
656
|
prompt += buildOrchestratorBody(hqName, context);
|
|
628
657
|
// Include user's custom prompt or action content
|
|
629
658
|
if (context.actionPrompt) {
|
|
@@ -662,9 +691,6 @@ function buildPrompt(context) {
|
|
|
662
691
|
if (context.epicTitle) {
|
|
663
692
|
prompt += `**Epic:** ${context.epicTitle}\n`;
|
|
664
693
|
}
|
|
665
|
-
if (context.specId) {
|
|
666
|
-
prompt += `**Spec:** ${context.specId}${context.specTitle ? ` - ${context.specTitle}` : ''}\n`;
|
|
667
|
-
}
|
|
668
694
|
if (context.ticketDescription) {
|
|
669
695
|
prompt += `\n## Description\n\n${context.ticketDescription}\n`;
|
|
670
696
|
}
|
|
@@ -686,6 +712,13 @@ function buildPrompt(context) {
|
|
|
686
712
|
if (context.customMessage) {
|
|
687
713
|
prompt += `\n## Additional Instructions\n\n${context.customMessage}\n`;
|
|
688
714
|
}
|
|
715
|
+
// Tool registry (TKT-083): inject available tools into agent prompt
|
|
716
|
+
if (context.hqPath) {
|
|
717
|
+
const toolsResult = resolveToolsForSpawn(context.hqPath, context.toolPolicy, path.join(context.hqPath, '.proletariat', 'scripts'));
|
|
718
|
+
if (toolsResult.promptSection) {
|
|
719
|
+
prompt += `\n${toolsResult.promptSection}`;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
689
722
|
// END HOOK - Action-specific completion instructions
|
|
690
723
|
prompt += `\n---\n\n## When Complete\n\n`;
|
|
691
724
|
// For revisions, use the revision-specific end prompt
|
|
@@ -787,13 +820,23 @@ export async function runHost(context, executor, config, displayMode = 'terminal
|
|
|
787
820
|
fs.writeFileSync(systemPromptPath, systemPrompt, { mode: 0o644 });
|
|
788
821
|
// Override user message: just action instructions or a default startup message
|
|
789
822
|
const userMessage = context.actionPrompt
|
|
790
|
-
|| '
|
|
823
|
+
|| 'Assess the current state of the project:\n'
|
|
824
|
+
+ '1. Check the board: `prlt board view` — what tickets are in progress, blocked, or ready?\n'
|
|
825
|
+
+ '2. List running agents: `prlt session list` — who is working on what? Any stale sessions?\n'
|
|
826
|
+
+ '3. Check open PRs: `gh pr list` — any PRs ready for review or merge?\n'
|
|
827
|
+
+ '4. Summarize what needs attention and recommend next actions.';
|
|
791
828
|
fs.writeFileSync(promptPath, userMessage, { mode: 0o644 });
|
|
792
829
|
}
|
|
793
830
|
else {
|
|
794
831
|
// Write full prompt (includes role context for non-Claude executors)
|
|
795
832
|
fs.writeFileSync(promptPath, prompt, { mode: 0o644 });
|
|
796
833
|
}
|
|
834
|
+
// Tool registry (TKT-083): generate MCP config for Claude Code
|
|
835
|
+
let mcpConfigPath = null;
|
|
836
|
+
if (context.hqPath && isClaudeExecutor(executor)) {
|
|
837
|
+
const toolsResult = resolveToolsForSpawn(context.hqPath, context.toolPolicy, baseDir);
|
|
838
|
+
mcpConfigPath = toolsResult.mcpConfigPath;
|
|
839
|
+
}
|
|
797
840
|
// Build the executor command using getExecutorCommand() output
|
|
798
841
|
// For Claude Code, we also support outputMode and additional flags
|
|
799
842
|
// For Codex, we use the codex adapter for deterministic command building (TKT-080)
|
|
@@ -811,7 +854,9 @@ export async function runHost(context, executor, config, displayMode = 'terminal
|
|
|
811
854
|
// TKT-053: Disable plan mode for background agents — prevents silent stalls
|
|
812
855
|
// when there's no user to approve the plan mode transition
|
|
813
856
|
const disallowPlanFlag = displayMode === 'background' ? '--disallowedTools EnterPlanMode ' : '';
|
|
814
|
-
|
|
857
|
+
// Tool registry (TKT-083): pass MCP config to Claude Code via --mcp-config flag
|
|
858
|
+
const mcpConfigFlag = mcpConfigPath ? `--mcp-config "${mcpConfigPath}" ` : '';
|
|
859
|
+
executorInvocation = `${cmd} ${permissionsFlag}${effortFlag}${printFlag}${disallowPlanFlag}${systemPromptFlag}${mcpConfigFlag}"$(cat "$PROMPT_PATH")"`;
|
|
815
860
|
}
|
|
816
861
|
else if (executor === 'codex') {
|
|
817
862
|
// TKT-080: Use Codex adapter for deterministic command building.
|
|
@@ -845,16 +890,25 @@ echo ""
|
|
|
845
890
|
echo "✅ Agent work complete. Press Enter to close or run more commands."
|
|
846
891
|
exec $SHELL
|
|
847
892
|
`;
|
|
893
|
+
// Wrap with srt sandbox if running in sandbox environment
|
|
894
|
+
let finalInvocation = executorInvocation;
|
|
895
|
+
if (context.executionEnvironment === 'sandbox') {
|
|
896
|
+
// Build the srt wrapper command
|
|
897
|
+
// The inner command is the executor invocation that reads from PROMPT_PATH
|
|
898
|
+
const srtCmd = buildSrtCommand(`bash -c '${executorInvocation.replace(/'/g, "'\\''")}'`, context, config);
|
|
899
|
+
finalInvocation = srtCmd;
|
|
900
|
+
}
|
|
848
901
|
const scriptContent = `#!/bin/bash
|
|
849
902
|
# Auto-generated script for ticket ${context.ticketId}
|
|
850
903
|
SCRIPT_PATH="${scriptPath}"
|
|
851
904
|
PROMPT_PATH="${promptPath}"${systemPromptVar}
|
|
852
905
|
${setTitleCmds}
|
|
853
906
|
echo "🚀 Starting: ${sessionName}"
|
|
907
|
+
${context.executionEnvironment === 'sandbox' ? 'echo "🔒 Running in srt sandbox (filesystem + network isolation)"' : ''}
|
|
854
908
|
echo ""
|
|
855
909
|
cd "${context.worktreePath}"
|
|
856
910
|
# Run executor in subshell with CLAUDECODE unset (prevents nested session error)
|
|
857
|
-
(unset CLAUDECODE CLAUDE_CODE_ENTRYPOINT; ${
|
|
911
|
+
(unset CLAUDECODE CLAUDE_CODE_ENTRYPOINT; ${finalInvocation})
|
|
858
912
|
|
|
859
913
|
# Clean up script and prompt files
|
|
860
914
|
rm -f "$SCRIPT_PATH" "$PROMPT_PATH"${systemPromptPath ? ' "$SYSTEM_PROMPT_PATH"' : ''}
|
|
@@ -1695,7 +1749,7 @@ function writePromptFile(context) {
|
|
|
1695
1749
|
* Uses docker exec for direct container access.
|
|
1696
1750
|
* Uses a prompt file to avoid shell escaping issues.
|
|
1697
1751
|
*/
|
|
1698
|
-
export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive', permissionMode = 'safe', displayMode = 'terminal') {
|
|
1752
|
+
export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive', permissionMode = 'safe', displayMode = 'terminal', mcpConfigFile) {
|
|
1699
1753
|
// Calculate the relative path from agentDir to worktreePath for cd
|
|
1700
1754
|
const relativePath = path.relative(context.agentDir, context.worktreePath);
|
|
1701
1755
|
const cdCmd = relativePath ? `cd /workspace/${relativePath} && ` : '';
|
|
@@ -1715,7 +1769,9 @@ export function buildDevcontainerCommand(context, executor, promptFile, containe
|
|
|
1715
1769
|
const effortFlag = '--effort high ';
|
|
1716
1770
|
// TKT-053: Disable plan mode for background agents — prevents silent stalls
|
|
1717
1771
|
const disallowPlanFlag = displayMode === 'background' ? '--disallowedTools EnterPlanMode ' : '';
|
|
1718
|
-
|
|
1772
|
+
// Tool registry (TKT-083): pass MCP config to Claude Code via --mcp-config flag
|
|
1773
|
+
const mcpConfigFlag = mcpConfigFile ? `--mcp-config ${mcpConfigFile} ` : '';
|
|
1774
|
+
executorCmd = `claude ${bypassTrustFlag}${permissionsFlag}${effortFlag}${printFlag}${disallowPlanFlag}${mcpConfigFlag}"$(cat ${promptFile})"`;
|
|
1719
1775
|
}
|
|
1720
1776
|
else if (executor === 'codex') {
|
|
1721
1777
|
// Use Codex adapter for mode validation and deterministic command building.
|
|
@@ -1806,6 +1862,16 @@ export async function runDevcontainer(context, executor, config, displayMode = '
|
|
|
1806
1862
|
}
|
|
1807
1863
|
// Write prompt to file in worktree (accessible by container)
|
|
1808
1864
|
const { hostPath: promptHostPath, containerPath: promptFile } = writePromptFile(context);
|
|
1865
|
+
// Tool registry (TKT-083): generate MCP config file for container
|
|
1866
|
+
let mcpConfigContainerPath;
|
|
1867
|
+
if (context.hqPath && isClaudeExecutor(executor)) {
|
|
1868
|
+
const toolsResult = resolveToolsForSpawn(context.hqPath, context.toolPolicy, context.worktreePath);
|
|
1869
|
+
if (toolsResult.mcpConfigPath) {
|
|
1870
|
+
// Map host path to container path
|
|
1871
|
+
const relativeMcp = path.relative(context.agentDir, toolsResult.mcpConfigPath);
|
|
1872
|
+
mcpConfigContainerPath = `/workspace/${relativeMcp}`;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1809
1875
|
// Inject fresh GitHub token into container (containers may be reused with stale/empty tokens)
|
|
1810
1876
|
// This ensures git push works even if the container was created before token was available
|
|
1811
1877
|
const githubToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
@@ -1822,7 +1888,7 @@ export async function runDevcontainer(context, executor, config, displayMode = '
|
|
|
1822
1888
|
}
|
|
1823
1889
|
// Build the docker exec command (just runs claude directly)
|
|
1824
1890
|
// tmux session setup is handled by runDevcontainerInTmux, not buildDevcontainerCommand
|
|
1825
|
-
const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.permissionMode, displayMode);
|
|
1891
|
+
const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.permissionMode, displayMode, mcpConfigContainerPath);
|
|
1826
1892
|
// Execute based on display mode
|
|
1827
1893
|
// When sessionManager is 'tmux', always use tmux inside container for session persistence
|
|
1828
1894
|
// (allows reattach via `prlt session attach` even for background mode)
|
|
@@ -2849,19 +2915,125 @@ exec $SHELL
|
|
|
2849
2915
|
}
|
|
2850
2916
|
}
|
|
2851
2917
|
// =============================================================================
|
|
2852
|
-
//
|
|
2918
|
+
// Sandbox Utilities
|
|
2919
|
+
// =============================================================================
|
|
2920
|
+
/**
|
|
2921
|
+
* Check if srt (sandbox-runtime) is installed on the host.
|
|
2922
|
+
*/
|
|
2923
|
+
export function isSrtInstalled() {
|
|
2924
|
+
try {
|
|
2925
|
+
execSync('which srt', { stdio: 'pipe' });
|
|
2926
|
+
return true;
|
|
2927
|
+
}
|
|
2928
|
+
catch {
|
|
2929
|
+
return false;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Build the srt command with filesystem and network restrictions.
|
|
2934
|
+
*
|
|
2935
|
+
* Filesystem policy (read-restriction philosophy from claude-code-sandbox):
|
|
2936
|
+
* - Read/write: agent worktree directory
|
|
2937
|
+
* - Read-only: repo source (if different from worktree)
|
|
2938
|
+
* - Read-only: additional configured read paths
|
|
2939
|
+
* - Deny: home directory, system paths, other repos
|
|
2940
|
+
*
|
|
2941
|
+
* Network policy:
|
|
2942
|
+
* - Allow: configured domains (GitHub, Anthropic API, npm registries, etc.)
|
|
2943
|
+
* - Deny: everything else
|
|
2944
|
+
*/
|
|
2945
|
+
export function buildSrtCommand(innerCommand, context, config) {
|
|
2946
|
+
const args = ['srt'];
|
|
2947
|
+
// Filesystem: always allow read/write to agent worktree
|
|
2948
|
+
args.push(`--fs-write=${context.worktreePath}`);
|
|
2949
|
+
// Allow read/write to the agent directory (parent of worktree, contains .devcontainer etc.)
|
|
2950
|
+
if (context.agentDir && context.agentDir !== context.worktreePath) {
|
|
2951
|
+
args.push(`--fs-write=${context.agentDir}`);
|
|
2952
|
+
}
|
|
2953
|
+
// Allow read/write to HQ scripts directory (for temp script files)
|
|
2954
|
+
if (context.hqPath) {
|
|
2955
|
+
const scriptsDir = path.join(context.hqPath, '.proletariat', 'scripts');
|
|
2956
|
+
args.push(`--fs-write=${scriptsDir}`);
|
|
2957
|
+
}
|
|
2958
|
+
// Allow read access to additional configured paths
|
|
2959
|
+
for (const readPath of config.sandbox.allowReadPaths) {
|
|
2960
|
+
args.push(`--fs-read=${readPath}`);
|
|
2961
|
+
}
|
|
2962
|
+
// Allow write access to additional configured paths
|
|
2963
|
+
for (const writePath of config.sandbox.allowWritePaths) {
|
|
2964
|
+
args.push(`--fs-write=${writePath}`);
|
|
2965
|
+
}
|
|
2966
|
+
// Allow read to temp directory (needed for script execution)
|
|
2967
|
+
args.push(`--fs-write=${os.tmpdir()}`);
|
|
2968
|
+
// Network: merge sandbox domains with firewall allowlist
|
|
2969
|
+
const allDomains = new Set([
|
|
2970
|
+
...config.sandbox.networkDomains,
|
|
2971
|
+
...config.firewall.allowlistDomains,
|
|
2972
|
+
]);
|
|
2973
|
+
for (const domain of allDomains) {
|
|
2974
|
+
args.push(`--net-allow=${domain}`);
|
|
2975
|
+
}
|
|
2976
|
+
// The inner command to execute inside the sandbox
|
|
2977
|
+
args.push('--');
|
|
2978
|
+
args.push(innerCommand);
|
|
2979
|
+
return args.join(' ');
|
|
2980
|
+
}
|
|
2981
|
+
// =============================================================================
|
|
2982
|
+
// Sandbox Runner - srt-based sandbox on host
|
|
2983
|
+
// =============================================================================
|
|
2984
|
+
/**
|
|
2985
|
+
* Run command in an srt sandbox on the host machine.
|
|
2986
|
+
* Uses the same tmux session approach as the host runner, but wraps the
|
|
2987
|
+
* executor command with srt for filesystem and network isolation.
|
|
2988
|
+
*
|
|
2989
|
+
* Falls back to host runner with warning if srt is not installed.
|
|
2990
|
+
*/
|
|
2991
|
+
export async function runSandbox(context, executor, config, displayMode = 'terminal') {
|
|
2992
|
+
// Check if srt is installed
|
|
2993
|
+
if (!isSrtInstalled()) {
|
|
2994
|
+
if (config.sandbox.fallbackToHost) {
|
|
2995
|
+
// Log warning via stderr (will be visible in terminal)
|
|
2996
|
+
process.stderr.write('\x1b[33m⚠️ srt (sandbox-runtime) not installed. Falling back to host execution.\n' +
|
|
2997
|
+
' Install srt for filesystem + network isolation: https://github.com/anthropic-experimental/sandbox-runtime\x1b[0m\n');
|
|
2998
|
+
// Fall back to host runner
|
|
2999
|
+
return runHost(context, executor, config, displayMode);
|
|
3000
|
+
}
|
|
3001
|
+
return {
|
|
3002
|
+
success: false,
|
|
3003
|
+
error: 'srt (sandbox-runtime) is not installed.\n\n' +
|
|
3004
|
+
'Install it from: https://github.com/anthropic-experimental/sandbox-runtime\n' +
|
|
3005
|
+
'Or set sandbox.fallbackToHost to true in execution config to fall back to host.',
|
|
3006
|
+
};
|
|
3007
|
+
}
|
|
3008
|
+
// Delegate to host runner — the sandbox wrapping happens at the script level
|
|
3009
|
+
// We set a flag on context so the host runner knows to wrap with srt
|
|
3010
|
+
const sandboxContext = {
|
|
3011
|
+
...context,
|
|
3012
|
+
executionEnvironment: 'sandbox',
|
|
3013
|
+
};
|
|
3014
|
+
return runHost(sandboxContext, executor, config, displayMode);
|
|
3015
|
+
}
|
|
3016
|
+
// =============================================================================
|
|
3017
|
+
// Cloud Runner (was VM Runner)
|
|
2853
3018
|
// =============================================================================
|
|
2854
|
-
|
|
2855
|
-
|
|
3019
|
+
/**
|
|
3020
|
+
* Run command on a remote machine (cloud) via SSH.
|
|
3021
|
+
* Formerly 'runVm' — renamed to reflect the simplified environment hierarchy.
|
|
3022
|
+
* Uses cloud config with fallback to legacy vm config for backwards compatibility.
|
|
3023
|
+
*/
|
|
3024
|
+
export async function runCloud(context, executor, config, host) {
|
|
3025
|
+
// Use cloud config, fall back to vm config for backwards compatibility
|
|
3026
|
+
const cloudConfig = config.cloud?.defaultHost ? config.cloud : config.vm;
|
|
3027
|
+
const targetHost = host || cloudConfig.defaultHost;
|
|
2856
3028
|
if (!targetHost) {
|
|
2857
3029
|
return {
|
|
2858
3030
|
success: false,
|
|
2859
|
-
error: 'No
|
|
3031
|
+
error: 'No cloud host specified. Use --host or configure execution.cloud.default_host',
|
|
2860
3032
|
};
|
|
2861
3033
|
}
|
|
2862
3034
|
const prompt = buildPrompt(context);
|
|
2863
|
-
const user =
|
|
2864
|
-
const keyPath =
|
|
3035
|
+
const user = cloudConfig.user;
|
|
3036
|
+
const keyPath = cloudConfig.keyPath;
|
|
2865
3037
|
const remoteWorkspace = `/workspace/${context.agentName}`;
|
|
2866
3038
|
try {
|
|
2867
3039
|
// Build SSH options
|
|
@@ -2870,7 +3042,7 @@ export async function runVm(context, executor, config, host) {
|
|
|
2870
3042
|
sshOpts = `-i "${keyPath}"`;
|
|
2871
3043
|
}
|
|
2872
3044
|
// Sync worktree to remote
|
|
2873
|
-
if (
|
|
3045
|
+
if (cloudConfig.syncMethod === 'rsync') {
|
|
2874
3046
|
let rsyncCmd = `rsync -avz`;
|
|
2875
3047
|
if (keyPath) {
|
|
2876
3048
|
rsyncCmd += ` -e "ssh -i ${keyPath}"`;
|
|
@@ -2884,7 +3056,7 @@ export async function runVm(context, executor, config, host) {
|
|
|
2884
3056
|
const gitPullCmd = `cd ${remoteWorkspace} && git fetch && git checkout ${context.branch}`;
|
|
2885
3057
|
execSync(`ssh ${sshOpts} ${user}@${targetHost} "${gitPullCmd}"`, { stdio: 'pipe' });
|
|
2886
3058
|
}
|
|
2887
|
-
// Validate Codex mode:
|
|
3059
|
+
// Validate Codex mode: Cloud runner is always non-tty (SSH + nohup)
|
|
2888
3060
|
if (executor === 'codex') {
|
|
2889
3061
|
const codexPermission = config.permissionMode;
|
|
2890
3062
|
const modeError = validateCodexMode(codexPermission, 'non-tty');
|
|
@@ -2916,29 +3088,38 @@ export async function runVm(context, executor, config, host) {
|
|
|
2916
3088
|
catch (error) {
|
|
2917
3089
|
return {
|
|
2918
3090
|
success: false,
|
|
2919
|
-
error: error instanceof Error ? error.message : 'Failed to execute on
|
|
3091
|
+
error: error instanceof Error ? error.message : 'Failed to execute on cloud',
|
|
2920
3092
|
};
|
|
2921
3093
|
}
|
|
2922
3094
|
}
|
|
3095
|
+
/** @deprecated Use runCloud instead */
|
|
3096
|
+
export const runVm = runCloud;
|
|
2923
3097
|
// =============================================================================
|
|
2924
3098
|
// Runner Dispatcher
|
|
2925
3099
|
// =============================================================================
|
|
2926
3100
|
export async function runExecution(environment, context, executor, config = DEFAULT_EXECUTION_CONFIG, options) {
|
|
2927
|
-
// Ensure
|
|
3101
|
+
// Ensure context knows its execution environment
|
|
3102
|
+
if (!context.executionEnvironment) {
|
|
3103
|
+
context.executionEnvironment = environment;
|
|
3104
|
+
}
|
|
3105
|
+
// Normalize environment (maps 'vm' -> 'cloud')
|
|
3106
|
+
const normalizedEnv = normalizeEnvironment(environment);
|
|
3107
|
+
// Ensure tmux server has keychain access for OAuth (host/sandbox only)
|
|
2928
3108
|
// Docker uses claude-credentials volume, devcontainer runs inside container
|
|
2929
|
-
if (
|
|
3109
|
+
if (normalizedEnv === 'host' || normalizedEnv === 'sandbox') {
|
|
2930
3110
|
await ensureTmuxServerHasKeychainAccess();
|
|
2931
3111
|
}
|
|
2932
|
-
switch (
|
|
3112
|
+
switch (normalizedEnv) {
|
|
2933
3113
|
case 'devcontainer':
|
|
2934
3114
|
return runDevcontainer(context, executor, config, options?.displayMode, options?.sessionManager);
|
|
2935
3115
|
case 'host':
|
|
2936
|
-
// Host uses tmux for session persistence (same as devcontainer)
|
|
2937
3116
|
return runHost(context, executor, config, options?.displayMode);
|
|
3117
|
+
case 'sandbox':
|
|
3118
|
+
return runSandbox(context, executor, config, options?.displayMode);
|
|
2938
3119
|
case 'docker':
|
|
2939
3120
|
return runDocker(context, executor, config);
|
|
2940
|
-
case '
|
|
2941
|
-
return
|
|
3121
|
+
case 'cloud':
|
|
3122
|
+
return runCloud(context, executor, config, options?.host);
|
|
2942
3123
|
default:
|
|
2943
3124
|
return { success: false, error: `Unknown execution environment: ${environment}` };
|
|
2944
3125
|
}
|