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,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task lifecycle helpers — auto-complete / mark in_progress via pi-tasks TaskStore.
|
|
3
|
+
* Kept separate from async-agents.ts so unit tests can import without loading
|
|
4
|
+
* @earendil-works/pi-coding-agent (unavailable in the tsx test harness).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
|
|
11
|
+
let TaskStoreClass: any = null;
|
|
12
|
+
const taskStoreReady: Promise<void> = (async () => {
|
|
13
|
+
const candidates = [
|
|
14
|
+
"@tintinweb/pi-tasks/dist/task-store.js",
|
|
15
|
+
pathToFileURL(path.join(os.homedir(), ".pi/agent/npm/node_modules/@tintinweb/pi-tasks/dist/task-store.js")).href,
|
|
16
|
+
];
|
|
17
|
+
for (const candidate of candidates) {
|
|
18
|
+
try {
|
|
19
|
+
const mod = await import(candidate);
|
|
20
|
+
if (mod.TaskStore) { TaskStoreClass = mod.TaskStore; break; }
|
|
21
|
+
} catch { continue; }
|
|
22
|
+
}
|
|
23
|
+
if (!TaskStoreClass) {
|
|
24
|
+
throw new Error("[task-lifecycle] FATAL: TaskStore not found — @tintinweb/pi-tasks is required but failed to load");
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
|
|
28
|
+
/** Auto-complete a task via pi-tasks TaskStore (in-process, no AI involvement). */
|
|
29
|
+
export async function autoCompleteTask(taskId: string, cwd: string, sessionId?: string, _storeFactory?: (path: string) => any): Promise<boolean> {
|
|
30
|
+
if (!taskId || taskId === "-1") return false;
|
|
31
|
+
await taskStoreReady;
|
|
32
|
+
|
|
33
|
+
const tasksDir = path.join(cwd, ".pi", "tasks");
|
|
34
|
+
const candidates: string[] = [];
|
|
35
|
+
if (sessionId) candidates.push(path.join(tasksDir, `tasks-${sessionId}.json`));
|
|
36
|
+
candidates.push(path.join(tasksDir, "tasks.json"));
|
|
37
|
+
|
|
38
|
+
for (const storePath of candidates) {
|
|
39
|
+
try {
|
|
40
|
+
const factory = _storeFactory ?? ((p: string) => new TaskStoreClass(p));
|
|
41
|
+
const store = factory(storePath);
|
|
42
|
+
const task = store.get(taskId);
|
|
43
|
+
if (task) {
|
|
44
|
+
// Task found in this store — do not fall through to later candidates
|
|
45
|
+
if (task.status !== "completed") {
|
|
46
|
+
store.update(taskId, { status: "completed" });
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
} catch (e: any) {
|
|
52
|
+
console.debug(`[task-lifecycle] autoCompleteTask failed for task ${taskId}: ${e?.message?.slice(0, 100)}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Auto-mark a task in_progress via pi-tasks TaskStore (in-process, no AI involvement). */
|
|
60
|
+
export async function autoMarkInProgress(taskId: string, cwd: string, sessionId?: string, _storeFactory?: (path: string) => any): Promise<boolean> {
|
|
61
|
+
if (!taskId || taskId === "-1") return false;
|
|
62
|
+
await taskStoreReady;
|
|
63
|
+
|
|
64
|
+
const tasksDir = path.join(cwd, ".pi", "tasks");
|
|
65
|
+
const candidates: string[] = [];
|
|
66
|
+
if (sessionId) candidates.push(path.join(tasksDir, `tasks-${sessionId}.json`));
|
|
67
|
+
candidates.push(path.join(tasksDir, "tasks.json"));
|
|
68
|
+
|
|
69
|
+
for (const storePath of candidates) {
|
|
70
|
+
try {
|
|
71
|
+
const factory = _storeFactory ?? ((p: string) => new TaskStoreClass(p));
|
|
72
|
+
const store = factory(storePath);
|
|
73
|
+
const task = store.get(taskId);
|
|
74
|
+
if (task) {
|
|
75
|
+
// Task found in this store — do not fall through to later candidates
|
|
76
|
+
if (task.status === "pending") {
|
|
77
|
+
store.update(taskId, { status: "in_progress" });
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
} catch (e: any) {
|
|
83
|
+
console.debug(`[task-lifecycle] autoMarkInProgress failed for task ${taskId}: ${e?.message?.slice(0, 100)}`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities used across orchestrator modules.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import { createRequire } from "node:module";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
|
|
12
|
+
/** Whether notify-send is available (false = ENOENT, never retry) */
|
|
13
|
+
let notifyAvailable: boolean | undefined;
|
|
14
|
+
|
|
15
|
+
/** Send a desktop notification via notify-send (Linux only, no-op if unavailable) */
|
|
16
|
+
export function terminalNotify(title: string, body: string): void {
|
|
17
|
+
// Subagent children and coms peers don't own a terminal
|
|
18
|
+
if (process.env.PI_SUBAGENT_CHILD === "1") return;
|
|
19
|
+
if (notifyAvailable === false) return;
|
|
20
|
+
|
|
21
|
+
const project = path.basename(process.cwd());
|
|
22
|
+
const child = execFile("notify-send", [`${title} (${project})`, body], {
|
|
23
|
+
timeout: 2000,
|
|
24
|
+
}, (err) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
if ((err as any).code === "ENOENT" || /DBus\.Error|not activatable|No session bus/.test(err.message)) {
|
|
27
|
+
notifyAvailable = false;
|
|
28
|
+
} else {
|
|
29
|
+
console.debug("[utils] notify-send failed:", err.message);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
notifyAvailable = true;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
child.unref();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Set SSH timeout for git operations — prevents hung connections */
|
|
39
|
+
export function ensureGitSshTimeout(): void {
|
|
40
|
+
if (!process.env.GIT_SSH_COMMAND) {
|
|
41
|
+
process.env.GIT_SSH_COMMAND = "ssh -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -o ConnectTimeout=10";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Format a Date as HH:MM (24h clock). */
|
|
46
|
+
export const clockHHMM = (d: Date = new Date()): string =>
|
|
47
|
+
`${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
48
|
+
|
|
49
|
+
export function isRunningInContainer(): boolean {
|
|
50
|
+
try {
|
|
51
|
+
// Check for /.dockerenv (Docker) or /run/.containerenv (Podman)
|
|
52
|
+
if (fs.existsSync("/.dockerenv") || fs.existsSync("/run/.containerenv"))
|
|
53
|
+
return true;
|
|
54
|
+
// Check cgroup for container runtimes
|
|
55
|
+
const cgroup = fs.readFileSync("/proc/1/cgroup", "utf-8");
|
|
56
|
+
if (/docker|containerd|kubepods|libpod/.test(cgroup)) return true;
|
|
57
|
+
} catch (e: any) { console.debug("[utils] container detection failed:", e?.message || e); }
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
62
|
+
const s = process.argv[1];
|
|
63
|
+
if (s && fs.existsSync(s))
|
|
64
|
+
return { command: process.execPath, args: [s, ...args] };
|
|
65
|
+
const e = path.basename(process.execPath).toLowerCase();
|
|
66
|
+
if (!/^(node|bun)(\.exe)?$/.test(e))
|
|
67
|
+
return { command: process.execPath, args };
|
|
68
|
+
return { command: "pi", args };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Resolve to the main git repo root when cwd is a worktree.
|
|
72
|
+
* Uses --git-common-dir → always returns the SHARED repo root.
|
|
73
|
+
* Use for shared resources: project settings, reviews.db. */
|
|
74
|
+
const repoRootCache = new Map<string, string>();
|
|
75
|
+
|
|
76
|
+
export function resolveRepoRoot(cwd: string): string {
|
|
77
|
+
const key = path.resolve(cwd);
|
|
78
|
+
const cached = repoRootCache.get(key);
|
|
79
|
+
if (cached !== undefined) return cached;
|
|
80
|
+
try {
|
|
81
|
+
const gitCommonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], { cwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
82
|
+
if (gitCommonDir && !gitCommonDir.startsWith("fatal")) {
|
|
83
|
+
const root = path.dirname(path.resolve(cwd, gitCommonDir));
|
|
84
|
+
repoRootCache.set(key, root);
|
|
85
|
+
return root;
|
|
86
|
+
}
|
|
87
|
+
} catch {}
|
|
88
|
+
repoRootCache.set(key, cwd);
|
|
89
|
+
return cwd;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Resolve to the current worktree root (or repo root if not in a worktree).
|
|
93
|
+
* Uses --show-toplevel → returns THIS worktree's root, not the shared repo root.
|
|
94
|
+
* Use for per-worktree resources: pi-config-review-state.json.
|
|
95
|
+
* For non-worktree repos, returns the same as resolveRepoRoot.
|
|
96
|
+
* @param cwd — Directory to resolve from (any path inside the worktree)
|
|
97
|
+
* @returns The worktree's top-level directory, or `cwd` as fallback */
|
|
98
|
+
const worktreeRootCache = new Map<string, string>();
|
|
99
|
+
|
|
100
|
+
export function resolveWorktreeRoot(cwd: string): string {
|
|
101
|
+
const key = path.resolve(cwd);
|
|
102
|
+
const cached = worktreeRootCache.get(key);
|
|
103
|
+
if (cached !== undefined) return cached;
|
|
104
|
+
try {
|
|
105
|
+
const toplevel = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
106
|
+
if (toplevel && !toplevel.startsWith("fatal")) {
|
|
107
|
+
const root = path.resolve(toplevel);
|
|
108
|
+
worktreeRootCache.set(key, root);
|
|
109
|
+
return root;
|
|
110
|
+
}
|
|
111
|
+
} catch (e: any) {
|
|
112
|
+
console.debug("[utils] resolveWorktreeRoot failed:", e?.message);
|
|
113
|
+
}
|
|
114
|
+
worktreeRootCache.set(key, cwd);
|
|
115
|
+
return cwd;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Get project-scoped temp dir under <cwd>/.pi/tmp/ */
|
|
119
|
+
export function getProjectTmpDir(cwd: string): string {
|
|
120
|
+
const dir = path.join(cwd, ".pi", "tmp");
|
|
121
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
122
|
+
return dir;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Get project-scoped data dir under <cwd>/.pi/data/ */
|
|
126
|
+
export function getProjectDataDir(cwd: string): string {
|
|
127
|
+
const dir = path.join(cwd, ".pi", "data");
|
|
128
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
129
|
+
return dir;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Parse start time (field 22) from /proc stat content — handles comm fields with spaces */
|
|
133
|
+
export function parseProcStartTime(statContent: string): string | null {
|
|
134
|
+
// Field 2 (comm) is wrapped in parens and may contain spaces/parens.
|
|
135
|
+
// Find the LAST ')' to reliably skip it, then split remaining fields.
|
|
136
|
+
const closeParenIdx = statContent.lastIndexOf(")");
|
|
137
|
+
if (closeParenIdx < 0) return null;
|
|
138
|
+
const fields = statContent.slice(closeParenIdx + 2).split(" ");
|
|
139
|
+
// After comm: field 3=state(idx 0), field 4=ppid(idx 1), ... field 22=starttime(idx 19)
|
|
140
|
+
return fields[19] || null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Safely call ctx.getSystemPromptOptions() — returns null if unavailable. */
|
|
144
|
+
export function tryGetSystemPromptOptions(ctx: any): { contextFiles?: any[]; skills?: any[]; selectedTools?: any[]; promptGuidelines?: any[] } | null {
|
|
145
|
+
try {
|
|
146
|
+
return ctx.getSystemPromptOptions?.() ?? null;
|
|
147
|
+
} catch (e: any) {
|
|
148
|
+
console.debug("[utils] getSystemPromptOptions failed:", e?.message);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Minimum pi version required by this pi-config version. */
|
|
154
|
+
export const MIN_PI_VERSION = "0.81.0";
|
|
155
|
+
|
|
156
|
+
/** Get the installed pi version from its package.json. */
|
|
157
|
+
export function getPiVersion(): string | null {
|
|
158
|
+
try {
|
|
159
|
+
// Resolve from pi's own install location (process.argv[1] → dist/cli.js → package root)
|
|
160
|
+
const piScript = process.argv[1];
|
|
161
|
+
if (piScript) {
|
|
162
|
+
const realPath = fs.realpathSync(piScript);
|
|
163
|
+
const piPkgPath = path.join(path.dirname(path.dirname(realPath)), "package.json");
|
|
164
|
+
if (fs.existsSync(piPkgPath)) {
|
|
165
|
+
const pkg = JSON.parse(fs.readFileSync(piPkgPath, "utf-8"));
|
|
166
|
+
if (pkg.name === "@earendil-works/pi-coding-agent" && pkg.version) return pkg.version;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch {}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Compare two semver strings. Returns -1 if a < b, 0 if equal, 1 if a > b. */
|
|
174
|
+
export function compareSemver(a: string, b: string): number {
|
|
175
|
+
// Strip prerelease/build metadata (e.g., '0.80.4-beta.1' → '0.80.4')
|
|
176
|
+
const pa = a.split("-")[0].split(".").map(Number);
|
|
177
|
+
const pb = b.split("-")[0].split(".").map(Number);
|
|
178
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
179
|
+
const na = pa[i] || 0;
|
|
180
|
+
const nb = pb[i] || 0;
|
|
181
|
+
if (na < nb) return -1;
|
|
182
|
+
if (na > nb) return 1;
|
|
183
|
+
}
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Check if installed pi version meets minimum requirement. */
|
|
188
|
+
export function checkMinPiVersion(minVersion: string = MIN_PI_VERSION): { ok: boolean; installed: string | null; required: string } {
|
|
189
|
+
const installed = getPiVersion();
|
|
190
|
+
if (!installed) return { ok: false, installed: null, required: minVersion };
|
|
191
|
+
return { ok: compareSemver(installed, minVersion) >= 0, installed, required: minVersion };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** DJB2 hash — deterministic string → number hash for session IDs. */
|
|
195
|
+
export function djb2Hash(input: string): number {
|
|
196
|
+
let hash = 0;
|
|
197
|
+
for (let i = 0; i < input.length; i++) {
|
|
198
|
+
hash = ((hash << 5) - hash + input.charCodeAt(i)) | 0;
|
|
199
|
+
}
|
|
200
|
+
return Math.abs(hash);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* True when this process is a pi --help / --version (or -h / -v) invocation.
|
|
205
|
+
* Pi still loads extensions before printing meta output, so providers should
|
|
206
|
+
* skip expensive model discovery and registration in that case.
|
|
207
|
+
*
|
|
208
|
+
* Only true when every arg before `--` is a meta flag (no other options/values),
|
|
209
|
+
* so prompt values like `-p --help` do not false-positive.
|
|
210
|
+
*/
|
|
211
|
+
export function isPiMetaInvocation(argv: string[] = process.argv): boolean {
|
|
212
|
+
let sawMeta = false;
|
|
213
|
+
for (const arg of argv.slice(2)) {
|
|
214
|
+
if (arg === "--") break;
|
|
215
|
+
if (
|
|
216
|
+
arg === "--help" ||
|
|
217
|
+
arg === "-h" ||
|
|
218
|
+
arg === "--version" ||
|
|
219
|
+
arg === "-v"
|
|
220
|
+
) {
|
|
221
|
+
sawMeta = true;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return sawMeta;
|
|
227
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pidash Extension for pi
|
|
3
|
+
*
|
|
4
|
+
* Live web dashboard — connects to the pidash daemon to expose this session.
|
|
5
|
+
* Forwards pi events to the browser and receives prompts/commands back.
|
|
6
|
+
*
|
|
7
|
+
* Standalone extension so it can be loaded independently.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { registerPidash } from "./pidash.js";
|
|
12
|
+
|
|
13
|
+
export default function (pi: ExtensionAPI) {
|
|
14
|
+
registerPidash(pi);
|
|
15
|
+
}
|