@os-eco/overstory-cli 0.7.4 → 0.7.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@os-eco/overstory-cli",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Multi-agent orchestration for AI coding agents — spawn workers in git worktrees via tmux, coordinate through SQLite mail, merge with tiered conflict resolution. Pluggable runtime adapters for Claude Code, Pi, and more.",
5
5
  "author": "Jaymin West",
6
6
  "license": "MIT",
@@ -540,7 +540,9 @@ describe("startCoordinator", () => {
540
540
  expect(calls.createSession).toHaveLength(1);
541
541
  const cmd = calls.createSession[0]?.command ?? "";
542
542
  expect(cmd).toContain("--append-system-prompt");
543
- expect(cmd).toContain("# Coordinator Agent");
543
+ // File path is passed via $(cat ...) instead of inlining content (overstory#45)
544
+ expect(cmd).toContain("$(cat '");
545
+ expect(cmd).toContain("agent-defs/coordinator.md");
544
546
  });
545
547
 
546
548
  test("reads model from manifest instead of hardcoding", async () => {
@@ -363,17 +363,20 @@ async function startCoordinator(
363
363
  // Inject the coordinator base definition via --append-system-prompt so the
364
364
  // coordinator knows its role, hierarchy rules, and delegation patterns
365
365
  // (overstory-gaio, overstory-0kwf).
366
+ // Pass the file path (not content) so the shell inside the tmux pane reads
367
+ // it via $(cat ...) — avoids tmux IPC "command too long" errors with large
368
+ // agent definitions (overstory#45).
366
369
  const agentDefPath = join(projectRoot, ".overstory", "agent-defs", "coordinator.md");
367
370
  const agentDefFile = Bun.file(agentDefPath);
368
- let appendSystemPrompt: string | undefined;
371
+ let appendSystemPromptFile: string | undefined;
369
372
  if (await agentDefFile.exists()) {
370
- appendSystemPrompt = await agentDefFile.text();
373
+ appendSystemPromptFile = agentDefPath;
371
374
  }
372
375
  const spawnCmd = runtime.buildSpawnCommand({
373
376
  model: resolvedModel.model,
374
377
  permissionMode: "bypass",
375
378
  cwd: projectRoot,
376
- appendSystemPrompt,
379
+ appendSystemPromptFile,
377
380
  env: {
378
381
  ...runtime.buildEnv(resolvedModel),
379
382
  OVERSTORY_AGENT_NAME: COORDINATOR_NAME,
@@ -142,17 +142,18 @@ async function startMonitor(opts: { json: boolean; attach: boolean }): Promise<v
142
142
  }
143
143
 
144
144
  // Spawn tmux session at project root with Claude Code (interactive mode).
145
+ // Pass file path (not content) to avoid tmux "command too long" (overstory#45).
145
146
  const agentDefPath = join(projectRoot, ".overstory", "agent-defs", "monitor.md");
146
147
  const agentDefFile = Bun.file(agentDefPath);
147
- let appendSystemPrompt: string | undefined;
148
+ let appendSystemPromptFile: string | undefined;
148
149
  if (await agentDefFile.exists()) {
149
- appendSystemPrompt = await agentDefFile.text();
150
+ appendSystemPromptFile = agentDefPath;
150
151
  }
151
152
  const spawnCmd = runtime.buildSpawnCommand({
152
153
  model: resolvedModel.model,
153
154
  permissionMode: "bypass",
154
155
  cwd: projectRoot,
155
- appendSystemPrompt,
156
+ appendSystemPromptFile,
156
157
  env: {
157
158
  ...runtime.buildEnv(resolvedModel),
158
159
  OVERSTORY_AGENT_NAME: MONITOR_NAME,
@@ -169,18 +169,19 @@ async function startSupervisor(opts: {
169
169
 
170
170
  // Spawn tmux session at project root with Claude Code (interactive mode).
171
171
  // Inject the supervisor base definition via --append-system-prompt.
172
+ // Pass file path (not content) to avoid tmux "command too long" (overstory#45).
172
173
  const tmuxSession = `overstory-${config.project.name}-supervisor-${opts.name}`;
173
174
  const agentDefPath = join(projectRoot, ".overstory", "agent-defs", "supervisor.md");
174
175
  const agentDefFile = Bun.file(agentDefPath);
175
- let appendSystemPrompt: string | undefined;
176
+ let appendSystemPromptFile: string | undefined;
176
177
  if (await agentDefFile.exists()) {
177
- appendSystemPrompt = await agentDefFile.text();
178
+ appendSystemPromptFile = agentDefPath;
178
179
  }
179
180
  const spawnCmd = runtime.buildSpawnCommand({
180
181
  model: resolvedModel.model,
181
182
  permissionMode: "bypass",
182
183
  cwd: projectRoot,
183
- appendSystemPrompt,
184
+ appendSystemPromptFile,
184
185
  env: {
185
186
  ...runtime.buildEnv(resolvedModel),
186
187
  OVERSTORY_AGENT_NAME: opts.name,
package/src/index.ts CHANGED
@@ -45,7 +45,7 @@ import { OverstoryError, WorktreeError } from "./errors.ts";
45
45
  import { jsonError } from "./json.ts";
46
46
  import { brand, chalk, muted, setQuiet } from "./logging/color.ts";
47
47
 
48
- export const VERSION = "0.7.4";
48
+ export const VERSION = "0.7.5";
49
49
 
50
50
  const rawArgs = process.argv.slice(2);
51
51
 
@@ -73,6 +73,46 @@ describe("ClaudeRuntime", () => {
73
73
  );
74
74
  });
75
75
 
76
+ test("with appendSystemPromptFile uses $(cat ...) expansion", () => {
77
+ const opts: SpawnOpts = {
78
+ model: "opus",
79
+ permissionMode: "bypass",
80
+ cwd: "/project",
81
+ env: {},
82
+ appendSystemPromptFile: "/project/.overstory/agent-defs/coordinator.md",
83
+ };
84
+ const cmd = runtime.buildSpawnCommand(opts);
85
+ expect(cmd).toBe(
86
+ `claude --model opus --permission-mode bypassPermissions --append-system-prompt "$(cat '/project/.overstory/agent-defs/coordinator.md')"`,
87
+ );
88
+ });
89
+
90
+ test("appendSystemPromptFile with single quotes in path", () => {
91
+ const opts: SpawnOpts = {
92
+ model: "opus",
93
+ permissionMode: "bypass",
94
+ cwd: "/project",
95
+ env: {},
96
+ appendSystemPromptFile: "/project/it's a path/agent.md",
97
+ };
98
+ const cmd = runtime.buildSpawnCommand(opts);
99
+ expect(cmd).toContain("$(cat '/project/it'\\''s a path/agent.md')");
100
+ });
101
+
102
+ test("appendSystemPromptFile takes precedence over appendSystemPrompt", () => {
103
+ const opts: SpawnOpts = {
104
+ model: "opus",
105
+ permissionMode: "bypass",
106
+ cwd: "/project",
107
+ env: {},
108
+ appendSystemPromptFile: "/project/.overstory/agent-defs/coordinator.md",
109
+ appendSystemPrompt: "This inline content should be ignored",
110
+ };
111
+ const cmd = runtime.buildSpawnCommand(opts);
112
+ expect(cmd).toContain("$(cat ");
113
+ expect(cmd).not.toContain("This inline content should be ignored");
114
+ });
115
+
76
116
  test("without appendSystemPrompt omits the flag", () => {
77
117
  const opts: SpawnOpts = {
78
118
  model: "haiku",
@@ -54,7 +54,14 @@ export class ClaudeRuntime implements AgentRuntime {
54
54
  const permMode = opts.permissionMode === "bypass" ? "bypassPermissions" : "default";
55
55
  let cmd = `claude --model ${opts.model} --permission-mode ${permMode}`;
56
56
 
57
- if (opts.appendSystemPrompt) {
57
+ if (opts.appendSystemPromptFile) {
58
+ // Read from file at shell expansion time — avoids tmux IPC message size
59
+ // limits (~8-16KB) that cause "command too long" errors when large agent
60
+ // definitions are inlined. The $(cat ...) expands inside the tmux pane's
61
+ // shell, so the tmux IPC message only carries the short command string.
62
+ const escaped = opts.appendSystemPromptFile.replace(/'/g, "'\\''");
63
+ cmd += ` --append-system-prompt "$(cat '${escaped}')"`;
64
+ } else if (opts.appendSystemPrompt) {
58
65
  // Single-quote the content for safe shell expansion.
59
66
  // POSIX single-quoted strings cannot contain single quotes, so escape
60
67
  // them using the standard technique: end quote, escaped quote, start quote.
@@ -126,6 +126,34 @@ describe("PiRuntime", () => {
126
126
  );
127
127
  });
128
128
 
129
+ test("with appendSystemPromptFile uses $(cat ...) expansion", () => {
130
+ const opts: SpawnOpts = {
131
+ model: "opus",
132
+ permissionMode: "bypass",
133
+ cwd: "/project",
134
+ env: {},
135
+ appendSystemPromptFile: "/project/.overstory/agent-defs/coordinator.md",
136
+ };
137
+ const cmd = runtime.buildSpawnCommand(opts);
138
+ expect(cmd).toBe(
139
+ `pi --model anthropic/claude-opus-4-6 --append-system-prompt "$(cat '/project/.overstory/agent-defs/coordinator.md')"`,
140
+ );
141
+ });
142
+
143
+ test("appendSystemPromptFile takes precedence over appendSystemPrompt", () => {
144
+ const opts: SpawnOpts = {
145
+ model: "opus",
146
+ permissionMode: "bypass",
147
+ cwd: "/project",
148
+ env: {},
149
+ appendSystemPromptFile: "/project/.overstory/agent-defs/coordinator.md",
150
+ appendSystemPrompt: "This inline content should be ignored",
151
+ };
152
+ const cmd = runtime.buildSpawnCommand(opts);
153
+ expect(cmd).toContain("$(cat ");
154
+ expect(cmd).not.toContain("This inline content should be ignored");
155
+ });
156
+
129
157
  test("without appendSystemPrompt omits the flag", () => {
130
158
  const opts: SpawnOpts = {
131
159
  model: "haiku",
@@ -75,7 +75,11 @@ export class PiRuntime implements AgentRuntime {
75
75
  buildSpawnCommand(opts: SpawnOpts): string {
76
76
  let cmd = `pi --model ${this.expandModel(opts.model)}`;
77
77
 
78
- if (opts.appendSystemPrompt) {
78
+ if (opts.appendSystemPromptFile) {
79
+ // Read from file at shell expansion time — avoids tmux command length limits.
80
+ const escaped = opts.appendSystemPromptFile.replace(/'/g, "'\\''");
81
+ cmd += ` --append-system-prompt "$(cat '${escaped}')"`;
82
+ } else if (opts.appendSystemPrompt) {
79
83
  // POSIX single-quote escape: end quote, backslash-quote, start quote.
80
84
  const escaped = opts.appendSystemPrompt.replace(/'/g, "'\\''");
81
85
  cmd += ` --append-system-prompt '${escaped}'`;
@@ -15,6 +15,8 @@ export interface SpawnOpts {
15
15
  systemPrompt?: string;
16
16
  /** Optional system prompt suffix appended after the base instructions. */
17
17
  appendSystemPrompt?: string;
18
+ /** Path to a file whose contents are appended as system prompt (avoids tmux command length limits). */
19
+ appendSystemPromptFile?: string;
18
20
  /** Working directory for the spawned process. */
19
21
  cwd: string;
20
22
  /** Additional environment variables to pass to the spawned process. */