@a5c-ai/babysitter-sdk 0.0.186 → 0.0.187-staging.02a0ee21

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 (102) hide show
  1. package/dist/cli/commands/costStats.d.ts +13 -0
  2. package/dist/cli/commands/costStats.d.ts.map +1 -0
  3. package/dist/cli/commands/costStats.js +319 -0
  4. package/dist/cli/commands/harnessPhase2.js +10 -10
  5. package/dist/cli/commands/instructions.d.ts.map +1 -1
  6. package/dist/cli/commands/instructions.js +5 -3
  7. package/dist/cli/main.d.ts.map +1 -1
  8. package/dist/cli/main.js +16 -1
  9. package/dist/config/defaults.d.ts +10 -0
  10. package/dist/config/defaults.d.ts.map +1 -1
  11. package/dist/config/defaults.js +19 -0
  12. package/dist/config/index.d.ts +1 -1
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/index.js +2 -1
  15. package/dist/cost/claudeCodeParser.d.ts +81 -0
  16. package/dist/cost/claudeCodeParser.d.ts.map +1 -0
  17. package/dist/cost/claudeCodeParser.js +232 -0
  18. package/dist/cost/collector.d.ts +42 -0
  19. package/dist/cost/collector.d.ts.map +1 -0
  20. package/dist/cost/collector.js +105 -0
  21. package/dist/cost/index.d.ts +18 -0
  22. package/dist/cost/index.d.ts.map +1 -0
  23. package/dist/cost/index.js +37 -0
  24. package/dist/cost/journal.d.ts +40 -0
  25. package/dist/cost/journal.d.ts.map +1 -0
  26. package/dist/cost/journal.js +137 -0
  27. package/dist/cost/types.d.ts +164 -0
  28. package/dist/cost/types.d.ts.map +1 -0
  29. package/dist/cost/types.js +231 -0
  30. package/dist/harness/claudeCode.js +1 -1
  31. package/dist/harness/cursor.js +1 -1
  32. package/dist/harness/discovery.d.ts.map +1 -1
  33. package/dist/harness/discovery.js +8 -1
  34. package/dist/harness/geminiCli.js +1 -1
  35. package/dist/harness/githubCopilot.js +1 -1
  36. package/dist/harness/index.d.ts +1 -0
  37. package/dist/harness/index.d.ts.map +1 -1
  38. package/dist/harness/index.js +3 -1
  39. package/dist/harness/invoker.d.ts.map +1 -1
  40. package/dist/harness/invoker.js +1 -0
  41. package/dist/harness/openclaw.d.ts +20 -0
  42. package/dist/harness/openclaw.d.ts.map +1 -0
  43. package/dist/harness/openclaw.js +338 -0
  44. package/dist/harness/opencode.d.ts.map +1 -1
  45. package/dist/harness/opencode.js +75 -5
  46. package/dist/harness/registry.d.ts.map +1 -1
  47. package/dist/harness/registry.js +2 -0
  48. package/dist/hooks/dispatcher.d.ts +9 -0
  49. package/dist/hooks/dispatcher.d.ts.map +1 -1
  50. package/dist/hooks/dispatcher.js +94 -0
  51. package/dist/hooks/index.d.ts +2 -1
  52. package/dist/hooks/index.d.ts.map +1 -1
  53. package/dist/hooks/index.js +2 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +1 -0
  57. package/dist/logging/runLogger.js +2 -2
  58. package/dist/prompts/babysitterMdDiscovery.d.ts +38 -0
  59. package/dist/prompts/babysitterMdDiscovery.d.ts.map +1 -0
  60. package/dist/prompts/babysitterMdDiscovery.js +150 -0
  61. package/dist/prompts/compose.d.ts.map +1 -1
  62. package/dist/prompts/compose.js +5 -0
  63. package/dist/prompts/context.d.ts +4 -0
  64. package/dist/prompts/context.d.ts.map +1 -1
  65. package/dist/prompts/context.js +37 -0
  66. package/dist/prompts/index.d.ts +1 -1
  67. package/dist/prompts/index.d.ts.map +1 -1
  68. package/dist/prompts/index.js +2 -1
  69. package/dist/prompts/parts/index.d.ts +3 -0
  70. package/dist/prompts/parts/index.d.ts.map +1 -1
  71. package/dist/prompts/parts/index.js +7 -1
  72. package/dist/prompts/parts/parallelPhaseDetection.d.ts +6 -0
  73. package/dist/prompts/parts/parallelPhaseDetection.d.ts.map +1 -0
  74. package/dist/prompts/parts/parallelPhaseDetection.js +10 -0
  75. package/dist/prompts/parts/projectInstructions.d.ts +8 -0
  76. package/dist/prompts/parts/projectInstructions.d.ts.map +1 -0
  77. package/dist/prompts/parts/projectInstructions.js +28 -0
  78. package/dist/prompts/parts/runOverlapDetection.d.ts +6 -0
  79. package/dist/prompts/parts/runOverlapDetection.d.ts.map +1 -0
  80. package/dist/prompts/parts/runOverlapDetection.js +10 -0
  81. package/dist/prompts/templates/commands/doctor.md +9 -9
  82. package/dist/prompts/templates/intent-fidelity-checks.md +3 -1
  83. package/dist/prompts/templates/parallel-phase-detection.md +59 -0
  84. package/dist/prompts/templates/process-creation.md +9 -2
  85. package/dist/prompts/templates/process-guidelines.md +5 -2
  86. package/dist/prompts/templates/run-overlap-detection.md +63 -0
  87. package/dist/prompts/templates/task-examples.md +47 -0
  88. package/dist/prompts/templates/task-kinds.md +44 -2
  89. package/dist/runtime/intrinsics/breakpoint.d.ts.map +1 -1
  90. package/dist/runtime/intrinsics/breakpoint.js +20 -1
  91. package/dist/runtime/orchestrateIteration.d.ts.map +1 -1
  92. package/dist/runtime/orchestrateIteration.js +14 -0
  93. package/dist/session/types.d.ts +2 -0
  94. package/dist/session/types.d.ts.map +1 -1
  95. package/dist/storage/createRunDir.d.ts.map +1 -1
  96. package/dist/storage/createRunDir.js +2 -0
  97. package/dist/storage/icloudWarning.d.ts +6 -0
  98. package/dist/storage/icloudWarning.d.ts.map +1 -0
  99. package/dist/storage/icloudWarning.js +84 -0
  100. package/dist/storage/journal.d.ts.map +1 -1
  101. package/dist/storage/journal.js +2 -0
  102. package/package.json +1 -1
