aos-harness 0.3.0 → 0.3.2

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.
@@ -211,6 +211,16 @@ export default function (pi: ExtensionAPI) {
211
211
  `AOS Harness initialized\nProject: ${projectRoot}\nProfiles: ${profiles.length} | Agents: ${agentMap.size}\n\nRun /aos-run to start a deliberation.`,
212
212
  "info",
213
213
  );
214
+
215
+ // Auto-start session if CLI passed environment variables
216
+ const autoProfile = process.env.AOS_PROFILE;
217
+ const autoBrief = process.env.AOS_BRIEF;
218
+ if (autoProfile && autoBrief && projectRoot) {
219
+ // Defer to let Pi finish initialization, then send the command as user message
220
+ setTimeout(() => {
221
+ pi.sendUserMessage("/aos-run");
222
+ }, 500);
223
+ }
214
224
  });
215
225
 
216
226
  // ── 2. /aos-run command — main entry point ────────────────
@@ -228,74 +238,110 @@ export default function (pi: ExtensionAPI) {
228
238
  return;
229
239
  }
230
240
 
231
- // ── Select profile ────────────────────────────────────
232
- const profilesDir = join(projectRoot, "core", "profiles");
233
- const profiles = listDirsWithFile(profilesDir, "profile.yaml");
241
+ // ── Check for CLI-provided env vars (auto-start mode) ──
242
+ const envProfile = process.env.AOS_PROFILE;
243
+ const envBrief = process.env.AOS_BRIEF;
244
+ const envDomain = process.env.AOS_DOMAIN;
245
+ const envSessionId = process.env.AOS_SESSION_ID;
246
+ const autoMode = !!(envProfile && envBrief);
234
247
 
235
- if (profiles.length === 0) {
236
- ctx.ui.notify(
237
- "No profiles found in core/profiles/.\nCreate a directory with a profile.yaml file.",
238
- "warning",
239
- );
240
- return;
241
- }
242
-
243
- const profileNames = profiles.map((p) => p.name);
244
- let profileIdx: number;
245
- if (profiles.length === 1) {
246
- profileIdx = 0;
247
- } else {
248
- const selected = await ctx.ui.select("Select a profile:", profileNames);
249
- profileIdx = typeof selected === "number" ? selected : Number(selected);
250
- }
251
- if (profileIdx === undefined || profileIdx === null || profileIdx < 0) {
252
- ctx.ui.notify("No profile selected. Cancelled.", "info");
253
- return;
254
- }
255
- const selectedProfile = profiles[profileIdx];
256
- const profileDir = selectedProfile.dir;
257
-
258
- // ── Select brief ──────────────────────────────────────
259
- const briefsDir = join(projectRoot, "core", "briefs");
260
- const briefs = listDirsWithFile(briefsDir, "brief.md");
261
-
262
- if (briefs.length === 0) {
263
- ctx.ui.notify(
264
- "No briefs found in core/briefs/.\nCreate a directory containing a brief.md file.",
265
- "warning",
266
- );
267
- return;
268
- }
248
+ let profileDir: string;
249
+ let selectedDomain: string | undefined;
250
+ let domainDir: string | undefined;
269
251
 
