@oxgeneral/orch 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/App-A5TZETZ2.js +21 -0
  2. package/dist/agent-Q34L27AY.js +9 -0
  3. package/dist/agent-shop-D2RS4BZK.js +2 -0
  4. package/dist/chunk-4MMHVHA6.js +112 -0
  5. package/dist/chunk-64WUDYEM.js +6 -0
  6. package/dist/chunk-6MJ7V6VY.js +2 -0
  7. package/dist/{chunk-DAVHOWGD.js → chunk-BCPUTULS.js} +16 -188
  8. package/dist/{chunk-UIJYU3J7.js → chunk-BSJVYRI3.js} +5 -2
  9. package/dist/chunk-BSJVYRI3.js.map +1 -0
  10. package/dist/chunk-CDFA4IIQ.js +2 -0
  11. package/dist/chunk-CHRW4CLD.js +2 -0
  12. package/dist/{chunk-O2OQCSBL.js → chunk-FDSFFFMT.js} +46 -29
  13. package/dist/chunk-FDSFFFMT.js.map +1 -0
  14. package/dist/chunk-HMMPM7MF.js +3 -0
  15. package/dist/chunk-HXOMNULD.js +2 -0
  16. package/dist/chunk-IKNBPOQL.js +2 -0
  17. package/dist/chunk-KFQTWMZI.js +3 -0
  18. package/dist/chunk-LXNRCJ22.js +2 -0
  19. package/dist/{chunk-C6XZ3FJT.js → chunk-MOWC2CHX.js} +7 -4
  20. package/dist/chunk-MOWC2CHX.js.map +1 -0
  21. package/dist/chunk-P4JTJBWO.js +2 -0
  22. package/dist/chunk-PJ5DKXGR.js +2 -0
  23. package/dist/chunk-QNZ6D63E.js +4 -0
  24. package/dist/chunk-RHFRHCN5.js +16 -0
  25. package/dist/chunk-RHFRHCN5.js.map +1 -0
  26. package/dist/chunk-TPTHLFOB.js +2 -0
  27. package/dist/chunk-UMZEA3JT.js +5 -0
  28. package/dist/chunk-X54D5JZG.js +11 -0
  29. package/dist/{claude-WUJU5KIE.js → claude-4R6L6BWY.js} +4 -4
  30. package/dist/{claude-WUJU5KIE.js.map → claude-4R6L6BWY.js.map} +1 -1
  31. package/dist/claude-ZLVOLRUG.js +2 -0
  32. package/dist/cli.js +1 -199
  33. package/dist/clipboard-service-WVON5ZN4.js +25 -0
  34. package/dist/{codex-NYJWEPRQ.js → codex-IMOW5ZUZ.js} +4 -4
  35. package/dist/{codex-NYJWEPRQ.js.map → codex-IMOW5ZUZ.js.map} +1 -1
  36. package/dist/codex-YRSODLH4.js +2 -0
  37. package/dist/config-OOB7QEKM.js +2 -0
  38. package/dist/container-CFMUT5KS.js +4 -0
  39. package/dist/context-FXRERFSP.js +7 -0
  40. package/dist/cursor-2HILMNM3.js +2 -0
  41. package/dist/{cursor-3YHVD4NP.js → cursor-NHV7X3WG.js} +4 -4
  42. package/dist/{cursor-3YHVD4NP.js.map → cursor-NHV7X3WG.js.map} +1 -1
  43. package/dist/doctor-GKTV4YYD.js +2 -0
  44. package/dist/doctor-service-PB7YBH3F.js +2 -0
  45. package/dist/editor-7IFRWVTL.js +8 -0
  46. package/dist/goal-RNNZYMNR.js +8 -0
  47. package/dist/index.d.ts +16 -14
  48. package/dist/index.js +247 -75
  49. package/dist/index.js.map +1 -1
  50. package/dist/init-56QC5QVX.js +53 -0
  51. package/dist/logs-4ISTWUWV.js +12 -0
  52. package/dist/msg-4ELI7Q52.js +9 -0
  53. package/dist/opencode-3S4VDXRG.js +2 -0
  54. package/dist/{opencode-FAMPSA6X.js → opencode-ULT6DYCT.js} +6 -5
  55. package/dist/opencode-ULT6DYCT.js.map +1 -0
  56. package/dist/orchestrator-6XH2TY3T.js +6 -0
  57. package/dist/{orchestrator-X2CWGFCL.js.map → orchestrator-6XH2TY3T.js.map} +1 -1
  58. package/dist/orchestrator-DRITPUWT.js +13 -0
  59. package/dist/org-WAK3CDPG.js +3 -0
  60. package/dist/process-manager-33H27MQF.js +2 -0
  61. package/dist/registry-BO2PPRNG.js +2 -0
  62. package/dist/run-NE5E4JPW.js +3 -0
  63. package/dist/shell-OBHIVC2H.js +2 -0
  64. package/dist/{shell-NJNW3O6K.js → shell-UXEJRK3D.js} +4 -4
  65. package/dist/{shell-NJNW3O6K.js.map → shell-UXEJRK3D.js.map} +1 -1
  66. package/dist/shop-picker-LE3SKFOX.js +5 -0
  67. package/dist/status-NYHZ7Q5G.js +2 -0
  68. package/dist/task-JUS5OZJR.js +20 -0
  69. package/dist/team-VCJSUDWX.js +4 -0
  70. package/dist/template-engine-42PKL5KD.js +2 -0
  71. package/dist/tui-R6LXKKSH.js +2 -0
  72. package/dist/update-FFKCOV63.js +2 -0
  73. package/dist/update-check-HGMBDYHL.js +2 -0
  74. package/dist/{workspace-manager-EVD67GCG.js → workspace-manager-ABXFBL2A.js} +3 -3
  75. package/dist/{workspace-manager-EVD67GCG.js.map → workspace-manager-ABXFBL2A.js.map} +1 -1
  76. package/dist/workspace-manager-Q6WWXSTR.js +3 -0
  77. package/package.json +1 -1
  78. package/readme.md +5 -5
  79. package/dist/App-GJVTVGRU.js +0 -6717
  80. package/dist/agent-7ZJ3ZDJ7.js +0 -183
  81. package/dist/agent-shop-YN2BSLHM.js +0 -2
  82. package/dist/chunk-2C2TFQ7K.js +0 -136
  83. package/dist/chunk-45K2XID7.js +0 -29
  84. package/dist/chunk-4IFIOMCW.js +0 -86
  85. package/dist/chunk-7X2GI5OV.js +0 -181
  86. package/dist/chunk-C6XZ3FJT.js.map +0 -1
  87. package/dist/chunk-CHIP7O6V.js +0 -83
  88. package/dist/chunk-FRTKB575.js +0 -87
  89. package/dist/chunk-HXYAZGLP.js +0 -15
  90. package/dist/chunk-I3SMISEF.js +0 -29
  91. package/dist/chunk-K6DMQERQ.js +0 -89
  92. package/dist/chunk-LV6GDBBI.js +0 -297
  93. package/dist/chunk-MGGSRXWJ.js +0 -69
  94. package/dist/chunk-O2OQCSBL.js.map +0 -1
  95. package/dist/chunk-P6ATSXGL.js +0 -107
  96. package/dist/chunk-PNE6LQRF.js +0 -5
  97. package/dist/chunk-U2VDNUZL.js +0 -52
  98. package/dist/chunk-UIJYU3J7.js.map +0 -1
  99. package/dist/chunk-VXS2CJFH.js +0 -273
  100. package/dist/chunk-XDVMX2FO.js +0 -8
  101. package/dist/chunk-XDVMX2FO.js.map +0 -1
  102. package/dist/chunk-XJTJ2TJV.js +0 -221
  103. package/dist/claude-ZUEKJJ4X.js +0 -5
  104. package/dist/clipboard-service-RTDUUQQU.js +0 -200
  105. package/dist/codex-7IXXXG5U.js +0 -123
  106. package/dist/config-OTAVSMOD.js +0 -75
  107. package/dist/container-RY54L3XC.js +0 -1596
  108. package/dist/context-OL4BVUV5.js +0 -83
  109. package/dist/cursor-622RBRHH.js +0 -97
  110. package/dist/doctor-XSGQSD57.js +0 -67
  111. package/dist/doctor-service-TPOMFAIG.js +0 -2
  112. package/dist/goal-FMYYN2FR.js +0 -138
  113. package/dist/init-45BEMVL6.js +0 -199
  114. package/dist/logs-PHPYWQ6I.js +0 -207
  115. package/dist/msg-FUWWLEKM.js +0 -95
  116. package/dist/opencode-FAMPSA6X.js.map +0 -1
  117. package/dist/opencode-WOR53TSC.js +0 -98
  118. package/dist/orchestrator-O6MFMATT.js +0 -1448
  119. package/dist/orchestrator-X2CWGFCL.js +0 -5
  120. package/dist/process-manager-HUVNAPQV.js +0 -2
  121. package/dist/registry-PQWRVNF2.js +0 -2
  122. package/dist/run-N72G5V2H.js +0 -95
  123. package/dist/shell-DVFHHYAZ.js +0 -5
  124. package/dist/shop-picker-2HY67UWP.js +0 -79
  125. package/dist/status-RZWN2C6C.js +0 -56
  126. package/dist/task-5EL2RNGW.js +0 -221
  127. package/dist/team-PFLP4PPL.js +0 -97
  128. package/dist/template-engine-AWIS56BL.js +0 -3
  129. package/dist/tui-LN5XHSQY.js +0 -245
  130. package/dist/update-YLP7FPNY.js +0 -64
  131. package/dist/update-check-4YKLGBFB.js +0 -2
  132. package/dist/workspace-manager-JM6U7JOH.js +0 -215