@@ -0,0 +1,338 @@
1
+ "use strict";
2
+ /**
3
+ * OpenClaw harness adapter.
4
+ *
5
+ * Centralizes all OpenClaw-specific behaviors:
6
+ * - Session ID resolution (BABYSITTER_SESSION_ID, OPENCLAW_SHELL context)
7
+ * - State directory conventions (global state dir by default)
8
+ * - Session binding (run:create -> state file with run association)
9
+ * - No stop hook (OpenClaw is a daemon with no stop-hook model)
10
+ * - Session-start hook handler (baseline state file + skill discovery context)
11
+ *
12
+ * OpenClaw characteristics:
13
+ * - Daemon/gateway model, CLI command: `openclaw`
14
+ * - Env vars: OPENCLAW_SHELL, OPENCLAW_HOME
15
+ * - Sessions use composite keys: `agent:<agentId>:<channel>:<type>:<id>`
16
+ * - NO stop-hook model — hooks are registered programmatically
17
+ * - Capabilities: SessionBinding, Mcp, HeadlessPrompt (NOT StopHook, NOT Programmatic)
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.createOpenClawAdapter = createOpenClawAdapter;
54
+ const path = __importStar(require("node:path"));
55
+ const node_fs_1 = require("node:fs");
56
+ const session_1 = require("../session");
57
+ const types_1 = require("./types");
58
+ const context_1 = require("../prompts/context");
59
+ const config_1 = require("../config");
60
+ const discovery_1 = require("./discovery");
61
+ const installSupport_1 = require("./installSupport");
62
+ // ---------------------------------------------------------------------------
63
+ // Constants
64
+ // ---------------------------------------------------------------------------
65
+ const HARNESS_NAME = "openclaw";
66
+ function createHookLogger(hookName) {
67
+ const logDir = (0, config_1.getGlobalLogDir)();
68
+ const logFile = logDir ? path.join(logDir, `${hookName}.log`) : null;
69
+ const context = {};
70
+ if (logFile) {
71
+ try {
72
+ (0, node_fs_1.mkdirSync)(logDir, { recursive: true });
73
+ }
74
+ catch {
75
+ // Best-effort
76
+ }
77
+ }
78
+ function write(level, message) {
79
+ if (!logFile)
80
+ return;
81
+ const ts = new Date().toISOString();
82
+ const ctxParts = Object.entries(context).map(([k, v]) => `${k}=${v}`);
83
+ const ctxStr = ctxParts.length > 0 ? ` [${ctxParts.join(" ")}]` : "";
84
+ const line = `[${level}] ${ts}${ctxStr} ${message}\n`;
85
+ try {
86
+ (0, node_fs_1.appendFileSync)(logFile, line);
87
+ }
88
+ catch {
89
+ // Best-effort
90
+ }
91
+ }
92
+ return {
93
+ info: (msg) => write("INFO", msg),
94
+ warn: (msg) => write("WARN", msg),
95
+ error: (msg) => write("ERROR", msg),
96
+ setContext: (key, value) => {
97
+ context[key] = value;
98
+ },
99
+ };
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // State directory resolution
103
+ // ---------------------------------------------------------------------------
104
+ function resolveStateDirInternal(args) {
105
+ if (args.stateDir)
106
+ return path.resolve(args.stateDir);
107
+ return (0, config_1.getGlobalStateDir)();
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Session ID resolution
111
+ // ---------------------------------------------------------------------------
112
+ function resolveSessionIdInternal(parsed) {
113
+ // 1. Explicit arg (highest priority)
114
+ if (parsed.sessionId)
115
+ return parsed.sessionId;
116
+ // 2. Cross-harness standard env var
117
+ if (process.env.BABYSITTER_SESSION_ID)
118
+ return process.env.BABYSITTER_SESSION_ID;
119
+ // 3. OpenClaw shell context — composite key provided by the gateway
120
+ if (process.env.OPENCLAW_SHELL)
121
+ return process.env.OPENCLAW_SHELL;
122
+ return undefined;
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Stop hook handler (no-op — OpenClaw has no stop-hook model)
126
+ // ---------------------------------------------------------------------------
127
+ function writeNoopHookResult() {
128
+ process.stdout.write("{}\n");
129
+ }
130
+ // ---------------------------------------------------------------------------
131
+ // SessionStart hook handler
132
+ // ---------------------------------------------------------------------------
133
+ async function handleSessionStartHookImpl(args) {
134
+ const { verbose } = args;
135
+ const log = createHookLogger("babysitter-openclaw-session-start-hook");
136
+ log.info("handleSessionStartHook started (openclaw)");
137
+ // OpenClaw is a daemon — session IDs come from the gateway env, not stdin.
138
+ const sessionId = process.env.BABYSITTER_SESSION_ID || process.env.OPENCLAW_SHELL || "";
139
+ if (!sessionId) {
140
+ log.info("No session ID — skipping state file creation");
141
+ process.stdout.write("{}\n");
142
+ return 0;
143
+ }
144
+ log.setContext("session", sessionId);
145
+ log.info(`Session ID: ${sessionId}`);
146
+ // Resolve state directory and create baseline session file
147
+ const stateDir = resolveStateDirInternal(args);
148
+ log.info(`Resolved stateDir: ${stateDir}`);
149
+ const filePath = (0, session_1.getSessionFilePath)(stateDir, sessionId);
150
+ try {
151
+ if (!(await (0, session_1.sessionFileExists)(filePath))) {
152
+ const nowTs = (0, session_1.getCurrentTimestamp)();
153
+ const state = {
154
+ active: true,
155
+ iteration: 1,
156
+ maxIterations: 256,
157
+ runId: "",
158
+ startedAt: nowTs,
159
+ lastIterationAt: nowTs,
160
+ iterationTimes: [],
161
+ };
162
+ await (0, session_1.writeSessionFile)(filePath, state, "");
163
+ log.info(`Created session state: ${filePath}`);
164
+ if (verbose) {
165
+ process.stderr.write(`[hook:run session-start] Created session state: ${filePath}\n`);
166
+ }
167
+ }
168
+ else {
169
+ log.info(`Session state already exists: ${filePath}`);
170
+ }
171
+ }
172
+ catch (e) {
173
+ const msg = e instanceof Error ? e.message : String(e);
174
+ log.warn(`Failed to create session state: ${msg}`);
175
+ if (verbose) {
176
+ process.stderr.write(`[hook:run session-start] Failed to create session state: ${msg}\n`);
177
+ }
178
+ }
179
+ // Output empty object
180
+ process.stdout.write("{}\n");
181
+ return 0;
182
+ }
183
+ // ---------------------------------------------------------------------------
184
+ // Session binding (run:create flow)
185
+ // ---------------------------------------------------------------------------
186
+ async function bindSessionImpl(opts) {
187
+ const { sessionId, runId, maxIterations = 256, prompt, verbose } = opts;
188
+ // Resolve state directory
189
+ const stateDir = resolveStateDirInternal({
190
+ stateDir: opts.stateDir,
191
+ pluginRoot: opts.pluginRoot,
192
+ });
193
+ const filePath = (0, session_1.getSessionFilePath)(stateDir, sessionId);
194
+ // Check for existing session (prevent re-entrant runs)
195
+ if (await (0, session_1.sessionFileExists)(filePath)) {
196
+ try {
197
+ const existing = await (0, session_1.readSessionFile)(filePath);
198
+ if (existing.state.runId && existing.state.runId !== runId) {
199
+ return {
200
+ harness: HARNESS_NAME,
201
+ sessionId,
202
+ stateFile: filePath,
203
+ error: `Session already associated with run: ${existing.state.runId}`,
204
+ };
205
+ }
206
+ // Update existing session with run ID
207
+ await (0, session_1.updateSessionState)(filePath, { runId, active: true }, { state: existing.state, prompt: existing.prompt });
208
+ if (verbose) {
209
+ process.stderr.write(`[run:create] Updated existing session ${sessionId} with run ${runId}\n`);
210
+ }
211
+ return { harness: HARNESS_NAME, sessionId, stateFile: filePath };
212
+ }
213
+ catch {
214
+ // Corrupted state file — overwrite
215
+ }
216
+ }
217
+ // Create new session state with run associated
218
+ const nowTs = (0, session_1.getCurrentTimestamp)();
219
+ const state = {
220
+ active: true,
221
+ iteration: 1,
222
+ maxIterations,
223
+ runId,
224
+ startedAt: nowTs,
225
+ lastIterationAt: nowTs,
226
+ iterationTimes: [],
227
+ };
228
+ try {
229
+ await (0, session_1.writeSessionFile)(filePath, state, prompt);
230
+ }
231
+ catch (e) {
232
+ return {
233
+ harness: HARNESS_NAME,
234
+ sessionId,
235
+ error: `Failed to write session state: ${e instanceof Error ? e.message : String(e)}`,
236
+ };
237
+ }
238
+ if (verbose) {
239
+ process.stderr.write(`[run:create] Session ${sessionId} initialized and bound to run ${runId}\n`);
240
+ }
241
+ return { harness: HARNESS_NAME, sessionId, stateFile: filePath };
242
+ }
243
+ // ---------------------------------------------------------------------------
244
+ // Adapter factory
245
+ // ---------------------------------------------------------------------------
246
+ function createOpenClawAdapter() {
247
+ return {
248
+ name: HARNESS_NAME,
249
+ isActive() {
250
+ return !!(process.env.OPENCLAW_SHELL || process.env.OPENCLAW_HOME);
251
+ },
252
+ autoResolvesSessionId() {
253
+ return true;
254
+ },
255
+ getMissingSessionIdHint() {
256
+ return ("Session ID is provided by the OpenClaw gateway. " +
257
+ "Ensure you're running inside an OpenClaw agent session.");
258
+ },
259
+ supportsHookType(hookType) {
260
+ // OpenClaw hooks are registered programmatically via the daemon.
261
+ // Only session-start is supported via the SDK hook:run path.
262
+ return hookType === "session-start";
263
+ },
264
+ getUnsupportedHookMessage(hookType) {
265
+ if (hookType === "stop") {
266
+ return ("OpenClaw does not support a blocking stop hook. " +
267
+ "The daemon manages agent lifecycle via agent_end signals. " +
268
+ "Use the OpenClaw gateway API instead.");
269
+ }
270
+ return `Hook type "${hookType}" is not supported by the OpenClaw adapter. OpenClaw hooks are registered programmatically.`;
271
+ },
272
+ getCapabilities() {
273
+ return [
274
+ types_1.HarnessCapability.SessionBinding,
275
+ types_1.HarnessCapability.Mcp,
276
+ types_1.HarnessCapability.HeadlessPrompt,
277
+ ];
278
+ },
279
+ resolveSessionId(parsed) {
280
+ return resolveSessionIdInternal(parsed);
281
+ },
282
+ resolveStateDir(args) {
283
+ return resolveStateDirInternal(args);
284
+ },
285
+ resolvePluginRoot(_args) {
286
+ // OpenClaw has no OPENCLAW_PLUGIN_ROOT env var.
287
+ return undefined;
288
+ },
289
+ bindSession(opts) {
290
+ return bindSessionImpl(opts);
291
+ },
292
+ handleStopHook(_args) {
293
+ // No-op: OpenClaw has no stop-hook model.
294
+ writeNoopHookResult();
295
+ return Promise.resolve(0);
296
+ },
297
+ handleSessionStartHook(args) {
298
+ return handleSessionStartHookImpl(args);
299
+ },
300
+ findHookDispatcherPath(_startCwd) {
301
+ // OpenClaw hooks are registered programmatically via the daemon.
302
+ return null;
303
+ },
304
+ async isCliInstalled() {
305
+ const result = await (0, discovery_1.checkCliAvailable)("openclaw");
306
+ return result.available;
307
+ },
308
+ async getCliInfo() {
309
+ const result = await (0, discovery_1.checkCliAvailable)("openclaw");
310
+ return {
311
+ command: "openclaw",
312
+ version: result.version,
313
+ path: result.path,
314
+ };
315
+ },
316
+ installHarness(options) {
317
+ return (0, installSupport_1.installCliViaNpm)({
318
+ harness: HARNESS_NAME,
319
+ cliCommand: "openclaw",
320
+ packageName: "openclaw",
321
+ summary: "Install the OpenClaw CLI globally via npm.",
322
+ options,
323
+ });
324
+ },
325
+ installPlugin(options) {
326
+ return (0, installSupport_1.installCliViaNpm)({
327
+ harness: HARNESS_NAME,
328
+ cliCommand: "openclaw",
329
+ packageName: "@a5c-ai/babysitter-openclaw",
330
+ summary: "Install the Babysitter OpenClaw plugin package, then register it via `openclaw plugin install babysitter-openclaw`.",
331
+ options,
332
+ });
333
+ },
334
+ getPromptContext(opts) {
335
+ return (0, context_1.createOpenClawContext)(opts);
336
+ },
337
+ };
338
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/harness/opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAkBH,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,SAAS,CAAC;AA+fjB,wBAAgB,qBAAqB,IAAI,cAAc,CAsHtD"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/harness/opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAmBH,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,SAAS,CAAC;AA4iBjB,wBAAgB,qBAAqB,IAAI,cAAc,CAkJtD"}
@@ -60,6 +60,7 @@ var __importStar = (this && this.__importStar) || (function () {
60
60
  Object.defineProperty(exports, "__esModule", { value: true });
61
61
  exports.createOpenCodeAdapter = createOpenCodeAdapter;
62
62
  const path = __importStar(require("node:path"));
63
+ const os = __importStar(require("node:os"));
63
64
  const node_fs_1 = require("node:fs");
64
65
  const journal_1 = require("../storage/journal");
65
66
  const runFiles_1 = require("../storage/runFiles");
@@ -73,7 +74,7 @@ const config_1 = require("../config");
73
74
  // ---------------------------------------------------------------------------
74
75
  const HARNESS_NAME = "opencode";
75
76
  function createHookLogger(hookName) {
76
- const logDir = process.env.BABYSITTER_LOG_DIR || ".a5c/logs";
77
+ const logDir = (0, config_1.getGlobalLogDir)();
77
78
  const logFile = logDir ? path.join(logDir, `${hookName}.log`) : null;
78
79
  const context = {};
79
80
  if (logFile) {
@@ -445,6 +446,7 @@ async function bindSessionImpl(opts) {
445
446
  }
446
447
  // Create new session state with run associated
447
448
  const nowTs = (0, session_1.getCurrentTimestamp)();
449
+ const accomplishTaskId = process.env.ACCOMPLISH_TASK_ID;
448
450
  const state = {
449
451
  active: true,
450
452
  iteration: 1,
@@ -453,6 +455,7 @@ async function bindSessionImpl(opts) {
453
455
  startedAt: nowTs,
454
456
  lastIterationAt: nowTs,
455
457
  iterationTimes: [],
458
+ ...(accomplishTaskId ? { metadata: { accomplishTaskId } } : {}),
456
459
  };
457
460
  try {
458
461
  await (0, session_1.writeSessionFile)(filePath, state, prompt);
@@ -482,6 +485,45 @@ function installOpenCodePlugin(_options) {
482
485
  };
483
486
  }
484
487
  // ---------------------------------------------------------------------------
488
+ // Accomplish AI data directory resolution
489
+ // ---------------------------------------------------------------------------
490
+ /**
491
+ * Returns candidate Accomplish AI data directories for the current platform.
492
+ *
493
+ * Accomplish is an Electron desktop app that spawns OpenCode as a subprocess.
494
+ * When running inside Accomplish, plugin bundles are stored under the
495
+ * platform-specific application data directory.
496
+ */
497
+ function getAccomplishDataDirs() {
498
+ const dirs = [];
499
+ // Derive from OPENCODE_CONFIG_DIR if set (parent is the Accomplish data dir)
500
+ const configDir = process.env.OPENCODE_CONFIG_DIR;
501
+ if (configDir) {
502
+ const parent = path.dirname(configDir);
503
+ if (parent && parent !== configDir) {
504
+ dirs.push(parent);
505
+ }
506
+ }
507
+ const home = os.homedir();
508
+ const platform = process.platform;
509
+ if (platform === "darwin") {
510
+ dirs.push(path.join(home, "Library", "Application Support", "Accomplish"));
511
+ }
512
+ else if (platform === "win32") {
513
+ if (process.env.APPDATA) {
514
+ dirs.push(path.join(process.env.APPDATA, "Accomplish"));
515
+ }
516
+ if (process.env.LOCALAPPDATA) {
517
+ dirs.push(path.join(process.env.LOCALAPPDATA, "Accomplish"));
518
+ }
519
+ }
520
+ else {
521
+ // Linux and other Unix-like
522
+ dirs.push(path.join(home, ".config", "Accomplish"));
523
+ }
524
+ return dirs;
525
+ }
526
+ // ---------------------------------------------------------------------------
485
527
  // Adapter factory
