@interf/compiler 0.1.8

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 (145) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +1008 -0
  3. package/TRADEMARKS.md +19 -0
  4. package/dist/bin.d.ts +3 -0
  5. package/dist/bin.d.ts.map +1 -0
  6. package/dist/bin.js +30 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/commands/benchmark.d.ts +3 -0
  9. package/dist/commands/benchmark.d.ts.map +1 -0
  10. package/dist/commands/benchmark.js +400 -0
  11. package/dist/commands/benchmark.js.map +1 -0
  12. package/dist/commands/compile.d.ts +3 -0
  13. package/dist/commands/compile.d.ts.map +1 -0
  14. package/dist/commands/compile.js +139 -0
  15. package/dist/commands/compile.js.map +1 -0
  16. package/dist/commands/create.d.ts +49 -0
  17. package/dist/commands/create.d.ts.map +1 -0
  18. package/dist/commands/create.js +813 -0
  19. package/dist/commands/create.js.map +1 -0
  20. package/dist/commands/default.d.ts +3 -0
  21. package/dist/commands/default.d.ts.map +1 -0
  22. package/dist/commands/default.js +52 -0
  23. package/dist/commands/default.js.map +1 -0
  24. package/dist/commands/doctor.d.ts +3 -0
  25. package/dist/commands/doctor.d.ts.map +1 -0
  26. package/dist/commands/doctor.js +146 -0
  27. package/dist/commands/doctor.js.map +1 -0
  28. package/dist/commands/init.d.ts +3 -0
  29. package/dist/commands/init.d.ts.map +1 -0
  30. package/dist/commands/init.js +235 -0
  31. package/dist/commands/init.js.map +1 -0
  32. package/dist/commands/list.d.ts +3 -0
  33. package/dist/commands/list.d.ts.map +1 -0
  34. package/dist/commands/list.js +32 -0
  35. package/dist/commands/list.js.map +1 -0
  36. package/dist/commands/reset.d.ts +3 -0
  37. package/dist/commands/reset.d.ts.map +1 -0
  38. package/dist/commands/reset.js +136 -0
  39. package/dist/commands/reset.js.map +1 -0
  40. package/dist/commands/status.d.ts +3 -0
  41. package/dist/commands/status.d.ts.map +1 -0
  42. package/dist/commands/status.js +67 -0
  43. package/dist/commands/status.js.map +1 -0
  44. package/dist/commands/verify.d.ts +3 -0
  45. package/dist/commands/verify.d.ts.map +1 -0
  46. package/dist/commands/verify.js +112 -0
  47. package/dist/commands/verify.js.map +1 -0
  48. package/dist/index.d.ts +30 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +18 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/lib/agents.d.ts +59 -0
  53. package/dist/lib/agents.d.ts.map +1 -0
  54. package/dist/lib/agents.js +760 -0
  55. package/dist/lib/agents.js.map +1 -0
  56. package/dist/lib/benchmark.d.ts +30 -0
  57. package/dist/lib/benchmark.d.ts.map +1 -0
  58. package/dist/lib/benchmark.js +325 -0
  59. package/dist/lib/benchmark.js.map +1 -0
  60. package/dist/lib/config.d.ts +6 -0
  61. package/dist/lib/config.d.ts.map +1 -0
  62. package/dist/lib/config.js +15 -0
  63. package/dist/lib/config.js.map +1 -0
  64. package/dist/lib/discovery.d.ts +8 -0
  65. package/dist/lib/discovery.d.ts.map +1 -0
  66. package/dist/lib/discovery.js +80 -0
  67. package/dist/lib/discovery.js.map +1 -0
  68. package/dist/lib/executors.d.ts +33 -0
  69. package/dist/lib/executors.d.ts.map +1 -0
  70. package/dist/lib/executors.js +44 -0
  71. package/dist/lib/executors.js.map +1 -0
  72. package/dist/lib/filesystem.d.ts +4 -0
  73. package/dist/lib/filesystem.d.ts.map +1 -0
  74. package/dist/lib/filesystem.js +61 -0
  75. package/dist/lib/filesystem.js.map +1 -0
  76. package/dist/lib/interf.d.ts +37 -0
  77. package/dist/lib/interf.d.ts.map +1 -0
  78. package/dist/lib/interf.js +1104 -0
  79. package/dist/lib/interf.js.map +1 -0
  80. package/dist/lib/local-workflows.d.ts +35 -0
  81. package/dist/lib/local-workflows.d.ts.map +1 -0
  82. package/dist/lib/local-workflows.js +149 -0
  83. package/dist/lib/local-workflows.js.map +1 -0
  84. package/dist/lib/parse.d.ts +9 -0
  85. package/dist/lib/parse.d.ts.map +1 -0
  86. package/dist/lib/parse.js +51 -0
  87. package/dist/lib/parse.js.map +1 -0
  88. package/dist/lib/registry.d.ts +28 -0
  89. package/dist/lib/registry.d.ts.map +1 -0
  90. package/dist/lib/registry.js +80 -0
  91. package/dist/lib/registry.js.map +1 -0
  92. package/dist/lib/runtime.d.ts +59 -0
  93. package/dist/lib/runtime.d.ts.map +1 -0
  94. package/dist/lib/runtime.js +615 -0
  95. package/dist/lib/runtime.js.map +1 -0
  96. package/dist/lib/schema.d.ts +705 -0
  97. package/dist/lib/schema.d.ts.map +1 -0
  98. package/dist/lib/schema.js +443 -0
  99. package/dist/lib/schema.js.map +1 -0
  100. package/dist/lib/state.d.ts +49 -0
  101. package/dist/lib/state.d.ts.map +1 -0
  102. package/dist/lib/state.js +633 -0
  103. package/dist/lib/state.js.map +1 -0
  104. package/dist/lib/summarize-plan.d.ts +16 -0
  105. package/dist/lib/summarize-plan.d.ts.map +1 -0
  106. package/dist/lib/summarize-plan.js +112 -0
  107. package/dist/lib/summarize-plan.js.map +1 -0
  108. package/dist/lib/user-config.d.ts +6 -0
  109. package/dist/lib/user-config.d.ts.map +1 -0
  110. package/dist/lib/user-config.js +16 -0
  111. package/dist/lib/user-config.js.map +1 -0
  112. package/dist/lib/validate.d.ts +149 -0
  113. package/dist/lib/validate.d.ts.map +1 -0
  114. package/dist/lib/validate.js +838 -0
  115. package/dist/lib/validate.js.map +1 -0
  116. package/dist/lib/workflow-definitions.d.ts +79 -0
  117. package/dist/lib/workflow-definitions.d.ts.map +1 -0
  118. package/dist/lib/workflow-definitions.js +565 -0
  119. package/dist/lib/workflow-definitions.js.map +1 -0
  120. package/dist/lib/workflows.d.ts +125 -0
  121. package/dist/lib/workflows.d.ts.map +1 -0
  122. package/dist/lib/workflows.js +1107 -0
  123. package/dist/lib/workflows.js.map +1 -0
  124. package/package.json +73 -0
  125. package/skills/benchmark/SKILL.md +129 -0
  126. package/skills/interface/analyze/SKILL.md +140 -0
  127. package/skills/interface/compile/SKILL.md +153 -0
  128. package/skills/interface/compile/references/output-format.md +48 -0
  129. package/skills/interface/create/SKILL.md +264 -0
  130. package/skills/interface/create/references/compile-plan-format.md +109 -0
  131. package/skills/interface/create/references/workflows.md +50 -0
  132. package/skills/interface/query/SKILL.md +34 -0
  133. package/skills/interface/retrieve/SKILL.md +166 -0
  134. package/skills/knowledge-base/compile/SKILL.md +220 -0
  135. package/skills/knowledge-base/compile/references/output-format.md +48 -0
  136. package/skills/knowledge-base/compile/references/stage-claims.md +60 -0
  137. package/skills/knowledge-base/compile/references/stage-entities.md +46 -0
  138. package/skills/knowledge-base/query/SKILL.md +33 -0
  139. package/skills/knowledge-base/summarize/SKILL.md +122 -0
  140. package/skills/workflow/create/SKILL.md +126 -0
  141. package/templates/interface/README.md +158 -0
  142. package/templates/interface/interfaces.md +99 -0
  143. package/templates/knowledge-base/README.md +138 -0
  144. package/templates/knowledge-base/interfignore +19 -0
  145. package/templates/knowledge-base/registry.md +118 -0