270
- const briefNames = briefs.map((b) => b.name);
271
- let briefIdx: number;
272
- if (briefs.length === 1) {
273
- briefIdx = 0;
252
+ if (autoMode) {
253
+ // CLI provided profile and brief via env vars — skip interactive selection
254
+ profileDir = join(projectRoot, "core", "profiles", envProfile);
255
+ if (!existsSync(join(profileDir, "profile.yaml"))) {
256
+ ctx.ui.notify(`Profile not found: ${envProfile}`, "error");
257
+ return;
258
+ }
259
+ briefPath = envBrief;
260
+ if (!existsSync(briefPath)) {
261
+ ctx.ui.notify(`Brief not found: ${briefPath}`, "error");
262
+ return;
263
+ }
264
+ if (envDomain) {
265
+ selectedDomain = envDomain;
266
+ domainDir = join(projectRoot, "core", "domains", envDomain);
267
+ if (!existsSync(join(domainDir, "domain.yaml"))) {
268
+ ctx.ui.notify(`Domain not found: ${envDomain}`, "warning");
269
+ selectedDomain = undefined;
270
+ domainDir = undefined;
271
+ }
272
+ }
273
+ if (envSessionId) {
274
+ sessionId = envSessionId;
275
+ }
274
276
  } else {
275
- const selected = await ctx.ui.select("Select a brief:", briefNames);
276
- briefIdx = typeof selected === "number" ? selected : Number(selected);
277
- }
278
- if (briefIdx === undefined || briefIdx === null || briefIdx < 0) {
279
- ctx.ui.notify("No brief selected. Cancelled.", "info");
280
- return;
281
- }
282
- const selectedBrief = briefs[briefIdx];
283
- briefPath = join(selectedBrief.dir, "brief.md");
277
+ // Interactive mode select profile, brief, domain via UI
278
+ // ── Select profile ────────────────────────────────────
279
+ const profilesDir = join(projectRoot, "core", "profiles");
280
+ const profiles = listDirsWithFile(profilesDir, "profile.yaml");
281
+
282
+ if (profiles.length === 0) {
283
+ ctx.ui.notify(
284
+ "No profiles found in core/profiles/.\nCreate a directory with a profile.yaml file.",
285
+ "warning",
286
+ );
287
+ return;
288
+ }
284
289
 
285
- // ── Optionally select domain ──────────────────────────
286
- const domainsDir = join(projectRoot, "core", "domains");
287
- let selectedDomain: string | undefined;
288
- let domainDir: string | undefined;
290
+ const profileNames = profiles.map((p) => p.name);
291
+ let profileIdx: number;
292
+ if (profiles.length === 1) {
293
+ profileIdx = 0;
294
+ } else {
295
+ const selected = await ctx.ui.select("Select a profile:", profileNames);
296
+ profileIdx = typeof selected === "number" ? selected : Number(selected);
297
+ }
298
+ if (profileIdx === undefined || profileIdx === null || profileIdx < 0) {
299
+ ctx.ui.notify("No profile selected. Cancelled.", "info");
300
+ return;
301
+ }
302
+ const selectedProfile = profiles[profileIdx];
303
+ profileDir = selectedProfile.dir;
304
+
305
+ // ── Select brief ──────────────────────────────────────
306
+ const briefsDir = join(projectRoot, "core", "briefs");
307
+ const briefs = listDirsWithFile(briefsDir, "brief.md");
308
+
309
+ if (briefs.length === 0) {
310
+ ctx.ui.notify(
311
+ "No briefs found in core/briefs/.\nCreate a directory containing a brief.md file.",
312
+ "warning",
313
+ );
314
+ return;
315
+ }
289
316
 
290
- if (existsSync(domainsDir)) {
291
- const domains = listDirsWithFile(domainsDir, "domain.yaml");
292
- if (domains.length > 0) {
293
- const domainNames = ["(none)", ...domains.map((d) => d.name)];
294
- const rawDomainIdx = await ctx.ui.select("Select a domain (optional):", domainNames);
295
- const domainIdx = typeof rawDomainIdx === "number" ? rawDomainIdx : Number(rawDomainIdx);
296
- if (domainIdx > 0) {
297
- selectedDomain = domains[domainIdx - 1].name;
298
- domainDir = domains[domainIdx - 1].dir;
317
+ const briefNames = briefs.map((b) => b.name);
318
+ let briefIdx: number;
319
+ if (briefs.length === 1) {
320
+ briefIdx = 0;
321
+ } else {
322
+ const selected = await ctx.ui.select("Select a brief:", briefNames);
323
+ briefIdx = typeof selected === "number" ? selected : Number(selected);
324
+ }
325
+ if (briefIdx === undefined || briefIdx === null || briefIdx < 0) {
326
+ ctx.ui.notify("No brief selected. Cancelled.", "info");
327
+ return;
328
+ }
329
+ const selectedBrief = briefs[briefIdx];
330
+ briefPath = join(selectedBrief.dir, "brief.md");
331
+
332
+ // ── Optionally select domain ──────────────────────────
333
+ const domainsDir = join(projectRoot, "core", "domains");
334
+
335
+ if (existsSync(domainsDir)) {
336
+ const domains = listDirsWithFile(domainsDir, "domain.yaml");
337
+ if (domains.length > 0) {
338
+ const domainNames = ["(none)", ...domains.map((d) => d.name)];
339
+ const rawDomainIdx = await ctx.ui.select("Select a domain (optional):", domainNames);
340
+ const domainIdx = typeof rawDomainIdx === "number" ? rawDomainIdx : Number(rawDomainIdx);
341
+ if (domainIdx > 0) {
342
+ selectedDomain = domains[domainIdx - 1].name;
343
+ domainDir = domains[domainIdx - 1].dir;
344
+ }
299
345
  }
300
346
  }
301
347
  }
@@ -362,7 +408,9 @@ export default function (pi: ExtensionAPI) {
362
408
  }
363
409
 
364
410
  // Determine memo output path
365
- const briefSlug = selectedBrief.name;
411
+ const briefSlug = autoMode
412
+ ? basename(briefPath, ".md").replace(/\s+/g, "-").toLowerCase()
413
+ : basename(briefPath, ".md").replace(/\s+/g, "-").toLowerCase();
366
414
  const dateStr = new Date().toISOString().split("T")[0];
367
415
  const memoDir = join(projectRoot, "output", "memos", `${dateStr}-${briefSlug}-${sessionId}`);
368
416
  mkdirSync(memoDir, { recursive: true });
@@ -378,7 +426,7 @@ export default function (pi: ExtensionAPI) {
378
426
 
379
427
  // Resolve template variables using spec-compliant underscore names (Section 6.13)
380
428
  // Also include hyphenated aliases for backward compatibility
381
- const briefSlugValue = selectedBrief.name;
429
+ const briefSlugValue = briefSlug;
382
430
  const constraintsStr = `${profileRaw.match(/min_minutes:\s*(\d+)/)?.[1] ?? "?"}-${profileRaw.match(/max_minutes:\s*(\d+)/)?.[1] ?? "?"} min`;
383
431
  const deliberationDirPath = join(projectRoot, ".aos", "sessions", sessionId);
384
432
  const transcriptFilePath = join(deliberationDirPath, "transcript.jsonl");
@@ -416,9 +464,11 @@ export default function (pi: ExtensionAPI) {
416
464
  // ── Block input (allow only halt and wrap) ────────────
417
465
  ui.blockInput(["halt", "wrap"]);
418
466
 
419
- ctx.ui.setStatus("aos", `AOS: ${selectedProfile.name} | ${selectedBrief.name}`);
467
+ const profileDisplayName = autoMode ? envProfile : basename(profileDir);
468
+ const briefDisplayName = autoMode ? basename(briefPath) : basename(briefPath);
469
+ ctx.ui.setStatus("aos", `AOS: ${profileDisplayName} | ${briefDisplayName}`);
420
470
  ctx.ui.notify(
421
- `Deliberation started!\nProfile: ${selectedProfile.name}\nBrief: ${selectedBrief.name}\nMemo: ${memoPath}\n\nType 'halt' to stop or 'wrap' to end early.`,
471
+ `Deliberation started!\nProfile: ${profileDisplayName}\nBrief: ${briefDisplayName}\nMemo: ${memoPath}\n\nType 'halt' to stop or 'wrap' to end early.`,
422
472
  "info",
423
473
  );
424
474
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aos-harness",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Agentic Orchestration System — assemble AI agents into deliberation and execution teams",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "test": "bun run src/index.ts validate"
37
37
  },
38
38
  "dependencies": {
39
- "@aos-harness/runtime": "0.3.0",
39
+ "@aos-harness/runtime": "0.3.2",
40
40
  "js-yaml": "^4.1.0"
41
41
  },
42
42
  "devDependencies": {