486
528
  // ---------------------------------------------------------------------------
487
529
  function createOpenCodeAdapter() {
@@ -491,8 +533,10 @@ function createOpenCodeAdapter() {
491
533
  // OpenCode does NOT inject distinctive env vars into plugin subprocesses.
492
534
  // The babysitter plugin's shell.env hook self-injects BABYSITTER_SESSION_ID.
493
535
  // OPENCODE_CONFIG is a real OpenCode env var that indicates OpenCode context.
536
+ // ACCOMPLISH_TASK_ID is set when running inside the Accomplish AI desktop app.
494
537
  return !!(process.env.BABYSITTER_SESSION_ID ||
495
- process.env.OPENCODE_CONFIG);
538
+ process.env.OPENCODE_CONFIG ||
539
+ process.env.ACCOMPLISH_TASK_ID);
496
540
  },
497
541
  autoResolvesSessionId() {
498
542
  // OpenCode does not natively inject session IDs — the babysitter
@@ -539,9 +583,27 @@ function createOpenCodeAdapter() {
539
583
  return resolveStateDirInternal(args);
540
584
  },
541
585
  resolvePluginRoot(args) {
542
- const root = args.pluginRoot ||
543
- process.env.OPENCODE_PLUGIN_ROOT;
544
- return root ? path.resolve(root) : undefined;
586
+ // 1. Explicit arg (highest priority)
587
+ if (args.pluginRoot)
588
+ return path.resolve(args.pluginRoot);
589
+ // 2. OPENCODE_PLUGIN_ROOT env var
590
+ if (process.env.OPENCODE_PLUGIN_ROOT) {
591
+ return path.resolve(process.env.OPENCODE_PLUGIN_ROOT);
592
+ }
593
+ // 3. OPENCODE_CONFIG_DIR env var → <configDir>/plugins/babysitter/
594
+ const configDir = process.env.OPENCODE_CONFIG_DIR;
595
+ if (configDir) {
596
+ const candidate = path.resolve(configDir, "plugins", "babysitter");
597
+ if ((0, node_fs_1.existsSync)(candidate))
598
+ return candidate;
599
+ }
600
+ // 4. Platform-specific Accomplish data dirs → <dataDir>/opencode/plugins/babysitter/
601
+ for (const dataDir of getAccomplishDataDirs()) {
602
+ const candidate = path.join(dataDir, "opencode", "plugins", "babysitter");
603
+ if ((0, node_fs_1.existsSync)(candidate))
604
+ return candidate;
605
+ }
606
+ return undefined;
545
607
  },
546
608
  bindSession(opts) {
547
609
  return bindSessionImpl(opts);
@@ -566,6 +628,14 @@ function createOpenCodeAdapter() {
566
628
  return tsCandidate;
567
629
  current = path.dirname(current);
568
630
  }
631
+ // Fallback: check Accomplish AI data directories
632
+ // Derive from OPENCODE_CONFIG_DIR first, then platform-specific paths
633
+ const accomplishDirs = getAccomplishDataDirs();
634
+ for (const dataDir of accomplishDirs) {
635
+ const candidate = path.join(dataDir, "opencode", "plugins", "babysitter", "index.js");
636
+ if ((0, node_fs_1.existsSync)(candidate))
637
+ return candidate;
638
+ }
569
639
  return null;
570
640
  },
571
641
  installPlugin(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/harness/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAiC9C;;;GAGG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAK9C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKpE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAQD;;GAEG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAK3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/harness/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAmC9C;;;GAGG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAK9C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKpE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAQD;;GAEG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAK3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -22,6 +22,7 @@ const internal_1 = require("./internal");
22
22
  const cursor_1 = require("./cursor");
23
23
  const githubCopilot_1 = require("./githubCopilot");
24
24
  const opencode_1 = require("./opencode");
25
+ const openclaw_1 = require("./openclaw");
25
26
  const customAdapter_1 = require("./customAdapter");
26
27
  // ---------------------------------------------------------------------------
27
28
  // Registry of known adapters (ordered by priority)
@@ -31,6 +32,7 @@ const knownAdapters = [
31
32
  (0, ohMyPi_1.createOhMyPiAdapter)(),
32
33
  (0, internal_1.createInternalAdapter)(),
33
34
  (0, pi_1.createPiAdapter)(),
35
+ (0, openclaw_1.createOpenClawAdapter)(),
34
36
  (0, opencode_1.createOpenCodeAdapter)(),
35
37
  (0, claudeCode_1.createClaudeCodeAdapter)(),
36
38
  (0, geminiCli_1.createGeminiCliAdapter)(),
@@ -3,6 +3,15 @@
3
3
  * Executes shell hooks from Node.js
4
4
  */
5
5
  import type { HookDispatcherOptions, HookResult } from "./types";
6
+ export interface DiscoveredHook {
7
+ path: string;
8
+ name: string;
9
+ location: "per-repo" | "per-user";
10
+ }
11
+ /**
12
+ * Discover hook scripts for a given hook type from project and user directories.
13
+ */
14
+ export declare function discoverHooks(hookType: string, cwd: string, homeDir?: string): Promise<DiscoveredHook[]>;
6
15
  /**
7
16
  * Find `plugins/babysitter/hooks/hook-dispatcher.sh` by walking up from cwd.
8
17
  * This allows running from nested projects/fixtures inside a mono-repo.
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/hooks/dispatcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EAEX,MAAM,SAAS,CAAC;AAIjB;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAetE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CAoHrB"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/hooks/dispatcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EAEX,MAAM,SAAS,CAAC;AAMjB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,UAAU,CAAC;CACnC;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAqB,GAC7B,OAAO,CAAC,cAAc,EAAE,CAAC,CAmC3B;AAkED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAetE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CAyHrB"}
@@ -37,13 +37,102 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  };
38
38
  })();
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.discoverHooks = discoverHooks;
40
41
  exports.findHookDispatcherPath = findHookDispatcherPath;
41
42
  exports.callHook = callHook;
42
43
  const node_child_process_1 = require("node:child_process");
43
44
  const node_fs_1 = require("node:fs");
45
+ const os = __importStar(require("node:os"));
44
46
  const path = __importStar(require("node:path"));
45
47
  const defaults_1 = require("../config/defaults");
46
48
  const harness_1 = require("../harness");
49
+ const HOOK_SCRIPT_EXTENSIONS = new Set([".sh", ".js", ".ts", ".py", ".bash"]);
50
+ /**
51
+ * Discover hook scripts for a given hook type from project and user directories.
52
+ */
53
+ async function discoverHooks(hookType, cwd, homeDir = os.homedir()) {
54
+ const projectHookDir = path.join(cwd, ".a5c", "hooks", hookType);
55
+ const userHookDir = path.join(homeDir, ".a5c", "hooks", hookType);
56
+ async function readHooksFromDir(dir, location) {
57
+ try {
58
+ const entries = await node_fs_1.promises.readdir(dir, { withFileTypes: true });
59
+ return entries
60
+ .filter((entry) => {
61
+ if (!entry.isFile())
62
+ return false;
63
+ const ext = path.extname(entry.name);
64
+ return HOOK_SCRIPT_EXTENSIONS.has(ext);
65
+ })
66
+ .map((entry) => ({
67
+ path: path.join(dir, entry.name),
68
+ name: entry.name,
69
+ location,
70
+ }));
71
+ }
72
+ catch (err) {
73
+ if (err.code === "ENOENT") {
74
+ return [];
75
+ }
76
+ throw err;
77
+ }
78
+ }
79
+ const [projectHooks, userHooks] = await Promise.all([
80
+ readHooksFromDir(projectHookDir, "per-repo"),
81
+ readHooksFromDir(userHookDir, "per-user"),
82
+ ]);
83
+ return [...projectHooks, ...userHooks];
84
+ }
85
+ /**
86
+ * Execute discovered hook scripts sequentially, passing payload as stdin JSON.
87
+ * @internal
88
+ */
89
+ async function executeDiscoveredHooks(hooks, payload, hookType, cwd, timeout) {
90
+ const payloadJson = JSON.stringify(payload);
91
+ const executedHooks = [];
92
+ for (const hook of hooks) {
93
+ const result = await new Promise((resolve) => {
94
+ const child = (0, node_child_process_1.spawn)("bash", [hook.path], {
95
+ cwd,
96
+ stdio: ["pipe", "pipe", "pipe"],
97
+ timeout,
98
+ });
99
+ child.stdin.write(payloadJson);
100
+ child.stdin.end();
101
+ let timedOut = false;
102
+ const timeoutHandle = setTimeout(() => {
103
+ timedOut = true;
104
+ child.kill("SIGTERM");
105
+ }, timeout);
106
+ child.on("error", () => {
107
+ clearTimeout(timeoutHandle);
108
+ resolve({ success: false, exitCode: null });
109
+ });
110
+ child.on("close", (exitCode) => {
111
+ clearTimeout(timeoutHandle);
112
+ if (timedOut) {
113
+ resolve({ success: false, exitCode: null });
114
+ }
115
+ else {
116
+ resolve({ success: exitCode === 0, exitCode });
117
+ }
118
+ });
119
+ });
120
+ executedHooks.push({
121
+ hookPath: hook.path,
122
+ hookName: hook.name,
123
+ hookLocation: hook.location,
124
+ status: result.success ? "success" : "failed",
125
+ exitCode: result.exitCode ?? undefined,
126
+ });
127
+ }
128
+ const allSucceeded = executedHooks.every((h) => h.status === "success");
129
+ return {
130
+ hookType,
131
+ success: allSucceeded,
132
+ error: allSucceeded ? undefined : `One or more discovered hooks failed for ${hookType}`,
133
+ executedHooks,
134
+ };
135
+ }
47
136
  /**
48
137
  * Find `plugins/babysitter/hooks/hook-dispatcher.sh` by walking up from cwd.
49
138
  * This allows running from nested projects/fixtures inside a mono-repo.
@@ -78,6 +167,11 @@ async function callHook(options) {
78
167
  const { hookType, payload, cwd = process.cwd(), timeout = defaults_1.DEFAULTS.hookTimeout, throwOnFailure = false, } = options;
79
168
  const dispatcherPath = findHookDispatcherPath(cwd);
80
169
  if (!dispatcherPath) {
170
+ // Fall back to filesystem-discovered hooks
171
+ const discoveredHooks = await discoverHooks(hookType, cwd);
172
+ if (discoveredHooks.length > 0) {
173
+ return executeDiscoveredHooks(discoveredHooks, payload, hookType, cwd, timeout);
174
+ }
81
175
  return {
82
176
  hookType,
83
177
  success: false,
@@ -2,7 +2,8 @@
2
2
  * Hook System API
3
3
  * Public interface for calling hooks from process files
4
4
  */
5
- export { callHook, } from "./dispatcher";
5
+ export { callHook, discoverHooks, } from "./dispatcher";
6
+ export type { DiscoveredHook } from "./dispatcher";
6
7
  export type { HookType, HookResult, HookExecutionResult, HookPayload, HookDispatcherOptions, OnRunStartPayload, OnRunCompletePayload, OnRunFailPayload, OnTaskStartPayload, OnTaskCompletePayload, OnStepDispatchPayload, OnIterationStartPayload, OnIterationEndPayload, OnBreakpointPayload, PreCommitPayload, PreBranchPayload, PostPlanningPayload, OnScorePayload, } from "./types";
7
8
  /**
8
9
  * Helper function to create hook payloads with proper typing
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,GACT,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC9D,OAAO,EAAE,CAAC,GACT,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAK3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC9D,OAAO,EAAE,CAAC,GACT,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAK3B"}
@@ -4,10 +4,11 @@
4
4
  * Public interface for calling hooks from process files
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.callHook = void 0;
7
+ exports.discoverHooks = exports.callHook = void 0;
8
8
  exports.createHookPayload = createHookPayload;
9
9
  var dispatcher_1 = require("./dispatcher");
10
10
  Object.defineProperty(exports, "callHook", { enumerable: true, get: function () { return dispatcher_1.callHook; } });
11
+ Object.defineProperty(exports, "discoverHooks", { enumerable: true, get: function () { return dispatcher_1.discoverHooks; } });
11
12
  /**
12
13
  * Helper function to create hook payloads with proper typing
13
14
  */
package/dist/index.d.ts CHANGED
@@ -13,5 +13,6 @@ export * from "./plugins";
13
13
  export * from "./interaction";
14
14
  export * from "./prompts";
15
15
  export * from "./logging";
16
+ export * from "./cost/index";
16
17
  export * as anycli from "./anycli/index";
17
18
  //# sourceMappingURL=index.d.ts.map