@@ -0,0 +1,760 @@
1
+ import { appendFileSync, existsSync, mkdirSync, cpSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { homedir, tmpdir } from "node:os";
4
+ import { execFileSync, spawn, spawnSync } from "node:child_process";
5
+ import { SKILLS_DIR } from "./config.js";
6
+ import { loadUserConfig } from "./user-config.js";
7
+ import chalk from "chalk";
8
+ const SHOW_AGENT_TOOL_EVENTS = process.env.INTERF_DEBUG_AGENT_TOOLS === "1";
9
+ const AGENTS = [
10
+ {
11
+ name: "claude-code",
12
+ displayName: "Claude Code",
13
+ skillsDir: join(homedir(), ".claude", "skills"),
14
+ command: "claude",
15
+ },
16
+ {
17
+ name: "codex",
18
+ displayName: "Codex",
19
+ skillsDir: join(homedir(), ".codex", "skills"),
20
+ command: "codex",
21
+ },
22
+ {
23
+ name: "cursor",
24
+ displayName: "Cursor",
25
+ skillsDir: join(homedir(), ".cursor", "skills"),
26
+ command: "cursor",
27
+ },
28
+ ];
29
+ const CODEX_NOISE_PATTERNS = [
30
+ /codex_core_skills::loader: failed to stat skills entry .*\/(interf-[^ ]+|knowledge-base\/[^ ]+|interface\/[^ ]+) \(symlink\): No such file or directory/,
31
+ /codex_core::codex: failed to load skill .*\/gstack\/SKILL\.md: invalid description: exceeds maximum length of 1024 characters/,
32
+ ];
33
+ const SKILLS_LIST_ARGS = ["-y", "skills", "list", "-g", "--json"];
34
+ const VISIBLE_STATUS_PREFIXES = ["STATUS:", "DONE:", "BLOCKED:", "ERROR:"];
35
+ let installedSkillsCache = null;
36
+ const autoRefreshAttemptedAgents = new Set();
37
+ const successfulPreflightAgents = new Set();
38
+ export function detectAgents() {
39
+ return AGENTS.filter((a) => {
40
+ const dir = join(homedir(), `.${a.name === "claude-code" ? "claude" : a.name}`);
41
+ return existsSync(dir);
42
+ });
43
+ }
44
+ export function commandExists(command) {
45
+ try {
46
+ execFileSync("which", [command], { stdio: "ignore" });
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ export function supportsAutomatedRuns(agent) {
54
+ return agent.name === "claude-code" || agent.name === "codex";
55
+ }
56
+ export function resolveAgent() {
57
+ const config = loadUserConfig();
58
+ if (config) {
59
+ const configured = AGENTS.find((a) => a.command === config.agentCommand) ??
60
+ AGENTS.find((a) => a.name === config.agent) ??
61
+ AGENTS.find((a) => a.displayName === config.agent);
62
+ if (!configured) {
63
+ return {
64
+ agent: null,
65
+ error: `Configured agent "${config.agent}" is not supported by this Interf build. Run \`interf init\` to choose a supported agent.`,
66
+ };
67
+ }
68
+ if (!commandExists(configured.command)) {
69
+ return {
70
+ agent: null,
71
+ error: `Configured agent "${configured.displayName}" is not available in PATH. Run \`interf init\` to reconfigure.`,
72
+ };
73
+ }
74
+ if (!supportsAutomatedRuns(configured)) {
75
+ return {
76
+ agent: null,
77
+ error: `Configured agent "${configured.displayName}" is detected, but this Interf build can only automate Claude Code or Codex. Run \`interf init\` and choose one of those agents.`,
78
+ };
79
+ }
80
+ return { agent: configured };
81
+ }
82
+ const detected = detectAgents().filter((a) => commandExists(a.command) && supportsAutomatedRuns(a));
83
+ if (detected.length === 0) {
84
+ return {
85
+ agent: null,
86
+ error: "No coding agent detected. Install Claude Code or Codex.",
87
+ };
88
+ }
89
+ if (detected.length > 1) {
90
+ return {
91
+ agent: null,
92
+ error: "Multiple coding agents detected but no default is configured. Run `interf init` to choose which agent compile commands should use.",
93
+ };
94
+ }
95
+ return { agent: detected[0] };
96
+ }
97
+ export function installSkills(agents) {
98
+ const targets = agents ?? detectAgents();
99
+ const results = [];
100
+ if (!existsSync(SKILLS_DIR))
101
+ return results;
102
+ installedSkillsCache = null;
103
+ const skills = listBundledInterfSkills();
104
+ for (const skill of skills) {
105
+ for (const agent of targets) {
106
+ try {
107
+ const target = join(agent.skillsDir, skill);
108
+ mkdirSync(dirname(target), { recursive: true });
109
+ cpSync(join(SKILLS_DIR, skill), target, { recursive: true });
110
+ results.push({ agent: agent.displayName, skill, success: true });
111
+ }
112
+ catch {
113
+ results.push({ agent: agent.displayName, skill, success: false });
114
+ }
115
+ }
116
+ }
117
+ return results;
118
+ }
119
+ export function refreshInterfSkills(agent) {
120
+ installedSkillsCache = null;
121
+ try {
122
+ execFileSync("npx", ["-y", "skills", "add", "--global", "--yes", SKILLS_DIR], { stdio: "ignore" });
123
+ }
124
+ catch {
125
+ // Ignore and fall back to direct installation below.
126
+ }
127
+ installSkills([agent]);
128
+ installedSkillsCache = null;
129
+ const health = inspectInterfSkills(agent);
130
+ return health.missing.length === 0 && health.stale.length === 0 && health.wrongAgent.length === 0;
131
+ }
132
+ export function inspectInterfSkills(agent) {
133
+ const health = { missing: [], stale: [], wrongAgent: [] };
134
+ const bundledSkills = listBundledInterfSkills();
135
+ if (bundledSkills.length === 0)
136
+ return health;
137
+ const installed = listInstalledSkills();
138
+ for (const skillName of bundledSkills) {
139
+ const localStatus = getAgentLocalSkillStatus(agent, skillName);
140
+ if (localStatus.current) {
141
+ continue;
142
+ }
143
+ if (localStatus.present) {
144
+ health.stale.push(skillName);
145
+ continue;
146
+ }
147
+ if (installed) {
148
+ const entry = installed.find((item) => item.name === skillName);
149
+ if (!entry) {
150
+ health.missing.push(skillName);
151
+ continue;
152
+ }
153
+ const agents = entry.agents ?? [];
154
+ if (agents.length > 0 && !agents.includes(agent.displayName)) {
155
+ health.wrongAgent.push(skillName);
156
+ continue;
157
+ }
158
+ if (!entry.path || !bundledSkillMatchesInstalledPath(skillName, join(entry.path, "SKILL.md"))) {
159
+ health.stale.push(skillName);
160
+ }
161
+ continue;
162
+ }
163
+ const candidatePaths = installedSkillCandidatePaths(agent, skillName);
164
+ if (candidatePaths.length === 0) {
165
+ health.missing.push(skillName);
166
+ continue;
167
+ }
168
+ const hasCurrentCopy = candidatePaths.some((path) => bundledSkillMatchesInstalledPath(skillName, path));
169
+ if (!hasCurrentCopy) {
170
+ health.stale.push(skillName);
171
+ }
172
+ }
173
+ return health;
174
+ }
175
+ function appendAgentEventLog(eventLogPath, event) {
176
+ if (!eventLogPath)
177
+ return;
178
+ mkdirSync(dirname(eventLogPath), { recursive: true });
179
+ appendFileSync(eventLogPath, `${JSON.stringify({
180
+ timestamp: new Date().toISOString(),
181
+ ...event,
182
+ })}\n`);
183
+ }
184
+ function appendAgentStatusLog(statusLogPath, text) {
185
+ if (!statusLogPath)
186
+ return;
187
+ mkdirSync(dirname(statusLogPath), { recursive: true });
188
+ appendFileSync(statusLogPath, `${new Date().toISOString()} ${text}\n`);
189
+ }
190
+ function emitVisibleAgentText(text, statusLogPath, lastVisibleText) {
191
+ if (lastVisibleText?.terminated) {
192
+ return false;
193
+ }
194
+ let displayed = false;
195
+ const lines = text
196
+ .split(/\r?\n/)
197
+ .map((line) => line.trim())
198
+ .filter((line) => shouldDisplayAgentText(line));
199
+ for (const line of lines) {
200
+ if (lastVisibleText && lastVisibleText.current === line) {
201
+ continue;
202
+ }
203
+ console.log(chalk.dim(` ${line}`));
204
+ appendAgentStatusLog(statusLogPath, line);
205
+ if (lastVisibleText) {
206
+ lastVisibleText.current = line;
207
+ if (isTerminalVisibleStatus(line)) {
208
+ lastVisibleText.terminated = true;
209
+ }
210
+ }
211
+ displayed = true;
212
+ }
213
+ return displayed;
214
+ }
215
+ export function spawnAgent(agent, dirPath, prompt, options = {}) {
216
+ const requiredSkill = extractRequiredSkill(prompt);
217
+ const skillError = ensureRequiredSkill(agent, requiredSkill);
218
+ if (skillError) {
219
+ console.log(chalk.red(` ${skillError}`));
220
+ return Promise.resolve(1);
221
+ }
222
+ return new Promise((resolve, reject) => {
223
+ const args = buildAgentArgs(agent, prompt, options.executionProfile);
224
+ appendAgentEventLog(options.eventLogPath, {
225
+ type: "spawn",
226
+ command: agent.command,
227
+ args,
228
+ cwd: dirPath,
229
+ });
230
+ const launchLine = `Launching ${agent.displayName}...`;
231
+ console.log(chalk.dim(` ${launchLine}`));
232
+ appendAgentStatusLog(options.statusLogPath, launchLine);
233
+ const proc = spawn(agent.command, args, {
234
+ cwd: dirPath,
235
+ stdio: ["ignore", "pipe", "pipe"],
236
+ });
237
+ const timeoutMs = options.executionProfile?.timeoutMs ?? null;
238
+ // Parse streaming NDJSON events and show progress
239
+ let buffer = "";
240
+ let stderrBuffer = "";
241
+ let lastRawOutputAt = Date.now();
242
+ let lastVisibleOutputAt = Date.now();
243
+ let lastHeartbeatAt = 0;
244
+ let sawRawOutput = false;
245
+ let sawVisibleEvent = false;
246
+ let timedOut = false;
247
+ let completionSatisfied = false;
248
+ let failureStatus = null;
249
+ const lastVisibleText = { current: null };
250
+ const heartbeat = setInterval(() => {
251
+ const now = Date.now();
252
+ const minQuietMs = sawVisibleEvent ? 15000 : 8000;
253
+ const minHeartbeatGapMs = sawVisibleEvent ? 30000 : 12000;
254
+ if (now - lastVisibleOutputAt < minQuietMs)
255
+ return;
256
+ if (now - lastHeartbeatAt < minHeartbeatGapMs)
257
+ return;
258
+ lastHeartbeatAt = now;
259
+ const seconds = Math.floor((now - lastVisibleOutputAt) / 1000);
260
+ const label = sawVisibleEvent
261
+ ? `${agent.displayName} is still running... (${seconds}s since last output)`
262
+ : sawRawOutput
263
+ ? `${agent.displayName} is active, but has not emitted visible progress yet... (${seconds}s)`
264
+ : `${agent.displayName} started, but has not emitted visible progress yet... (${seconds}s)`;
265
+ console.log(chalk.dim(` ${label}`));
266
+ }, 5000);
267
+ const forwardSignal = () => {
268
+ proc.kill("SIGINT");
269
+ };
270
+ const timeout = typeof timeoutMs === "number" && timeoutMs > 0
271
+ ? setTimeout(() => {
272
+ timedOut = true;
273
+ appendAgentEventLog(options.eventLogPath, {
274
+ type: "timeout",
275
+ timeout_ms: timeoutMs,
276
+ });
277
+ console.log(chalk.red(` ${agent.displayName} timed out after ${Math.round(timeoutMs / 1000)}s.`));
278
+ proc.kill("SIGINT");
279
+ }, timeoutMs)
280
+ : null;
281
+ const completionPoll = options.completionCheck
282
+ ? setInterval(() => {
283
+ if (timedOut || completionSatisfied)
284
+ return;
285
+ try {
286
+ if (!options.completionCheck?.())
287
+ return;
288
+ completionSatisfied = true;
289
+ appendAgentEventLog(options.eventLogPath, {
290
+ type: "completion-check-satisfied",
291
+ });
292
+ proc.kill("SIGINT");
293
+ }
294
+ catch {
295
+ // Ignore completion check failures and let the agent continue.
296
+ }
297
+ }, 1000)
298
+ : null;
299
+ process.once("SIGINT", forwardSignal);
300
+ proc.stdout?.on("data", (chunk) => {
301
+ lastRawOutputAt = Date.now();
302
+ sawRawOutput = true;
303
+ buffer += chunk.toString();
304
+ const lines = buffer.split("\n");
305
+ buffer = lines.pop() ?? "";
306
+ for (const line of lines) {
307
+ if (!line.trim())
308
+ continue;
309
+ appendAgentEventLog(options.eventLogPath, {
310
+ type: "stdout",
311
+ line,
312
+ });
313
+ try {
314
+ const event = JSON.parse(line);
315
+ failureStatus ||= extractAgentFailureStatus(event);
316
+ if (displayEvent(event, options.statusLogPath, lastVisibleText)) {
317
+ sawVisibleEvent = true;
318
+ lastVisibleOutputAt = Date.now();
319
+ }
320
+ }
321
+ catch {
322
+ if (emitVisibleAgentText(line, options.statusLogPath, lastVisibleText)) {
323
+ sawVisibleEvent = true;
324
+ lastVisibleOutputAt = Date.now();
325
+ }
326
+ }
327
+ }
328
+ });
329
+ proc.stderr?.on("data", (chunk) => {
330
+ stderrBuffer += chunk.toString();
331
+ const lines = stderrBuffer.split("\n");
332
+ stderrBuffer = lines.pop() ?? "";
333
+ for (const line of lines) {
334
+ if (!line.trim())
335
+ continue;
336
+ appendAgentEventLog(options.eventLogPath, {
337
+ type: "stderr",
338
+ line,
339
+ });
340
+ if (CODEX_NOISE_PATTERNS.some((pattern) => pattern.test(line)))
341
+ continue;
342
+ lastRawOutputAt = Date.now();
343
+ sawRawOutput = true;
344
+ lastVisibleOutputAt = Date.now();
345
+ process.stderr.write(line + "\n");
346
+ sawVisibleEvent = true;
347
+ }
348
+ });
349
+ proc.on("close", (code) => {
350
+ clearInterval(heartbeat);
351
+ if (timeout)
352
+ clearTimeout(timeout);
353
+ if (completionPoll)
354
+ clearInterval(completionPoll);
355
+ process.removeListener("SIGINT", forwardSignal);
356
+ const finalCode = timedOut ? 124 : completionSatisfied ? 0 : failureStatus ? 1 : (code ?? 0);
357
+ appendAgentEventLog(options.eventLogPath, {
358
+ type: "close",
359
+ code: finalCode,
360
+ failure_status: failureStatus,
361
+ });
362
+ resolve(finalCode);
363
+ });
364
+ proc.on("error", () => {
365
+ clearInterval(heartbeat);
366
+ if (timeout)
367
+ clearTimeout(timeout);
368
+ if (completionPoll)
369
+ clearInterval(completionPoll);
370
+ process.removeListener("SIGINT", forwardSignal);
371
+ appendAgentEventLog(options.eventLogPath, {
372
+ type: "error",
373
+ message: `Failed to start ${agent.displayName}`,
374
+ });
375
+ console.log(chalk.red(` Failed to start ${agent.displayName}.`));
376
+ resolve(1);
377
+ });
378
+ });
379
+ }
380
+ export function buildAgentArgs(agent, prompt, executionProfile = {}) {
381
+ if (agent.name === "claude-code") {
382
+ const args = [
383
+ "-p",
384
+ prompt,
385
+ "--output-format",
386
+ "stream-json",
387
+ "--verbose",
388
+ "--allowedTools",
389
+ "Read,Write,Edit,Bash,Grep,Glob,Agent",
390
+ ];
391
+ if (executionProfile.model) {
392
+ args.push("--model", executionProfile.model);
393
+ }
394
+ if (executionProfile.effort) {
395
+ args.push("--effort", executionProfile.effort);
396
+ }
397
+ return args;
398
+ }
399
+ if (agent.name === "codex") {
400
+ const args = [
401
+ "exec",
402
+ "--json",
403
+ "-s",
404
+ "workspace-write",
405
+ "--skip-git-repo-check",
406
+ ];
407
+ if (executionProfile.model) {
408
+ args.push("--model", executionProfile.model);
409
+ }
410
+ if (executionProfile.profile) {
411
+ args.push("--profile", executionProfile.profile);
412
+ }
413
+ if (executionProfile.effort) {
414
+ args.push("-c", `model_reasoning_effort=${JSON.stringify(executionProfile.effort)}`);
415
+ }
416
+ args.push(prompt);
417
+ return args;
418
+ }
419
+ throw new Error(`Agent "${agent.displayName}" is not yet supported for automated compile runs.`);
420
+ }
421
+ export function buildAgentPreflightPrompt() {
422
+ return [
423
+ "Interf doctor preflight.",
424
+ 'Reply with exactly one line: "DONE: Interf doctor ok".',
425
+ "Do not use tools.",
426
+ "Do not read files.",
427
+ "Do not write files.",
428
+ ].join(" ");
429
+ }
430
+ export function runAgentPreflight(agent, options = {}) {
431
+ const timeoutMs = options.timeoutMs ?? 30000;
432
+ const spawnSyncImpl = options.spawnSyncImpl ?? spawnSync;
433
+ const dirPath = mkdtempSync(join(tmpdir(), "interf-doctor-"));
434
+ try {
435
+ const result = spawnSyncImpl(agent.command, buildAgentArgs(agent, buildAgentPreflightPrompt()), {
436
+ cwd: dirPath,
437
+ encoding: "utf8",
438
+ timeout: timeoutMs,
439
+ });
440
+ const stdout = typeof result.stdout === "string" ? result.stdout.trim() : "";
441
+ const stderr = sanitizeAgentStderr(typeof result.stderr === "string" ? result.stderr : "");
442
+ const timedOut = result.error?.name === "Error" && result.error.message.includes("timed out");
443
+ if (result.error) {
444
+ return {
445
+ ok: false,
446
+ code: typeof result.status === "number" ? result.status : null,
447
+ timedOut,
448
+ stdout,
449
+ stderr,
450
+ error: result.error.message,
451
+ };
452
+ }
453
+ return {
454
+ ok: result.status === 0,
455
+ code: typeof result.status === "number" ? result.status : null,
456
+ timedOut: false,
457
+ stdout,
458
+ stderr,
459
+ error: result.status === 0 ? null : `Exited with code ${result.status ?? "unknown"}`,
460
+ };
461
+ }
462
+ finally {
463
+ rmSync(dirPath, { recursive: true, force: true });
464
+ }
465
+ }
466
+ export function ensureAgentAutomatedRunReady(agent, options = {}) {
467
+ if (!options.force && successfulPreflightAgents.has(agent.name)) {
468
+ return { ok: true };
469
+ }
470
+ const runPreflight = options.runPreflight ?? runAgentPreflight;
471
+ const result = runPreflight(agent);
472
+ if (!result.ok) {
473
+ const details = [result.error, result.stderr].filter(Boolean).join(" ");
474
+ return {
475
+ ok: false,
476
+ error: `${agent.displayName} failed Interf executor preflight. Run \`interf doctor --live\` for details.${details ? ` ${details}` : ""}`,
477
+ };
478
+ }
479
+ successfulPreflightAgents.add(agent.name);
480
+ return { ok: true };
481
+ }
482
+ function displayEvent(event, statusLogPath, lastVisibleText) {
483
+ const type = event.type;
484
+ let displayed = false;
485
+ // Tool use — show tool name + brief input
486
+ if (type === "tool_use" && SHOW_AGENT_TOOL_EVENTS) {
487
+ const name = event.name ?? "unknown";
488
+ const input = event.input;
489
+ let detail = "";
490
+ if (input) {
491
+ // Show file path for Read/Write/Edit/Glob
492
+ const path = (input.file_path ?? input.path ?? input.pattern ?? input.command ?? "");
493
+ if (path)
494
+ detail = chalk.dim(` ${path.slice(0, 80)}`);
495
+ }
496
+ console.log(chalk.blue(` → ${name}`) + detail);
497
+ displayed = true;
498
+ }
499
+ // Tool result — skip (too verbose)
500
+ // Assistant text — show thinking/status messages
501
+ if (type === "assistant") {
502
+ const msg = event.message;
503
+ const content = msg?.content;
504
+ if (Array.isArray(content)) {
505
+ for (const block of content) {
506
+ if (block.type === "text" && block.text) {
507
+ const text = block.text.trim();
508
+ displayed = emitVisibleAgentText(text, statusLogPath, lastVisibleText) || displayed;
509
+ }
510
+ if (block.type === "tool_use" && SHOW_AGENT_TOOL_EVENTS) {
511
+ const name = block.name;
512
+ const input = block.input;
513
+ let detail = "";
514
+ if (input) {
515
+ const path = (input.file_path ?? input.path ?? input.pattern ?? input.command ?? "");
516
+ if (path)
517
+ detail = chalk.dim(` ${path.slice(0, 80)}`);
518
+ }
519
+ console.log(chalk.blue(` → ${name}`) + detail);
520
+ displayed = true;
521
+ }
522
+ }
523
+ }
524
+ }
525
+ if (type === "item.started" || type === "item.completed") {
526
+ const item = event.item;
527
+ if (item) {
528
+ displayed = displayItemEvent(type, item, statusLogPath, lastVisibleText) || displayed;
529
+ }
530
+ }
531
+ // Result — show final summary
532
+ if (type === "result") {
533
+ const result = event.result;
534
+ const subtype = event.subtype;
535
+ if (result && subtype !== "tool_result") {
536
+ const lines = result.split("\n");
537
+ for (const line of lines) {
538
+ displayed = emitVisibleAgentText(line, statusLogPath, lastVisibleText) || displayed;
539
+ }
540
+ }
541
+ }
542
+ const message = pickString(event.message) ??
543
+ pickString(event.text) ??
544
+ pickString(event.content) ??
545
+ pickString(event.output) ??
546
+ pickString(event.summary);
547
+ if (message && type !== "assistant" && type !== "result") {
548
+ const text = message.trim();
549
+ displayed = emitVisibleAgentText(text, statusLogPath, lastVisibleText) || displayed;
550
+ }
551
+ return displayed;
552
+ }
553
+ function pickString(value) {
554
+ if (typeof value === "string")
555
+ return value;
556
+ if (!Array.isArray(value))
557
+ return null;
558
+ for (const entry of value) {
559
+ if (typeof entry === "string")
560
+ return entry;
561
+ if (entry && typeof entry === "object" && "text" in entry && typeof entry.text === "string") {
562
+ return entry.text;
563
+ }
564
+ }
565
+ return null;
566
+ }
567
+ function displayItemEvent(type, item, statusLogPath, lastVisibleText) {
568
+ const itemType = item.type;
569
+ if (itemType === "agent_message") {
570
+ const text = item.text?.trim();
571
+ return text ? emitVisibleAgentText(text, statusLogPath, lastVisibleText) : false;
572
+ }
573
+ if (!SHOW_AGENT_TOOL_EVENTS) {
574
+ return false;
575
+ }
576
+ const title = pickString(item.title) ?? pickString(item.name);
577
+ const input = item.input;
578
+ const detail = input ? describeInput(input) : "";
579
+ if (type === "item.started" && title) {
580
+ console.log(chalk.blue(` → ${title}`) + detail);
581
+ return true;
582
+ }
583
+ if (type === "item.completed" && title) {
584
+ console.log(chalk.blue(` → ${title}`) + detail);
585
+ return true;
586
+ }
587
+ return false;
588
+ }
589
+ function describeInput(input) {
590
+ const path = (input.file_path ?? input.path ?? input.pattern ?? input.command ?? "");
591
+ return path ? chalk.dim(` ${path.slice(0, 80)}`) : "";
592
+ }
593
+ function shouldDisplayAgentText(text) {
594
+ if (text.length === 0 || text.length >= 300)
595
+ return false;
596
+ return VISIBLE_STATUS_PREFIXES.some((prefix) => text.startsWith(prefix));
597
+ }
598
+ function isTerminalVisibleStatus(text) {
599
+ return text.startsWith("DONE:") || text.startsWith("BLOCKED:") || text.startsWith("ERROR:");
600
+ }
601
+ export function extractAgentFailureStatus(event) {
602
+ const candidates = [];
603
+ const directMessage = pickString(event.message) ??
604
+ pickString(event.text) ??
605
+ pickString(event.content) ??
606
+ pickString(event.output) ??
607
+ pickString(event.summary);
608
+ if (directMessage)
609
+ candidates.push(directMessage);
610
+ const item = event.item;
611
+ if (item && typeof item === "object") {
612
+ const itemText = pickString(item.text);
613
+ if (itemText)
614
+ candidates.push(itemText);
615
+ }
616
+ for (const candidate of candidates) {
617
+ const text = candidate.trim();
618
+ if (text.startsWith("BLOCKED:") || text.startsWith("ERROR:")) {
619
+ return text;
620
+ }
621
+ }
622
+ return null;
623
+ }
624
+ function sanitizeAgentStderr(stderr) {
625
+ return stderr
626
+ .split("\n")
627
+ .filter((line) => line.trim().length > 0)
628
+ .filter((line) => !CODEX_NOISE_PATTERNS.some((pattern) => pattern.test(line)))
629
+ .join("\n")
630
+ .trim();
631
+ }
632
+ function validateInstalledSkill(agent, skillName) {
633
+ if (!skillName)
634
+ return null;
635
+ const localStatus = getAgentLocalSkillStatus(agent, skillName);
636
+ if (localStatus.current)
637
+ return null;
638
+ if (localStatus.present) {
639
+ return `Skill "${skillName}" is installed, but outdated. Run \`interf init\` and refresh Interf skills before syncing.`;
640
+ }
641
+ const installed = listInstalledSkills();
642
+ const bundledPath = join(SKILLS_DIR, skillName, "SKILL.md");
643
+ if (installed) {
644
+ const entry = installed.find((item) => item.name === skillName);
645
+ if (!entry) {
646
+ return `Skill "${skillName}" is not installed. Run \`interf init\` to install current Interf skills before syncing.`;
647
+ }
648
+ const agents = entry.agents ?? [];
649
+ if (agents.length > 0 && !agents.includes(agent.displayName)) {
650
+ return `Skill "${skillName}" is installed, but not for ${agent.displayName}. Run \`interf init\` and install skills for that agent before syncing.`;
651
+ }
652
+ if (existsSync(bundledPath)) {
653
+ const installedPath = entry.path ? join(entry.path, "SKILL.md") : null;
654
+ if (!installedPath || !bundledSkillMatchesInstalledPath(skillName, installedPath)) {
655
+ return `Skill "${skillName}" is installed, but outdated. Run \`interf init\` and refresh Interf skills before syncing.`;
656
+ }
657
+ }
658
+ return null;
659
+ }
660
+ const fallbackPaths = installedSkillCandidatePaths(agent, skillName);
661
+ if (fallbackPaths.length === 0) {
662
+ return `Skill "${skillName}" is not installed for ${agent.displayName}. Run \`interf init\` to install current Interf skills before syncing.`;
663
+ }
664
+ if (existsSync(bundledPath)) {
665
+ const hasCurrentCopy = fallbackPaths.some((path) => bundledSkillMatchesInstalledPath(skillName, path));
666
+ if (!hasCurrentCopy) {
667
+ return `Skill "${skillName}" is installed, but outdated. Run \`interf init\` and refresh Interf skills before syncing.`;
668
+ }
669
+ }
670
+ return null;
671
+ }
672
+ function ensureRequiredSkill(agent, skillName) {
673
+ const initialError = validateInstalledSkill(agent, skillName);
674
+ if (!initialError || !skillName)
675
+ return initialError;
676
+ if (autoRefreshAttemptedAgents.has(agent.name)) {
677
+ return initialError;
678
+ }
679
+ autoRefreshAttemptedAgents.add(agent.name);
680
+ console.log(chalk.dim(` Refreshing Interf skills for ${agent.displayName}...`));
681
+ if (!refreshInterfSkills(agent)) {
682
+ return validateInstalledSkill(agent, skillName) ?? initialError;
683
+ }
684
+ const finalError = validateInstalledSkill(agent, skillName);
685
+ if (!finalError) {
686
+ console.log(chalk.dim(" Interf skills refreshed."));
687
+ }
688
+ return finalError;
689
+ }
690
+ function listInstalledSkills() {
691
+ if (installedSkillsCache)
692
+ return installedSkillsCache;
693
+ try {
694
+ const output = execFileSync("npx", SKILLS_LIST_ARGS, { encoding: "utf-8" });
695
+ installedSkillsCache = JSON.parse(output);
696
+ return installedSkillsCache;
697
+ }
698
+ catch {
699
+ return null;
700
+ }
701
+ }
702
+ function extractRequiredSkill(prompt) {
703
+ for (const line of prompt.split("\n")) {
704
+ const trimmed = line.trim();
705
+ if (!trimmed)
706
+ continue;
707
+ if (!trimmed.startsWith("/"))
708
+ return null;
709
+ return trimmed.slice(1);
710
+ }
711
+ return null;
712
+ }
713
+ function listBundledInterfSkills() {
714
+ if (!existsSync(SKILLS_DIR))
715
+ return [];
716
+ return walkBundledInterfSkills(SKILLS_DIR);
717
+ }
718
+ function walkBundledInterfSkills(rootPath, prefix = "") {
719
+ const skills = [];
720
+ for (const entry of readdirSync(rootPath)) {
721
+ const dirPath = join(rootPath, entry);
722
+ if (!statSync(dirPath).isDirectory())
723
+ continue;
724
+ const skillName = prefix ? `${prefix}/${entry}` : entry;
725
+ if (existsSync(join(dirPath, "SKILL.md"))) {
726
+ skills.push(skillName);
727
+ continue;
728
+ }
729
+ skills.push(...walkBundledInterfSkills(dirPath, skillName));
730
+ }
731
+ return skills.sort();
732
+ }
733
+ function installedSkillCandidatePaths(agent, skillName) {
734
+ const candidates = new Set();
735
+ candidates.add(join(agent.skillsDir, skillName, "SKILL.md"));
736
+ candidates.add(join(homedir(), ".agents", "skills", skillName, "SKILL.md"));
737
+ return Array.from(candidates).filter((path) => existsSync(path));
738
+ }
739
+ function bundledSkillMatchesInstalledPath(skillName, installedSkillPath) {
740
+ const bundledSkillPath = join(SKILLS_DIR, skillName, "SKILL.md");
741
+ if (!existsSync(bundledSkillPath) || !existsSync(installedSkillPath))
742
+ return false;
743
+ const bundled = normalizeSkillDoc(readFileSync(bundledSkillPath, "utf-8"));
744
+ const installed = normalizeSkillDoc(readFileSync(installedSkillPath, "utf-8"));
745
+ return bundled === installed;
746
+ }
747
+ function normalizeSkillDoc(content) {
748
+ return content.replace(/\r\n/g, "\n").trim();
749
+ }
750
+ function getAgentLocalSkillStatus(agent, skillName) {
751
+ const localSkillPath = join(agent.skillsDir, skillName, "SKILL.md");
752
+ if (!existsSync(localSkillPath)) {
753
+ return { present: false, current: false };
754
+ }
755
+ return {
756
+ present: true,
757
+ current: bundledSkillMatchesInstalledPath(skillName, localSkillPath),
758
+ };
759
+ }
760
+ //# sourceMappingURL=agents.js.map