ac-framework 2.0.0 → 2.1.0

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": "ac-framework",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Agentic Coding Framework - Multi-assistant configuration system with OpenSpec workflows",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -43,6 +43,10 @@ function runCommand(command, args, options = {}) {
43
43
  });
44
44
  }
45
45
 
46
+ function sleep(ms) {
47
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
48
+ }
49
+
46
50
  function workerCommand(sessionId, role, roleLog) {
47
51
  return `bash -lc 'node "${runnerPath}" agents worker --session ${sessionId} --role ${role} 2>&1 | tee -a "${roleLog}"'`;
48
52
  }
@@ -120,18 +124,53 @@ async function writeZellijLayout({ layoutPath, sessionId, sessionDir }) {
120
124
  await writeFile(layoutPath, content, 'utf8');
121
125
  }
122
126
 
123
- export async function spawnZellijSession({ sessionName, sessionDir, sessionId, binaryPath }) {
127
+ export async function spawnZellijSession({
128
+ sessionName,
129
+ sessionDir,
130
+ sessionId,
131
+ binaryPath,
132
+ waitForSessionMs = 10000,
133
+ pollIntervalMs = 250,
134
+ runCommandImpl,
135
+ spawnImpl,
136
+ }) {
124
137
  const layoutPath = resolve(sessionDir, 'synapsegrid-layout.kdl');
125
138
  await writeZellijLayout({ layoutPath, sessionId, sessionDir });
126
139
  const command = binaryPath || process.env.ACFM_ZELLIJ_BIN || 'zellij';
127
- await runCommand(command, ['--session', sessionName, '--layout', layoutPath, '--detach']);
128
- return { layoutPath };
140
+
141
+ const spawnFn = spawnImpl || spawn;
142
+ const child = spawnFn(command, ['--session', sessionName, '--layout', layoutPath], {
143
+ cwd: sessionDir,
144
+ env: process.env,
145
+ detached: true,
146
+ stdio: 'ignore',
147
+ });
148
+ if (typeof child.unref === 'function') {
149
+ child.unref();
150
+ }
151
+
152
+ const startedAt = Date.now();
153
+ while ((Date.now() - startedAt) < waitForSessionMs) {
154
+ // eslint-disable-next-line no-await-in-loop
155
+ const exists = await zellijSessionExists(sessionName, binaryPath, { runCommandImpl });
156
+ if (exists) {
157
+ return { layoutPath };
158
+ }
159
+ // eslint-disable-next-line no-await-in-loop
160
+ await sleep(pollIntervalMs);
161
+ }
162
+
163
+ throw new Error(
164
+ `Timed out waiting for zellij session '${sessionName}' to start (binary: ${command}). ` +
165
+ 'Try `acfm agents doctor` or fallback with `acfm agents start --mux tmux ...`'
166
+ );
129
167
  }
130
168
 
131
- export async function zellijSessionExists(sessionName, binaryPath) {
169
+ export async function zellijSessionExists(sessionName, binaryPath, options = {}) {
132
170
  try {
171
+ const runner = options.runCommandImpl || runCommand;
133
172
  const command = binaryPath || process.env.ACFM_ZELLIJ_BIN || 'zellij';
134
- const result = await runCommand(command, ['list-sessions']);
173
+ const result = await runner(command, ['list-sessions']);
135
174
  const lines = result.stdout.split('\n').map((line) => line.trim()).filter(Boolean);
136
175
  return lines.some((line) => line === sessionName || line.startsWith(`${sessionName} `));
137
176
  } catch {