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.
- package/README.md +808 -0
- package/agents/api-documenter.md +32 -0
- package/agents/bash-expert.md +53 -0
- package/agents/code-reviewer-docs.md +145 -0
- package/agents/code-reviewer-guidelines.md +97 -0
- package/agents/code-reviewer-quality.md +142 -0
- package/agents/code-reviewer-security.md +107 -0
- package/agents/code-reviewer-spec.md +164 -0
- package/agents/debugger.md +63 -0
- package/agents/docker-expert.md +40 -0
- package/agents/docs-fetcher.md +48 -0
- package/agents/git-expert.md +63 -0
- package/agents/github-expert.md +83 -0
- package/agents/go-expert.md +62 -0
- package/agents/issue-reviewer-feasibility.md +60 -0
- package/agents/issue-reviewer-scope.md +63 -0
- package/agents/issue-reviewer-spec.md +77 -0
- package/agents/java-expert.md +31 -0
- package/agents/jenkins-expert.md +38 -0
- package/agents/kubernetes-expert.md +40 -0
- package/agents/planner.md +42 -0
- package/agents/python-expert.md +61 -0
- package/agents/reviewer.md +26 -0
- package/agents/scout.md +40 -0
- package/agents/security-auditor.md +254 -0
- package/agents/technical-documentation-writer.md +37 -0
- package/agents/test-automator.md +55 -0
- package/agents/test-runner.md +47 -0
- package/agents/ts-expert.md +23 -0
- package/agents/worker.md +30 -0
- package/extensions/acpx-provider/index.ts +104 -0
- package/extensions/acpx-provider/load-runtime.ts +99 -0
- package/extensions/acpx-provider/runtime-models.ts +40 -0
- package/extensions/cli-provider/agents/claude.ts +128 -0
- package/extensions/cli-provider/agents/cursor.ts +84 -0
- package/extensions/cli-provider/agents/gemini.ts +100 -0
- package/extensions/cli-provider/discover.ts +77 -0
- package/extensions/cli-provider/index.ts +57 -0
- package/extensions/cli-provider/parsers.ts +304 -0
- package/extensions/cli-provider/providers.ts +58 -0
- package/extensions/cli-provider/runner.ts +225 -0
- package/extensions/cli-provider/runtime-models.ts +32 -0
- package/extensions/cli-provider/session-reaper.ts +122 -0
- package/extensions/cli-provider/sessions.ts +533 -0
- package/extensions/cli-provider/shared/discover-cache.ts +235 -0
- package/extensions/cli-provider/types.ts +48 -0
- package/extensions/coms/coms-net-server.ts +1642 -0
- package/extensions/coms/coms-net-wrapper.ts +521 -0
- package/extensions/coms/coms-net.ts +1589 -0
- package/extensions/coms/coms-p2p.ts +1879 -0
- package/extensions/coms/coms-shared.ts +510 -0
- package/extensions/coms/coms-wrapper.ts +138 -0
- package/extensions/coms/index.ts +20 -0
- package/extensions/coms/themeMap.ts +147 -0
- package/extensions/image-gen/image-gen.ts +332 -0
- package/extensions/image-gen/index.ts +14 -0
- package/extensions/orchestrator/agents.ts +134 -0
- package/extensions/orchestrator/ask-user.ts +293 -0
- package/extensions/orchestrator/async-agents.ts +1037 -0
- package/extensions/orchestrator/async-capability.ts +129 -0
- package/extensions/orchestrator/async-runner.ts +324 -0
- package/extensions/orchestrator/async-status-parse.ts +31 -0
- package/extensions/orchestrator/async-status-ui.ts +201 -0
- package/extensions/orchestrator/async-wait.ts +36 -0
- package/extensions/orchestrator/btw.ts +210 -0
- package/extensions/orchestrator/cron-status-format.ts +126 -0
- package/extensions/orchestrator/cron-status-ui.ts +132 -0
- package/extensions/orchestrator/cron.ts +730 -0
- package/extensions/orchestrator/dreaming.ts +355 -0
- package/extensions/orchestrator/enforcement-helpers.ts +530 -0
- package/extensions/orchestrator/enforcement-rules.ts +329 -0
- package/extensions/orchestrator/enforcement.ts +828 -0
- package/extensions/orchestrator/extended-autocomplete.ts +593 -0
- package/extensions/orchestrator/git-helpers.ts +420 -0
- package/extensions/orchestrator/github-autocomplete.ts +250 -0
- package/extensions/orchestrator/icons.ts +17 -0
- package/extensions/orchestrator/index.ts +100 -0
- package/extensions/orchestrator/memory-embeddings.ts +281 -0
- package/extensions/orchestrator/memory-promotion.ts +449 -0
- package/extensions/orchestrator/memory-provenance.ts +80 -0
- package/extensions/orchestrator/memory-query-class.ts +90 -0
- package/extensions/orchestrator/memory-scoring.ts +434 -0
- package/extensions/orchestrator/memory-tools.ts +883 -0
- package/extensions/orchestrator/memory-tree.ts +212 -0
- package/extensions/orchestrator/nvim.ts +169 -0
- package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
- package/extensions/orchestrator/overlay-dashboard.ts +512 -0
- package/extensions/orchestrator/pi-config-review-state.ts +303 -0
- package/extensions/orchestrator/preference-extractor.ts +106 -0
- package/extensions/orchestrator/project-settings.ts +468 -0
- package/extensions/orchestrator/promotion-queue.ts +311 -0
- package/extensions/orchestrator/resolve-agent-model.ts +32 -0
- package/extensions/orchestrator/review-ui.ts +204 -0
- package/extensions/orchestrator/rule-placeholders.ts +12 -0
- package/extensions/orchestrator/rules.ts +481 -0
- package/extensions/orchestrator/session-search.ts +243 -0
- package/extensions/orchestrator/session-validation.ts +374 -0
- package/extensions/orchestrator/situation-report.ts +370 -0
- package/extensions/orchestrator/status-bar.ts +31 -0
- package/extensions/orchestrator/status-line.ts +192 -0
- package/extensions/orchestrator/status.ts +110 -0
- package/extensions/orchestrator/subagent-tool.ts +1748 -0
- package/extensions/orchestrator/task-lifecycle.ts +88 -0
- package/extensions/orchestrator/utils.ts +227 -0
- package/extensions/pidash/index.ts +15 -0
- package/extensions/pidash/pidash.ts +941 -0
- package/extensions/pidiff/index.ts +13 -0
- package/extensions/pidiff/pidiff.ts +369 -0
- package/extensions/providers/acpx-driver.ts +479 -0
- package/extensions/providers/built-in-drivers.ts +52 -0
- package/extensions/providers/claude-driver.ts +319 -0
- package/extensions/providers/cursor-acpx-driver.ts +398 -0
- package/extensions/providers/cursor-cli-driver.ts +484 -0
- package/extensions/providers/gemini-driver.ts +302 -0
- package/extensions/providers/index.ts +513 -0
- package/extensions/shared/build-system-prompt.ts +57 -0
- package/extensions/shared/create-runtime-provider.ts +190 -0
- package/extensions/shared/daemon-manager.ts +248 -0
- package/extensions/shared/file-logger.ts +115 -0
- package/extensions/shared/managed-refresh.ts +211 -0
- package/extensions/shared/provider-driver.ts +295 -0
- package/extensions/shared/provider-errors.ts +305 -0
- package/extensions/shared/provider-registry.ts +388 -0
- package/extensions/shared/stream-builder.ts +275 -0
- package/extensions/shared/types.ts +20 -0
- package/extensions/shared/ui/README.md +10 -0
- package/extensions/shared/ui/badge.tsx +52 -0
- package/extensions/shared/ui/button.tsx +58 -0
- package/extensions/shared/ui/collapsible.tsx +21 -0
- package/extensions/shared/ui/dropdown-menu.tsx +266 -0
- package/extensions/shared/ui/input.tsx +20 -0
- package/extensions/shared/ui/popover.tsx +114 -0
- package/extensions/shared/ui/resizable.tsx +48 -0
- package/extensions/shared/ui/scroll-area.tsx +53 -0
- package/extensions/shared/ui/select.tsx +201 -0
- package/extensions/shared/ui/separator.tsx +25 -0
- package/extensions/shared/ui/tooltip.tsx +64 -0
- package/extensions/shared/ws-client.ts +74 -0
- package/package.json +59 -0
- package/prompts/coderabbit-rate-limit.md +95 -0
- package/prompts/create-coms-feature-manager.md +186 -0
- package/prompts/create-skill.md +128 -0
- package/prompts/domain-model.md +109 -0
- package/prompts/external-ai.md +665 -0
- package/prompts/implement-and-review.md +25 -0
- package/prompts/implement.md +26 -0
- package/prompts/issue-review.md +256 -0
- package/prompts/pr-review.md +753 -0
- package/prompts/query-db.md +154 -0
- package/prompts/refine-review.md +208 -0
- package/prompts/release.md +235 -0
- package/prompts/remember.md +33 -0
- package/prompts/review-handler-status.md +27 -0
- package/prompts/review-handler.md +677 -0
- package/prompts/review-local.md +159 -0
- package/prompts/scout-and-plan.md +21 -0
- package/rules/00-orchestrator-core.md +42 -0
- package/rules/05-issue-first-workflow.md +119 -0
- package/rules/10-agent-routing.md +58 -0
- package/rules/15-mcp-launchpad.md +46 -0
- package/rules/20-code-review-loop.md +234 -0
- package/rules/25-documentation-updates.md +15 -0
- package/rules/30-prompt-templates.md +19 -0
- package/rules/35-memory.md +240 -0
- package/rules/40-critical-rules.md +124 -0
- package/rules/45-file-preview.md +29 -0
- package/rules/50-agent-bug-reporting.md +90 -0
- package/rules/55-coms-protocol.md +63 -0
- package/rules/60-task-tracking.md +66 -0
- package/skills/conflict-resolver/SKILL.md +213 -0
- package/skills/skill-creator/SKILL.md +85 -0
- package/skills/systematic-debugging/SKILL.md +353 -0
- package/skills/test-driven-development/SKILL.md +335 -0
- package/skills/update-agents-md/SKILL.md +282 -0
- 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
|
package/agents/worker.md
ADDED
|
@@ -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
|
+
};
|