@@ -1,245 +0,0 @@
1
- #!/usr/bin/env node
2
- // src/cli/commands/tui.ts
3
- function registerTuiCommand(program, container) {
4
- program.command("tui").description("Launch interactive TUI dashboard").action(async () => {
5
- await container.paths.requireInit();
6
- const tasks = await container.taskService.list();
7
- const agents = await container.agentService.list();
8
- const state = await container.stateStore.read();
9
- const { render } = await import('ink');
10
- const { createElement } = await import('react');
11
- const { App } = await import('./App-GJVTVGRU.js');
12
- const onRunTask = async (taskId) => {
13
- await container.orchestrator.runTask(taskId);
14
- };
15
- const onCreateTask = async (title, opts) => {
16
- return container.taskService.create({
17
- title,
18
- priority: opts?.priority,
19
- description: opts?.description,
20
- attachments: opts?.attachments
21
- });
22
- };
23
- const onCancelTask = async (taskId) => {
24
- await container.orchestrator.cancelTask(taskId);
25
- };
26
- const onRetryTask = async (taskId) => {
27
- await container.taskService.retry(taskId);
28
- };
29
- const onAssignTask = async (taskId, agentId) => {
30
- await container.taskService.assign(taskId, agentId);
31
- };
32
- const onRunAll = async () => {
33
- await container.orchestrator.runAll();
34
- };
35
- const onDisableAgent = async (agentId) => {
36
- await container.agentService.disable(agentId);
37
- };
38
- const onEnableAgent = async (agentId) => {
39
- await container.agentService.enable(agentId);
40
- };
41
- const onSubscribeEvents = (handler) => {
42
- return container.eventBus.onAny(handler);
43
- };
44
- const onRefreshTasks = async () => {
45
- return container.taskService.list();
46
- };
47
- const onRefreshAgents = async () => {
48
- return container.agentService.list();
49
- };
50
- const onRefreshState = async () => {
51
- return container.stateStore.read();
52
- };
53
- const onAddAgent = async (name, adapter, opts) => {
54
- return container.agentService.create({
55
- name,
56
- adapter: adapter ?? "claude",
57
- model: opts?.model || void 0,
58
- role: opts?.role || void 0,
59
- approval_policy: opts?.approval_policy || void 0,
60
- skills: opts?.skills || void 0
61
- });
62
- };
63
- const onDeleteAgent = async (agentId) => {
64
- await container.agentService.remove(agentId);
65
- };
66
- const onDeleteTask = async (taskId) => {
67
- await container.taskService.delete(taskId);
68
- };
69
- const onApproveTask = async (taskId) => {
70
- await container.taskService.updateStatus(taskId, "done");
71
- };
72
- const onRejectTask = async (taskId, feedback) => {
73
- await container.taskService.reject(taskId, feedback);
74
- };
75
- const onUpdateTask = async (taskId, fields) => {
76
- return container.taskService.update(taskId, fields);
77
- };
78
- const onUpdateAgent = async (agentId, fields) => {
79
- return container.agentService.update(agentId, {
80
- ...fields,
81
- approval_policy: fields.approval_policy
82
- });
83
- };
84
- const onForceStopAgent = async (agentId) => {
85
- await container.orchestrator.forceStopAgent(agentId);
86
- };
87
- const onToggleAutonomous = async (agentId, enabled) => {
88
- return container.agentService.setAutonomous(agentId, enabled);
89
- };
90
- const onLoadHistory = async (onBatch) => {
91
- const allRuns = await container.runService.listAll();
92
- const FIRST_BATCH = 3;
93
- const TOTAL_RUNS = 10;
94
- const firstRuns = allRuns.slice(0, FIRST_BATCH);
95
- const restRuns = allRuns.slice(FIRST_BATCH, TOTAL_RUNS);
96
- const loadRunEvents = async (run) => {
97
- const events = await container.runService.readEventsTail(run.id, 30);
98
- return events.map((evt) => ({
99
- timestamp: evt.timestamp,
100
- agentId: run.agent_id,
101
- taskId: run.task_id,
102
- type: evt.type,
103
- data: evt.data
104
- }));
105
- };
106
- if (firstRuns.length > 0) {
107
- const firstEntries = (await Promise.all(firstRuns.map(loadRunEvents))).flat();
108
- firstEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
109
- onBatch(firstEntries.slice(-200));
110
- }
111
- if (restRuns.length > 0) {
112
- const restEntries = (await Promise.all(restRuns.map(loadRunEvents))).flat();
113
- restEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
114
- onBatch(restEntries.slice(-200));
115
- }
116
- };
117
- const onCreateTeam = async (input) => {
118
- return container.teamService.create(input);
119
- };
120
- const onListTeams = async () => {
121
- return container.teamService.list();
122
- };
123
- const onJoinTeam = async (teamId, agentId) => {
124
- return container.teamService.join(teamId, agentId);
125
- };
126
- const onLeaveTeam = async (teamId, agentId) => {
127
- return container.teamService.leave(teamId, agentId);
128
- };
129
- const onDisbandTeam = async (teamId) => {
130
- await container.teamService.disband(teamId);
131
- };
132
- const onSetTeamLead = async (teamId, agentId) => {
133
- return container.teamService.setLead(teamId, agentId);
134
- };
135
- const onRefreshGoals = async () => {
136
- return container.goalService.list();
137
- };
138
- const onCreateGoal = async (input) => {
139
- return container.goalService.create(input);
140
- };
141
- const onUpdateGoal = async (id, fields) => {
142
- return container.goalService.update(id, fields);
143
- };
144
- const onUpdateGoalStatus = async (id, status) => {
145
- return container.goalService.updateStatus(id, status);
146
- };
147
- const onDeleteGoal = async (id) => {
148
- await container.goalService.delete(id);
149
- };
150
- const onGetGoalProgress = async (goalId) => {
151
- return container.goalService.getProgressReport(goalId);
152
- };
153
- const onStartWatch = async () => {
154
- await container.orchestrator.startWatch();
155
- };
156
- const onStopWatch = async () => {
157
- await container.orchestrator.stop();
158
- };
159
- const currentVersion = program.version() ?? "0.0.0";
160
- const updateCheckPromise = import('./update-check-4YKLGBFB.js').then((m) => m.checkForUpdateSWR(currentVersion)).catch(() => null);
161
- let watchStarted = false;
162
- let watchError;
163
- try {
164
- await container.orchestrator.startWatch();
165
- watchStarted = true;
166
- } catch (err) {
167
- watchError = err instanceof Error ? err.message : String(err);
168
- }
169
- const updateInfo = await updateCheckPromise;
170
- const { waitUntilExit } = render(
171
- createElement(App, {
172
- projectName: container.config.project.name,
173
- tasks,
174
- agents,
175
- state,
176
- onRunTask,
177
- onCreateTask,
178
- onCancelTask,
179
- onRetryTask,
180
- onAssignTask,
181
- onRunAll,
182
- onDisableAgent,
183
- onEnableAgent,
184
- onSubscribeEvents,
185
- onRefreshTasks,
186
- onRefreshAgents,
187
- onRefreshState,
188
- onLoadHistory,
189
- onAddAgent,
190
- onDeleteAgent,
191
- onApproveTask,
192
- onRejectTask,
193
- onDeleteTask,
194
- onUpdateTask,
195
- onUpdateAgent,
196
- onForceStopAgent,
197
- onToggleAutonomous,
198
- onRefreshGoals,
199
- onCreateGoal,
200
- onUpdateGoal,
201
- onUpdateGoalStatus,
202
- onDeleteGoal,
203
- onGetGoalProgress,
204
- onCreateTeam,
205
- onListTeams,
206
- onJoinTeam,
207
- onLeaveTeam,
208
- onDisbandTeam,
209
- onSetTeamLead,
210
- onStartWatch,
211
- onStopWatch,
212
- initialWatchActive: watchStarted,
213
- watchError,
214
- version: currentVersion,
215
- latestVersion: updateInfo?.updateAvailable ? updateInfo.latest : void 0,
216
- initialActivityFilter: container.globalConfig.tui.activity_filter,
217
- onSaveActivityFilter: async (preset) => {
218
- await container.globalConfigStore.set("activity_filter", preset);
219
- },
220
- initialNotifications: container.globalConfig.tui.notifications,
221
- onSaveNotifications: async (notif) => {
222
- await container.globalConfigStore.set("notifications", notif);
223
- },
224
- initialMaxConcurrent: container.config.scheduling.max_concurrent_agents,
225
- onSaveMaxConcurrent: async (value) => {
226
- await container.configStore.set("scheduling.max_concurrent_agents", value);
227
- container.config.scheduling.max_concurrent_agents = value;
228
- },
229
- onCompleteOnboarding: async () => {
230
- const s = await container.stateStore.read();
231
- s.onboardingCompleted = true;
232
- await container.stateStore.write(s);
233
- }
234
- }),
235
- { kittyKeyboard: { mode: "auto", flags: ["disambiguateEscapeCodes"] } }
236
- );
237
- await waitUntilExit();
238
- if (watchStarted) {
239
- await container.orchestrator.stop().catch(() => {
240
- });
241
- }
242
- });
243
- }
244
-
245
- export { registerTuiCommand };
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env node
2
- import { checkForUpdateNow } from './chunk-FRTKB575.js';
3
- import { amber, dim } from './chunk-7X2GI5OV.js';
4
- import { execFile } from 'child_process';
5
- import chalk from 'chalk';
6
-
7
- var PACKAGE_NAME = "@oxgeneral/orch";
8
- function runInstall() {
9
- return new Promise((resolve) => {
10
- const child = execFile(
11
- "npm",
12
- ["install", "-g", `${PACKAGE_NAME}@latest`],
13
- { timeout: 6e4 },
14
- (err, stdout, stderr) => {
15
- const output = (stdout ?? "") + (stderr ?? "");
16
- resolve({ code: err ? 1 : 0, output });
17
- }
18
- );
19
- child.stdout?.pipe(process.stdout);
20
- child.stderr?.pipe(process.stderr);
21
- });
22
- }
23
- function registerUpdateCommand(program) {
24
- program.command("update").description("Check for updates and install the latest version").option("--check", "Only check, do not install").action(async (opts) => {
25
- console.log();
26
- console.log(` ${amber("orch update")} \xB7 checking for updates\u2026`);
27
- console.log();
28
- const currentVersion = program.version() ?? "0.0.0";
29
- const info = await checkForUpdateNow(currentVersion);
30
- if (!info) {
31
- console.log(` ${chalk.ansi256(167)("\u2715")} Could not reach npm registry`);
32
- console.log(` Check your network connection and try again.`);
33
- console.log();
34
- process.exit(1);
35
- }
36
- if (!info.updateAvailable) {
37
- console.log(` ${chalk.ansi256(72)("\u2713")} Already up to date ${dim(`(${info.current})`)}`);
38
- console.log();
39
- return;
40
- }
41
- console.log(` ${chalk.ansi256(214)("\u25CF")} Update available: ${dim(info.current)} \u2192 ${chalk.ansi256(72)(info.latest)}`);
42
- console.log();
43
- if (opts.check) {
44
- console.log(` Run ${dim("orch update")} to install.`);
45
- console.log();
46
- return;
47
- }
48
- console.log(` Installing ${PACKAGE_NAME}@${info.latest}\u2026`);
49
- console.log();
50
- const result = await runInstall();
51
- if (result.code !== 0) {
52
- console.log();
53
- console.log(` ${chalk.ansi256(167)("\u2715")} Update failed. Try manually:`);
54
- console.log(` npm install -g ${PACKAGE_NAME}@latest`);
55
- console.log();
56
- process.exit(1);
57
- }
58
- console.log();
59
- console.log(` ${chalk.ansi256(72)("\u2713")} Updated to ${info.latest}`);
60
- console.log();
61
- });
62
- }
63
-
64
- export { registerUpdateCommand };
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export { checkForUpdateNow, checkForUpdateSWR, printUpdateNotification } from './chunk-FRTKB575.js';
@@ -1,215 +0,0 @@
1
- #!/usr/bin/env node
2
- import { sanitizeId, validateWorkspacePath, ensureDir } from './chunk-LV6GDBBI.js';
3
- import { WorkspaceError } from './chunk-2C2TFQ7K.js';
4
- import path from 'path';
5
- import fs from 'fs/promises';
6
-
7
- // src/infrastructure/workspace/merge-strategy.ts
8
- var MergeStrategy = class {
9
- constructor(projectRoot, processManager) {
10
- this.projectRoot = projectRoot;
11
- this.processManager = processManager;
12
- }
13
- /**
14
- * Merge a branch into the current branch with --no-ff.
15
- * On conflict, aborts the merge and returns conflict info.
16
- */
17
- async mergeBack(branch) {
18
- return new Promise((resolve) => {
19
- const { process: proc } = this.processManager.spawn(
20
- "git",
21
- ["merge", "--no-ff", branch, "-m", `Merge ${branch}`],
22
- { cwd: this.projectRoot }
23
- );
24
- let output = "";
25
- const maxOutputLen = 2e3;
26
- const appendOutput = (chunk) => {
27
- if (output.length < maxOutputLen) output += chunk.toString();
28
- };
29
- proc.stdout?.on("data", appendOutput);
30
- proc.stderr?.on("data", appendOutput);
31
- proc.on("close", (code) => {
32
- if (code === 0) {
33
- resolve({ success: true });
34
- return;
35
- }
36
- const trimmedOutput = output.slice(0, 1e3);
37
- const isConflict = trimmedOutput.includes("CONFLICT") || trimmedOutput.includes("Merge conflict");
38
- if (!isConflict) {
39
- resolve({ success: false, conflictInfo: trimmedOutput });
40
- return;
41
- }
42
- try {
43
- const { process: abortProc } = this.processManager.spawn(
44
- "git",
45
- ["merge", "--abort"],
46
- { cwd: this.projectRoot }
47
- );
48
- abortProc.on("close", () => {
49
- resolve({ success: false, conflictInfo: trimmedOutput });
50
- });
51
- abortProc.on("error", () => {
52
- resolve({ success: false, conflictInfo: trimmedOutput });
53
- });
54
- } catch {
55
- resolve({ success: false, conflictInfo: trimmedOutput });
56
- }
57
- });
58
- proc.on("error", (err) => {
59
- resolve({ success: false, conflictInfo: err.message });
60
- });
61
- });
62
- }
63
- };
64
-
65
- // src/infrastructure/workspace/workspace-manager.ts
66
- var WorkspaceManager = class {
67
- constructor(projectRoot, orchestryDir, processManager) {
68
- this.projectRoot = projectRoot;
69
- this.orchestryDir = orchestryDir;
70
- this.processManager = processManager;
71
- this.mergeStrategy = new MergeStrategy(projectRoot, processManager);
72
- }
73
- mergeStrategy;
74
- gitRepoChecked = false;
75
- isGitRepo = false;
76
- async prepare(task, agent, config) {
77
- const mode = this.resolveMode(task, agent, config);
78
- if (mode !== "shared") {
79
- await this.requireGitRepo(mode);
80
- }
81
- switch (mode) {
82
- case "shared":
83
- return { path: this.projectRoot };
84
- case "worktree":
85
- return this.prepareWorktree(task);
86
- case "isolated":
87
- return { path: await this.prepareIsolated(task) };
88
- default:
89
- return { path: this.projectRoot };
90
- }
91
- }
92
- async requireGitRepo(mode) {
93
- if (!this.gitRepoChecked) {
94
- try {
95
- const { process: proc } = this.processManager.spawn(
96
- "git",
97
- ["rev-parse", "--is-inside-work-tree"],
98
- { cwd: this.projectRoot }
99
- );
100
- const code = await new Promise((resolve) => {
101
- proc.on("close", resolve);
102
- proc.on("error", () => resolve(1));
103
- });
104
- this.isGitRepo = code === 0;
105
- } catch {
106
- this.isGitRepo = false;
107
- }
108
- if (this.isGitRepo) this.gitRepoChecked = true;
109
- }
110
- if (!this.isGitRepo) {
111
- throw new WorkspaceError(
112
- `workspace_mode "${mode}" requires a git repository`,
113
- 'Run: git init && git add -A && git commit -m "Initial commit"\n Or set workspace_mode: shared in .orchestry/config.yml'
114
- );
115
- }
116
- }
117
- async mergeBack(branch) {
118
- return this.mergeStrategy.mergeBack(branch);
119
- }
120
- async cleanup(taskId) {
121
- const workspacePath = path.join(this.orchestryDir, "workspaces", sanitizeId(taskId));
122
- try {
123
- const { process: proc } = this.processManager.spawn(
124
- "git",
125
- ["worktree", "remove", "--force", workspacePath],
126
- { cwd: this.projectRoot }
127
- );
128
- await new Promise((resolve) => {
129
- proc.on("close", () => resolve());
130
- proc.on("error", () => resolve());
131
- });
132
- } catch {
133
- }
134
- try {
135
- await fs.rm(workspacePath, { recursive: true, force: true });
136
- } catch {
137
- }
138
- }
139
- validate(workspacePath, projectRoot) {
140
- validateWorkspacePath(workspacePath, projectRoot);
141
- }
142
- resolveMode(task, agent, config) {
143
- return task.workspace_mode ?? agent.config.workspace_mode ?? config.defaults.agent.workspace_mode ?? "worktree";
144
- }
145
- async prepareWorktree(task) {
146
- const workspacePath = path.join(
147
- this.orchestryDir,
148
- "workspaces",
149
- sanitizeId(task.id)
150
- );
151
- await ensureDir(path.dirname(workspacePath));
152
- const titleSlug = sanitizeTitle(task.title) || sanitizeId(task.id);
153
- const branchName = `orchestry/${sanitizeId(task.id)}/${titleSlug}`;
154
- const { process: proc } = this.processManager.spawn(
155
- "git",
156
- ["worktree", "add", workspacePath, "-b", branchName],
157
- { cwd: this.projectRoot }
158
- );
159
- await new Promise((resolve, reject) => {
160
- proc.on("close", (code) => {
161
- if (code === 0) resolve();
162
- else reject(new Error(`git worktree add failed with code ${code}`));
163
- });
164
- proc.on("error", reject);
165
- });
166
- const worktreeOrchestry = path.join(workspacePath, ".orchestry");
167
- await fs.rm(worktreeOrchestry, { recursive: true, force: true }).catch(() => {
168
- });
169
- return { path: workspacePath, branch: branchName };
170
- }
171
- async prepareIsolated(task) {
172
- const workspacePath = path.join(
173
- this.orchestryDir,
174
- "workspaces",
175
- sanitizeId(task.id)
176
- );
177
- await ensureDir(path.dirname(workspacePath));
178
- try {
179
- const { process: proc } = this.processManager.spawn(
180
- "git",
181
- ["clone", "--local", "--no-hardlinks", ".", workspacePath],
182
- { cwd: this.projectRoot }
183
- );
184
- await new Promise((resolve, reject) => {
185
- proc.on("close", (code) => {
186
- if (code === 0) resolve();
187
- else reject(new Error("git clone failed"));
188
- });
189
- proc.on("error", reject);
190
- });
191
- } catch {
192
- const excludeFile = path.join(this.orchestryDir, "workspace-exclude");
193
- const args = ["-a", `--exclude-from=${excludeFile}`, "./", `${workspacePath}/`];
194
- const { process: proc } = this.processManager.spawn("rsync", args, {
195
- cwd: this.projectRoot
196
- });
197
- await new Promise((resolve, reject) => {
198
- proc.on("close", (code) => {
199
- if (code === 0) resolve();
200
- else reject(new Error(`rsync failed with code ${code}`));
201
- });
202
- proc.on("error", reject);
203
- });
204
- }
205
- const clonedOrchestry = path.join(workspacePath, ".orchestry");
206
- await fs.rm(clonedOrchestry, { recursive: true, force: true }).catch(() => {
207
- });
208
- return workspacePath;
209
- }
210
- };
211
- function sanitizeTitle(title) {
212
- return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
213
- }
214
-
215
- export { WorkspaceManager };