pi-orchestrator-config 4.1.5

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 (175) hide show
  1. package/README.md +808 -0
  2. package/agents/api-documenter.md +32 -0
  3. package/agents/bash-expert.md +53 -0
  4. package/agents/code-reviewer-docs.md +145 -0
  5. package/agents/code-reviewer-guidelines.md +97 -0
  6. package/agents/code-reviewer-quality.md +142 -0
  7. package/agents/code-reviewer-security.md +107 -0
  8. package/agents/code-reviewer-spec.md +164 -0
  9. package/agents/debugger.md +63 -0
  10. package/agents/docker-expert.md +40 -0
  11. package/agents/docs-fetcher.md +48 -0
  12. package/agents/git-expert.md +63 -0
  13. package/agents/github-expert.md +83 -0
  14. package/agents/go-expert.md +62 -0
  15. package/agents/issue-reviewer-feasibility.md +60 -0
  16. package/agents/issue-reviewer-scope.md +63 -0
  17. package/agents/issue-reviewer-spec.md +77 -0
  18. package/agents/java-expert.md +31 -0
  19. package/agents/jenkins-expert.md +38 -0
  20. package/agents/kubernetes-expert.md +40 -0
  21. package/agents/planner.md +42 -0
  22. package/agents/python-expert.md +61 -0
  23. package/agents/reviewer.md +26 -0
  24. package/agents/scout.md +40 -0
  25. package/agents/security-auditor.md +254 -0
  26. package/agents/technical-documentation-writer.md +37 -0
  27. package/agents/test-automator.md +55 -0
  28. package/agents/test-runner.md +47 -0
  29. package/agents/ts-expert.md +23 -0
  30. package/agents/worker.md +30 -0
  31. package/extensions/acpx-provider/index.ts +104 -0
  32. package/extensions/acpx-provider/load-runtime.ts +99 -0
  33. package/extensions/acpx-provider/runtime-models.ts +40 -0
  34. package/extensions/cli-provider/agents/claude.ts +128 -0
  35. package/extensions/cli-provider/agents/cursor.ts +84 -0
  36. package/extensions/cli-provider/agents/gemini.ts +100 -0
  37. package/extensions/cli-provider/discover.ts +77 -0
  38. package/extensions/cli-provider/index.ts +57 -0
  39. package/extensions/cli-provider/parsers.ts +304 -0
  40. package/extensions/cli-provider/providers.ts +58 -0
  41. package/extensions/cli-provider/runner.ts +225 -0
  42. package/extensions/cli-provider/runtime-models.ts +32 -0
  43. package/extensions/cli-provider/session-reaper.ts +122 -0
  44. package/extensions/cli-provider/sessions.ts +533 -0
  45. package/extensions/cli-provider/shared/discover-cache.ts +235 -0
  46. package/extensions/cli-provider/types.ts +48 -0
  47. package/extensions/coms/coms-net-server.ts +1642 -0
  48. package/extensions/coms/coms-net-wrapper.ts +521 -0
  49. package/extensions/coms/coms-net.ts +1589 -0
  50. package/extensions/coms/coms-p2p.ts +1879 -0
  51. package/extensions/coms/coms-shared.ts +510 -0
  52. package/extensions/coms/coms-wrapper.ts +138 -0
  53. package/extensions/coms/index.ts +20 -0
  54. package/extensions/coms/themeMap.ts +147 -0
  55. package/extensions/image-gen/image-gen.ts +332 -0
  56. package/extensions/image-gen/index.ts +14 -0
  57. package/extensions/orchestrator/agents.ts +134 -0
  58. package/extensions/orchestrator/ask-user.ts +293 -0
  59. package/extensions/orchestrator/async-agents.ts +1037 -0
  60. package/extensions/orchestrator/async-capability.ts +129 -0
  61. package/extensions/orchestrator/async-runner.ts +324 -0
  62. package/extensions/orchestrator/async-status-parse.ts +31 -0
  63. package/extensions/orchestrator/async-status-ui.ts +201 -0
  64. package/extensions/orchestrator/async-wait.ts +36 -0
  65. package/extensions/orchestrator/btw.ts +210 -0
  66. package/extensions/orchestrator/cron-status-format.ts +126 -0
  67. package/extensions/orchestrator/cron-status-ui.ts +132 -0
  68. package/extensions/orchestrator/cron.ts +730 -0
  69. package/extensions/orchestrator/dreaming.ts +355 -0
  70. package/extensions/orchestrator/enforcement-helpers.ts +530 -0
  71. package/extensions/orchestrator/enforcement-rules.ts +329 -0
  72. package/extensions/orchestrator/enforcement.ts +828 -0
  73. package/extensions/orchestrator/extended-autocomplete.ts +593 -0
  74. package/extensions/orchestrator/git-helpers.ts +420 -0
  75. package/extensions/orchestrator/github-autocomplete.ts +250 -0
  76. package/extensions/orchestrator/icons.ts +17 -0
  77. package/extensions/orchestrator/index.ts +100 -0
  78. package/extensions/orchestrator/memory-embeddings.ts +281 -0
  79. package/extensions/orchestrator/memory-promotion.ts +449 -0
  80. package/extensions/orchestrator/memory-provenance.ts +80 -0
  81. package/extensions/orchestrator/memory-query-class.ts +90 -0
  82. package/extensions/orchestrator/memory-scoring.ts +434 -0
  83. package/extensions/orchestrator/memory-tools.ts +883 -0
  84. package/extensions/orchestrator/memory-tree.ts +212 -0
  85. package/extensions/orchestrator/nvim.ts +169 -0
  86. package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
  87. package/extensions/orchestrator/overlay-dashboard.ts +512 -0
  88. package/extensions/orchestrator/pi-config-review-state.ts +303 -0
  89. package/extensions/orchestrator/preference-extractor.ts +106 -0
  90. package/extensions/orchestrator/project-settings.ts +468 -0
  91. package/extensions/orchestrator/promotion-queue.ts +311 -0
  92. package/extensions/orchestrator/resolve-agent-model.ts +32 -0
  93. package/extensions/orchestrator/review-ui.ts +204 -0
  94. package/extensions/orchestrator/rule-placeholders.ts +12 -0
  95. package/extensions/orchestrator/rules.ts +481 -0
  96. package/extensions/orchestrator/session-search.ts +243 -0
  97. package/extensions/orchestrator/session-validation.ts +374 -0
  98. package/extensions/orchestrator/situation-report.ts +370 -0
  99. package/extensions/orchestrator/status-bar.ts +31 -0
  100. package/extensions/orchestrator/status-line.ts +192 -0
  101. package/extensions/orchestrator/status.ts +110 -0
  102. package/extensions/orchestrator/subagent-tool.ts +1748 -0
  103. package/extensions/orchestrator/task-lifecycle.ts +88 -0
  104. package/extensions/orchestrator/utils.ts +227 -0
  105. package/extensions/pidash/index.ts +15 -0
  106. package/extensions/pidash/pidash.ts +941 -0
  107. package/extensions/pidiff/index.ts +13 -0
  108. package/extensions/pidiff/pidiff.ts +369 -0
  109. package/extensions/providers/acpx-driver.ts +479 -0
  110. package/extensions/providers/built-in-drivers.ts +52 -0
  111. package/extensions/providers/claude-driver.ts +319 -0
  112. package/extensions/providers/cursor-acpx-driver.ts +398 -0
  113. package/extensions/providers/cursor-cli-driver.ts +484 -0
  114. package/extensions/providers/gemini-driver.ts +302 -0
  115. package/extensions/providers/index.ts +513 -0
  116. package/extensions/shared/build-system-prompt.ts +57 -0
  117. package/extensions/shared/create-runtime-provider.ts +190 -0
  118. package/extensions/shared/daemon-manager.ts +248 -0
  119. package/extensions/shared/file-logger.ts +115 -0
  120. package/extensions/shared/managed-refresh.ts +211 -0
  121. package/extensions/shared/provider-driver.ts +295 -0
  122. package/extensions/shared/provider-errors.ts +305 -0
  123. package/extensions/shared/provider-registry.ts +388 -0
  124. package/extensions/shared/stream-builder.ts +275 -0
  125. package/extensions/shared/types.ts +20 -0
  126. package/extensions/shared/ui/README.md +10 -0
  127. package/extensions/shared/ui/badge.tsx +52 -0
  128. package/extensions/shared/ui/button.tsx +58 -0
  129. package/extensions/shared/ui/collapsible.tsx +21 -0
  130. package/extensions/shared/ui/dropdown-menu.tsx +266 -0
  131. package/extensions/shared/ui/input.tsx +20 -0
  132. package/extensions/shared/ui/popover.tsx +114 -0
  133. package/extensions/shared/ui/resizable.tsx +48 -0
  134. package/extensions/shared/ui/scroll-area.tsx +53 -0
  135. package/extensions/shared/ui/select.tsx +201 -0
  136. package/extensions/shared/ui/separator.tsx +25 -0
  137. package/extensions/shared/ui/tooltip.tsx +64 -0
  138. package/extensions/shared/ws-client.ts +74 -0
  139. package/package.json +59 -0
  140. package/prompts/coderabbit-rate-limit.md +95 -0
  141. package/prompts/create-coms-feature-manager.md +186 -0
  142. package/prompts/create-skill.md +128 -0
  143. package/prompts/domain-model.md +109 -0
  144. package/prompts/external-ai.md +665 -0
  145. package/prompts/implement-and-review.md +25 -0
  146. package/prompts/implement.md +26 -0
  147. package/prompts/issue-review.md +256 -0
  148. package/prompts/pr-review.md +753 -0
  149. package/prompts/query-db.md +154 -0
  150. package/prompts/refine-review.md +208 -0
  151. package/prompts/release.md +235 -0
  152. package/prompts/remember.md +33 -0
  153. package/prompts/review-handler-status.md +27 -0
  154. package/prompts/review-handler.md +677 -0
  155. package/prompts/review-local.md +159 -0
  156. package/prompts/scout-and-plan.md +21 -0
  157. package/rules/00-orchestrator-core.md +42 -0
  158. package/rules/05-issue-first-workflow.md +119 -0
  159. package/rules/10-agent-routing.md +58 -0
  160. package/rules/15-mcp-launchpad.md +46 -0
  161. package/rules/20-code-review-loop.md +234 -0
  162. package/rules/25-documentation-updates.md +15 -0
  163. package/rules/30-prompt-templates.md +19 -0
  164. package/rules/35-memory.md +240 -0
  165. package/rules/40-critical-rules.md +124 -0
  166. package/rules/45-file-preview.md +29 -0
  167. package/rules/50-agent-bug-reporting.md +90 -0
  168. package/rules/55-coms-protocol.md +63 -0
  169. package/rules/60-task-tracking.md +66 -0
  170. package/skills/conflict-resolver/SKILL.md +213 -0
  171. package/skills/skill-creator/SKILL.md +85 -0
  172. package/skills/systematic-debugging/SKILL.md +353 -0
  173. package/skills/test-driven-development/SKILL.md +335 -0
  174. package/skills/update-agents-md/SKILL.md +282 -0
  175. package/templates/coms-feature-manager-prompt.md +313 -0
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: test-automator
3
+ description: Create comprehensive test suites with unit, integration, and e2e tests. Sets up CI pipelines, mocking strategies, and test data.
4
+ tools: read, write, edit, bash
5
+ ---
6
+
7
+ You are a test automation specialist focused on comprehensive testing strategies.
8
+
9
+ ## Base Rules
10
+
11
+ - Execute first, explain after
12
+ - Do NOT explain what you will do — just do it
13
+ - Do NOT ask for confirmation unless creating/modifying resources
14
+ - If a task falls outside your domain, report it and hand off
15
+
16
+ ## Focus Areas
17
+
18
+ - Unit test design with mocking and fixtures
19
+ - Integration tests with test containers
20
+ - E2E tests with Playwright/Cypress
21
+ - CI/CD test pipeline configuration
22
+ - Test data management and factories
23
+ - Coverage analysis and reporting
24
+
25
+ ## Approach
26
+
27
+ 1. Test pyramid — many unit, fewer integration, minimal E2E
28
+ 2. Arrange-Act-Assert pattern
29
+ 3. Test behavior, not implementation
30
+ 4. Deterministic tests — no flakiness
31
+ 5. Fast feedback — parallelize when possible
32
+
33
+ ## TDD Discipline (MANDATORY)
34
+
35
+ Follow the test-driven-development skill for all test creation.
36
+ This skill is available as `test-driven-development` — load it when writing tests.
37
+
38
+ The RED-GREEN-REFACTOR cycle:
39
+
40
+ 1. **RED** — Write a failing test first. Watch it fail.
41
+ 2. **GREEN** — Write minimal code to make it pass.
42
+ 3. **REFACTOR** — Clean up while tests stay green.
43
+
44
+ **Iron Law: No production code without a failing test first.**
45
+ If code was written before the test — delete it, start over from the test.
46
+
47
+ ## Output
48
+
49
+ - Test suite with clear test names
50
+ - Mock/stub implementations for dependencies
51
+ - Test data factories or fixtures
52
+ - CI pipeline configuration for tests
53
+ - Coverage report setup
54
+
55
+ Use appropriate testing frameworks (Jest, pytest, etc). Include both happy and edge cases.
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: test-runner
3
+ description: Run tests and analyze failures. Returns detailed failure analysis without making fixes.
4
+ tools: bash, read
5
+ ---
6
+
7
+ You are a specialized test execution agent. Run tests and provide concise failure analysis.
8
+
9
+ ## Base Rules
10
+
11
+ - Execute first, explain after
12
+ - Never attempt fixes — only analyze and report
13
+ - Return control promptly after analysis
14
+
15
+ ## Workflow
16
+
17
+ 1. Run the test command provided
18
+ 2. Parse and analyze test results
19
+ 3. For failures, provide:
20
+ - Test name and location
21
+ - Expected vs actual result
22
+ - Most likely fix location
23
+ - One-line suggestion for fix approach
24
+ 4. Return control
25
+
26
+ ## Output Format
27
+
28
+ ```text
29
+ ✅ Passing: X tests
30
+ ❌ Failing: Y tests
31
+
32
+ Failed Test 1: test_name (file:line)
33
+ Expected: [brief description]
34
+ Actual: [brief description]
35
+ Fix location: path/to/file.rb:line
36
+ Suggested approach: [one line]
37
+
38
+ Returning control for fixes.
39
+ ```
40
+
41
+ ## Important Constraints
42
+
43
+ - Run exactly what is specified
44
+ - Keep analysis concise (avoid verbose stack traces)
45
+ - Focus on actionable information
46
+ - Never modify files
47
+ - Return control promptly after analysis
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: ts-expert
3
+ description: TypeScript, JavaScript, and frontend framework development (React/Vue/Angular/CSS). UI design, component creation, and modern web technologies.
4
+ tools: read, write, edit, bash
5
+ ---
6
+
7
+ You are a frontend development specialist covering JavaScript, TypeScript, React, Vue, Angular, CSS, and modern web technologies.
8
+
9
+ ## Base Rules
10
+
11
+ - Execute first, explain after
12
+ - Do NOT explain what you will do — just do it
13
+ - Do NOT ask for confirmation unless creating/modifying resources
14
+ - If a task falls outside your domain, report it and hand off
15
+
16
+ ## Core Expertise
17
+
18
+ - JavaScript/TypeScript development
19
+ - React/Vue/Angular components
20
+ - CSS/styling work
21
+ - Frontend testing (Jest, Vitest, Cypress, Playwright)
22
+ - Build tooling (Vite, Webpack, esbuild)
23
+ - UI/UX implementation
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: worker
3
+ description: General-purpose agent for tasks that don't match any specialist. Full capabilities.
4
+ tools: read, write, edit, bash
5
+ ---
6
+
7
+ You are a general-purpose development agent. Handle any task that doesn't have a dedicated specialist.
8
+
9
+ ## Base Rules
10
+
11
+ - Execute first, explain after
12
+ - Do NOT explain what you will do — just do it
13
+ - Do NOT ask for confirmation unless creating/modifying resources
14
+ - If you identify a task that would be better handled by a specialist, report it and hand off
15
+
16
+ ## Capabilities
17
+
18
+ - Read, analyze, and modify any file type
19
+ - Run shell commands
20
+ - General code writing and refactoring
21
+ - File system operations
22
+ - Research and analysis
23
+
24
+ ## Long-Running Commands
25
+
26
+ When running commands that poll, sleep, or wait (e.g., `reviews poll`, `watch`, monitoring loops):
27
+
28
+ - **Do NOT set a bash timeout** unless the task explicitly specifies one
29
+ - These commands can run for 30+ minutes — a short timeout kills them mid-sleep
30
+ - If the task says "do NOT set a timeout", omit the timeout parameter entirely
@@ -0,0 +1,104 @@
1
+ /**
2
+ * ACPX Provider Extension — Backward-Compatible Shim
3
+ *
4
+ * This file exists solely for backward compatibility with consumers that
5
+ * resolve `extensions/acpx-provider/index.ts` from the pi-orchestrator-config
6
+ * package (e.g. pi-sidecar).
7
+ *
8
+ * Delegates provider registration to the unified driver extension at
9
+ * `extensions/providers/index.ts`. Re-exports all public APIs so existing
10
+ * import paths continue to work.
11
+ *
12
+ * DO NOT add new logic here — add it to the driver or shared modules.
13
+ *
14
+ * @module acpx-provider (shim)
15
+ */
16
+
17
+ import { randomUUID } from "node:crypto";
18
+ import path from "node:path";
19
+ import os from "node:os";
20
+ import { rm } from "node:fs/promises";
21
+ import { fileLog } from "../shared/file-logger.js";
22
+ import { loadAcpxRuntime } from "./load-runtime.js";
23
+
24
+ // Re-export model mapping (consumed by tests and unified extension)
25
+ export {
26
+ mapAcpxDiscoveredModels,
27
+ modelIdToDisplayName,
28
+ } from "./runtime-models.js";
29
+
30
+ /**
31
+ * Discover available models for an acpx agent.
32
+ *
33
+ * Uses the acpx/runtime library API: creates a temporary session, queries
34
+ * getStatus().models.availableModelIds, then closes the session.
35
+ *
36
+ * This function is kept here (not moved to a sub-module) because pi-sidecar
37
+ * imports it from this exact path via jiti.
38
+ */
39
+ export async function discoverAcpxModels(
40
+ agent: string,
41
+ cwd?: string,
42
+ ): Promise<Array<{ id: string; name: string; provider: string }>> {
43
+ if (!/^[a-z0-9_-]+$/i.test(agent)) {
44
+ throw new Error(`Invalid agent name: ${agent}`);
45
+ }
46
+
47
+ const {
48
+ createAcpRuntime,
49
+ createFileSessionStore,
50
+ createAgentRegistry,
51
+ } = await loadAcpxRuntime();
52
+
53
+ const { modelIdToDisplayName: displayName } = await import("./runtime-models.js");
54
+
55
+ const effectiveCwd = cwd || process.cwd();
56
+ const uid = randomUUID().slice(0, 8);
57
+ const stateDir = path.join(os.homedir(), ".acpx", `discover-${process.pid}-${uid}`);
58
+ const runtime = createAcpRuntime({
59
+ cwd: effectiveCwd,
60
+ sessionStore: createFileSessionStore({ stateDir }),
61
+ agentRegistry: createAgentRegistry(),
62
+ permissionMode: "deny-all",
63
+ });
64
+
65
+ let handle: any;
66
+ try {
67
+ handle = await runtime.ensureSession({
68
+ sessionKey: `discover-${agent}-${uid}`,
69
+ agent,
70
+ mode: "oneshot",
71
+ cwd: effectiveCwd,
72
+ });
73
+
74
+ const status = await runtime.getStatus({ handle });
75
+ const modelIds: string[] = status.models?.availableModelIds || [];
76
+
77
+ return modelIds.map((modelId: string) => ({
78
+ id: `${agent}:${modelId}`,
79
+ name: `${displayName(modelId)} (${agent})`,
80
+ provider: `acpx-${agent}`,
81
+ }));
82
+ } catch (err) {
83
+ fileLog("acpx-provider", "debug", "acpx-provider", `model discovery failed for ${agent}:`, err);
84
+ return [];
85
+ } finally {
86
+ if (handle) {
87
+ await runtime.close({ handle, reason: "discovery complete" }).catch((err: any) => {
88
+ fileLog("acpx-provider", "debug", "acpx-provider", "failed to close discovery session:", err);
89
+ });
90
+ }
91
+ await rm(stateDir, { recursive: true, force: true }).catch(() => {});
92
+ }
93
+ }
94
+
95
+ /**
96
+ * No-op extension entry point.
97
+ *
98
+ * Registration is now handled by the unified provider extension at
99
+ * `extensions/providers/index.ts`. This default export exists only so
100
+ * pi's extension loader does not throw when loading this directory.
101
+ */
102
+ export default async function (_pi: unknown) {
103
+ // Intentional no-op — registration moved to extensions/providers/index.ts
104
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Load acpx/runtime: prefer a global `npm install -g acpx`, then fall back to the
3
+ * package-local `acpx` dependency (required for plain `pi` / ~/.pi package installs).
4
+ */
5
+
6
+ import { createRequire } from "node:module";
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { pathToFileURL } from "node:url";
10
+ import { execFileSync } from "node:child_process";
11
+ import { homedir } from "node:os";
12
+
13
+ /** Minimal surface used by acpx-provider. */
14
+ export type AcpxRuntimeModule = {
15
+ createAcpRuntime: (...args: any[]) => any;
16
+ createFileSessionStore: (...args: any[]) => any;
17
+ createAgentRegistry: (...args: any[]) => any;
18
+ };
19
+
20
+ let cachedRoots: string[] | null = null;
21
+ let cachedRuntime: Promise<AcpxRuntimeModule> | null = null;
22
+
23
+ function globalNodeModuleRoots(): string[] {
24
+ if (cachedRoots) return cachedRoots;
25
+ const roots: string[] = [];
26
+ try {
27
+ const root = execFileSync("npm", ["root", "-g"], {
28
+ encoding: "utf-8",
29
+ timeout: 15_000,
30
+ }).trim();
31
+ if (root) roots.push(root);
32
+ } catch {
33
+ /* ignore */
34
+ }
35
+ for (const candidate of [
36
+ "/usr/local/lib/node_modules",
37
+ join(homedir(), ".npm-global", "lib", "node_modules"),
38
+ ]) {
39
+ if (candidate && !roots.includes(candidate)) roots.push(candidate);
40
+ }
41
+ cachedRoots = roots;
42
+ return roots;
43
+ }
44
+
45
+ async function importFromPackageRoot(pkgRoot: string): Promise<AcpxRuntimeModule | null> {
46
+ const runtimeJs = join(pkgRoot, "dist", "runtime.js");
47
+ if (existsSync(runtimeJs)) {
48
+ return (await import(pathToFileURL(runtimeJs).href)) as AcpxRuntimeModule;
49
+ }
50
+ const pkgJson = join(pkgRoot, "package.json");
51
+ if (!existsSync(pkgJson)) return null;
52
+ try {
53
+ const req = createRequire(pkgJson);
54
+ const resolved = req.resolve("./dist/runtime.js");
55
+ return (await import(pathToFileURL(resolved).href)) as AcpxRuntimeModule;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ async function resolveAcpxRuntime(): Promise<AcpxRuntimeModule> {
62
+ for (const root of globalNodeModuleRoots()) {
63
+ const mod = await importFromPackageRoot(join(root, "acpx"));
64
+ if (typeof mod?.createAcpRuntime === "function") return mod;
65
+ }
66
+
67
+ try {
68
+ const mod = (await import("acpx/runtime")) as AcpxRuntimeModule;
69
+ if (typeof mod?.createAcpRuntime === "function") return mod;
70
+ } catch {
71
+ /* ignore */
72
+ }
73
+
74
+ throw new Error(
75
+ "acpx/runtime not found. Install with: npm install -g acpx " +
76
+ "(or ensure the pi-config package dependency `acpx` is installed)",
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Resolve acpx/runtime: global install first, then package dependency import.
82
+ * Memoized — concurrent/repeated calls share one resolution. Rejected loads
83
+ * clear the cache so a later install can succeed.
84
+ */
85
+ export function loadAcpxRuntime(): Promise<AcpxRuntimeModule> {
86
+ if (!cachedRuntime) {
87
+ cachedRuntime = resolveAcpxRuntime().catch((err) => {
88
+ cachedRuntime = null;
89
+ throw err;
90
+ });
91
+ }
92
+ return cachedRuntime;
93
+ }
94
+
95
+ /** Test helper — clear memoization between cases. */
96
+ export function clearAcpxRuntimeCache(): void {
97
+ cachedRoots = null;
98
+ cachedRuntime = null;
99
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Map ACPX discovery results → full createProvider Model[].
3
+ * Kept separate from index.ts so unit tests do not load runtime/stream code.
4
+ */
5
+
6
+ import { buildRuntimeModel } from "../shared/create-runtime-provider.js";
7
+
8
+ export function modelIdToDisplayName(modelId: string): string {
9
+ // Strip bracket suffixes for display: gpt-5.4[context=272k,...] -> Gpt 5.4
10
+ const bracketIdx = modelId.indexOf("[");
11
+ const baseName = bracketIdx >= 0 ? modelId.substring(0, bracketIdx) : modelId;
12
+ return baseName
13
+ .replace(/-/g, " ")
14
+ .replace(/\b\w/g, (c) => c.toUpperCase());
15
+ }
16
+
17
+ export function mapAcpxDiscoveredModels(
18
+ agent: string,
19
+ modelIds: readonly string[],
20
+ ): ReturnType<typeof buildRuntimeModel>[] {
21
+ const provider = `acpx-${agent}`;
22
+ if (modelIds.length === 0) {
23
+ return [
24
+ buildRuntimeModel({
25
+ id: `${agent}:default`,
26
+ name: `${agent} (default)`,
27
+ api: "acpx",
28
+ provider,
29
+ }),
30
+ ];
31
+ }
32
+ return modelIds.map((m) =>
33
+ buildRuntimeModel({
34
+ id: `${agent}:${m}`,
35
+ name: `${modelIdToDisplayName(m)} (${agent})`,
36
+ api: "acpx",
37
+ provider,
38
+ }),
39
+ );
40
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Claude Code CLI agent — flags + binary-catalog discovery.
3
+ */
4
+
5
+ import { closeSync, openSync, readSync, statSync } from "node:fs";
6
+ import { cliProviderLog } from "../../shared/file-logger.js";
7
+ import type { CliProviderDef, DiscoveredCliModel } from "../types.js";
8
+ import {
9
+ cacheKeyForFile,
10
+ readModelCache,
11
+ resolveBinary,
12
+ writeModelCache,
13
+ } from "../shared/discover-cache.js";
14
+
15
+ /** Catalog entries embedded in the Claude Code binary (minified JS/strings). */
16
+ const CATALOG_RE =
17
+ /\{id:"(claude-[^"]+)",family:"([^"]+)",display_name:"([^"]+)"/g;
18
+
19
+ const DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;
20
+ /** Enough overlap that a catalog match cannot split across chunks. */
21
+ const DEFAULT_OVERLAP = 512;
22
+
23
+ /** Parse selectable catalog from a string (tests / small fixtures). */
24
+ export function parseClaudeBinaryCatalog(
25
+ binaryContents: string,
26
+ ): DiscoveredCliModel[] {
27
+ const models: DiscoveredCliModel[] = [];
28
+ const seen = new Set<string>();
29
+ CATALOG_RE.lastIndex = 0;
30
+ let m: RegExpExecArray | null;
31
+ while ((m = CATALOG_RE.exec(binaryContents))) {
32
+ const id = m[1];
33
+ if (seen.has(id)) continue;
34
+ seen.add(id);
35
+ models.push({ id, name: m[3] });
36
+ }
37
+ return models;
38
+ }
39
+
40
+ /**
41
+ * Scan a Claude binary for the embedded model catalog without loading the
42
+ * whole file into a JS string (binaries are ~250MB+ ELF).
43
+ */
44
+ export function scanClaudeBinaryCatalog(
45
+ filePath: string,
46
+ opts?: { chunkSize?: number; overlap?: number },
47
+ ): DiscoveredCliModel[] {
48
+ const chunkSize = opts?.chunkSize ?? DEFAULT_CHUNK_SIZE;
49
+ const overlap = Math.min(opts?.overlap ?? DEFAULT_OVERLAP, chunkSize - 1);
50
+ const size = statSync(filePath).size;
51
+ const fd = openSync(filePath, "r");
52
+ const readBuf = Buffer.alloc(chunkSize);
53
+ let prev = Buffer.alloc(0);
54
+ const models: DiscoveredCliModel[] = [];
55
+ const seen = new Set<string>();
56
+ let offset = 0;
57
+
58
+ try {
59
+ while (offset < size) {
60
+ const n = readSync(fd, readBuf, 0, chunkSize, offset);
61
+ if (n <= 0) break;
62
+
63
+ const chunk = Buffer.concat([prev, readBuf.subarray(0, n)]);
64
+ // latin1 = 1:1 byte mapping; catalog ids are ASCII
65
+ const text = chunk.toString("latin1");
66
+ CATALOG_RE.lastIndex = 0;
67
+ let m: RegExpExecArray | null;
68
+ while ((m = CATALOG_RE.exec(text))) {
69
+ const id = m[1];
70
+ if (seen.has(id)) continue;
71
+ seen.add(id);
72
+ models.push({ id, name: m[3] });
73
+ }
74
+
75
+ prev =
76
+ overlap > 0
77
+ ? chunk.subarray(Math.max(0, chunk.length - overlap))
78
+ : Buffer.alloc(0);
79
+ offset += n;
80
+ }
81
+ } finally {
82
+ closeSync(fd);
83
+ }
84
+
85
+ return models;
86
+ }
87
+
88
+ function discoverClaudeModels(): DiscoveredCliModel[] {
89
+ const binary = resolveBinary("claude");
90
+ if (!binary) return [];
91
+ try {
92
+ const key = cacheKeyForFile("claude", binary);
93
+ const cached = readModelCache(key);
94
+ if (cached && cached.length > 0) return cached;
95
+
96
+ const models = scanClaudeBinaryCatalog(binary);
97
+ if (models.length > 0) writeModelCache(key, models);
98
+ return models;
99
+ } catch (err) {
100
+ cliProviderLog("error", "claude: binary catalog failed", err);
101
+ return [];
102
+ }
103
+ }
104
+
105
+ export const claudeProvider: CliProviderDef = {
106
+ name: "claude",
107
+ binary: "claude",
108
+ buildBaseArgs: (model) => {
109
+ // -p skips workspace trust; stream-json requires --verbose
110
+ const args = [
111
+ "-p",
112
+ "--verbose",
113
+ "--dangerously-skip-permissions",
114
+ "--output-format",
115
+ "stream-json",
116
+ "--include-partial-messages",
117
+ ];
118
+ if (model && model !== "default") {
119
+ args.unshift("--model", model);
120
+ }
121
+ return args;
122
+ },
123
+ resumeFlag: "--resume",
124
+ continueFlags: ["--continue"],
125
+ outputFormat: "stream-json",
126
+ promptOnStdin: true,
127
+ discoverModels: discoverClaudeModels,
128
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Cursor CLI agent — flags, discovery, headless trust/force.
3
+ *
4
+ * Model ids from `agent --list-models` (CLI namespace, not acpx).
5
+ */
6
+
7
+ import { spawnSync } from "node:child_process";
8
+ import { cliProviderLog } from "../../shared/file-logger.js";
9
+ import type { CliProviderDef, DiscoveredCliModel } from "../types.js";
10
+ import { resolveBinary } from "../shared/discover-cache.js";
11
+
12
+ /** Strip CSI / OSC ANSI sequences so colored CLI output still parses (#666). */
13
+ export function stripAnsi(text: string): string {
14
+ return text
15
+ .replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "")
16
+ .replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "")
17
+ .replace(/\u001b[@-Z\\-_]/g, "");
18
+ }
19
+
20
+ /** Parse `agent --list-models` text output. */
21
+ export function parseAgentListModels(stdout: string): DiscoveredCliModel[] {
22
+ const models: DiscoveredCliModel[] = [];
23
+ const seen = new Set<string>();
24
+ for (const line of stripAnsi(stdout).split(/\r?\n/)) {
25
+ const m = line.match(
26
+ /^([a-zA-Z0-9][a-zA-Z0-9._\[\]=,:-]*)\s+-\s+(.+?)\s*$/,
27
+ );
28
+ if (!m) continue;
29
+ const id = m[1];
30
+ const name = m[2].replace(/\s*\(default\)\s*$/i, "").trim();
31
+ if (seen.has(id)) continue;
32
+ seen.add(id);
33
+ models.push({ id, name });
34
+ }
35
+ return models;
36
+ }
37
+
38
+ function discoverCursorModels(): DiscoveredCliModel[] {
39
+ const binary = resolveBinary("agent");
40
+ if (!binary) return [];
41
+
42
+ const r = spawnSync(binary, ["--list-models"], {
43
+ encoding: "utf-8",
44
+ timeout: 25_000,
45
+ // Prefer plain text; still strip ANSI in parser if CLI ignores NO_COLOR.
46
+ env: { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" },
47
+ });
48
+ const out = `${r.stdout || ""}\n${r.stderr || ""}`;
49
+ const models = parseAgentListModels(out);
50
+ if (models.length === 0) {
51
+ cliProviderLog(
52
+ "warn",
53
+ `cursor: --list-models failed (status=${r.status}): ${out.slice(0, 200)}`,
54
+ );
55
+ }
56
+ return models;
57
+ }
58
+
59
+ export const cursorProvider: CliProviderDef = {
60
+ name: "cursor",
61
+ binary: "agent",
62
+ buildBaseArgs: (model, cwd) => {
63
+ // --trust: workspace; --force: auto-approve; stream-partial-output: live tokens
64
+ const args = [
65
+ "--print",
66
+ "--trust",
67
+ "--force",
68
+ "--output-format",
69
+ "stream-json",
70
+ "--stream-partial-output",
71
+ "--workspace",
72
+ cwd,
73
+ ];
74
+ if (model && model !== "default") {
75
+ args.unshift("--model", model);
76
+ }
77
+ return args;
78
+ },
79
+ resumeFlag: "--resume",
80
+ continueFlags: ["--continue"],
81
+ outputFormat: "stream-json",
82
+ promptOnStdin: true,
83
+ discoverModels: discoverCursorModels,
84
+ };