pi-orchestrator-config 4.2.0 → 4.3.2
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 +76 -67
- package/agents/code-reviewer-docs.md +11 -1
- package/agents/code-reviewer-guidelines.md +11 -1
- package/agents/code-reviewer-quality.md +11 -1
- package/agents/code-reviewer-security.md +11 -1
- package/agents/code-reviewer-spec.md +11 -1
- package/agents/git-expert.md +42 -3
- package/agents/github-expert.md +11 -0
- package/extensions/acpx-provider/runtime-models.ts +18 -6
- package/extensions/cli-provider/agents/claude.ts +3 -2
- package/extensions/cli-provider/agents/cursor.ts +3 -5
- package/extensions/cli-provider/agents/gemini.ts +3 -2
- package/extensions/cli-provider/discover.ts +7 -7
- package/extensions/cli-provider/index.ts +5 -0
- package/extensions/cli-provider/runtime-models.ts +18 -6
- package/extensions/cli-provider/session-reaper.ts +5 -7
- package/extensions/cli-provider/sessions.ts +4 -0
- package/extensions/cli-provider/shared/discover-cache.ts +17 -176
- package/extensions/coms/coms-p2p.ts +1719 -423
- package/extensions/coms/coms-shared.ts +261 -39
- package/extensions/coms/coms-wrapper.ts +100 -62
- package/extensions/coms/index.ts +8 -4
- package/extensions/coms/mixed-turn.ts +26 -0
- package/extensions/coms/probe-socket.ts +42 -0
- package/extensions/coms/themeMap.ts +0 -1
- package/extensions/image-gen/image-gen.ts +19 -1
- package/extensions/orchestrator/async-agents.ts +563 -102
- package/extensions/orchestrator/async-capability.ts +8 -8
- package/extensions/orchestrator/cron.ts +15 -9
- package/extensions/orchestrator/dreaming.ts +32 -25
- package/extensions/orchestrator/enforcement-helpers.ts +459 -17
- package/extensions/orchestrator/enforcement-rules.ts +5 -4
- package/extensions/orchestrator/enforcement.ts +80 -27
- package/extensions/orchestrator/extended-autocomplete.ts +7 -0
- package/extensions/orchestrator/git-helpers.ts +2 -2
- package/extensions/orchestrator/index.ts +78 -10
- package/extensions/orchestrator/markdown-transformer.ts +313 -0
- package/extensions/orchestrator/memory-embeddings.ts +36 -34
- package/extensions/orchestrator/memory-provenance.ts +29 -25
- package/extensions/orchestrator/memory-scoring.ts +23 -18
- package/extensions/orchestrator/memory-tools.ts +10 -3
- package/extensions/orchestrator/overlay-dashboard.ts +17 -5
- package/extensions/orchestrator/parse-model-override.ts +29 -0
- package/extensions/orchestrator/pi-config-review-state.ts +57 -33
- package/extensions/orchestrator/project-settings.ts +333 -127
- package/extensions/orchestrator/resolve-agent-model.ts +24 -13
- package/extensions/orchestrator/review-porcelain.ts +4 -0
- package/extensions/orchestrator/review-ui.ts +63 -15
- package/extensions/orchestrator/rule-placeholders.ts +411 -2
- package/extensions/orchestrator/rules.ts +80 -132
- package/extensions/orchestrator/settings-tui-helpers.ts +380 -0
- package/extensions/orchestrator/settings-tui-submenus.ts +647 -0
- package/extensions/orchestrator/settings-tui.ts +636 -0
- package/extensions/orchestrator/state-jsonl.ts +431 -0
- package/extensions/orchestrator/subagent-tool.ts +157 -59
- package/extensions/orchestrator/sync-limit.ts +23 -0
- package/extensions/orchestrator/task-lifecycle.ts +77 -67
- package/extensions/orchestrator/utils.ts +8 -5
- package/extensions/pidash/pidash.ts +193 -156
- package/extensions/pidiff/pidiff.ts +96 -38
- package/extensions/pidiff/spawn-lock.ts +62 -0
- package/extensions/pitasks/index.ts +430 -0
- package/extensions/pitasks/reminders.ts +37 -0
- package/extensions/pitasks/task-store.ts +467 -0
- package/extensions/pitasks/task-tools.ts +375 -0
- package/extensions/pitasks/task-widget.ts +181 -0
- package/extensions/pitasks/types.ts +38 -0
- package/extensions/providers/acpx-driver.ts +12 -2
- package/extensions/providers/built-in-drivers.ts +6 -6
- package/extensions/providers/claude-driver.ts +1 -1
- package/extensions/providers/cursor-cli-driver.ts +1 -1
- package/extensions/providers/gemini-driver.ts +1 -1
- package/extensions/providers/index.ts +144 -26
- package/extensions/providers/initialized-guard.ts +27 -0
- package/extensions/providers/restore-default-model.ts +596 -0
- package/extensions/providers/session-shutdown.ts +39 -0
- package/extensions/shared/coms-active.ts +23 -0
- package/extensions/shared/create-runtime-provider.ts +2 -0
- package/extensions/shared/daemon-manager.ts +0 -8
- package/extensions/shared/file-logger.ts +132 -25
- package/extensions/shared/logger.ts +50 -0
- package/extensions/shared/models-dev.ts +421 -0
- package/extensions/shared/oneshot.ts +116 -0
- package/extensions/shared/resolve-binary.ts +191 -0
- package/extensions/shared/types.ts +3 -0
- package/package.json +9 -3
- package/prompts/coderabbit-rate-limit.md +2 -0
- package/rules/00-orchestrator-core.md +3 -0
- package/rules/10-agent-routing.md +12 -1
- package/rules/20-code-review-loop.md +52 -25
- package/rules/30-prompt-templates.md +4 -0
- package/rules/35-memory.md +3 -3
- package/rules/40-critical-rules.md +3 -0
- package/rules/55-coms-protocol.md +84 -18
- package/rules/60-task-tracking.md +2 -0
- package/settings-keys.json +563 -0
- package/templates/coms-feature-manager-prompt.md +4 -6
- package/extensions/coms/coms-net-server.ts +0 -1642
- package/extensions/coms/coms-net-wrapper.ts +0 -521
- package/extensions/coms/coms-net.ts +0 -1589
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oneshot argv helpers — skip session extras so `pi -p` / `--mode json` can exit.
|
|
3
|
+
*
|
|
4
|
+
* Lives in shared/ so coms/pidash/pidiff/pitasks can import without the
|
|
5
|
+
* orchestrator utils module. Keep ONESHOT_VALUE_FLAGS in sync with pi
|
|
6
|
+
* `parseArgs` (`@earendil-works/pi-coding-agent` `dist/cli/args.js`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createLogger } from "./logger.js";
|
|
10
|
+
|
|
11
|
+
const log = createLogger("oneshot");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Value-taking flags from pi `parseArgs`. `--mode` is handled separately.
|
|
15
|
+
* Next token is always a value, even if it starts with `-` (including `--`).
|
|
16
|
+
*/
|
|
17
|
+
const ONESHOT_VALUE_FLAGS = new Set([
|
|
18
|
+
"--model",
|
|
19
|
+
"--models",
|
|
20
|
+
"--provider",
|
|
21
|
+
"--api-key",
|
|
22
|
+
"--system-prompt",
|
|
23
|
+
"--append-system-prompt",
|
|
24
|
+
"--name",
|
|
25
|
+
"-n",
|
|
26
|
+
"--session",
|
|
27
|
+
"--session-id",
|
|
28
|
+
"--fork",
|
|
29
|
+
"--session-dir",
|
|
30
|
+
"--tools",
|
|
31
|
+
"-t",
|
|
32
|
+
"--exclude-tools",
|
|
33
|
+
"-xt",
|
|
34
|
+
"--thinking",
|
|
35
|
+
"--export",
|
|
36
|
+
"--extension",
|
|
37
|
+
"-e",
|
|
38
|
+
"--skill",
|
|
39
|
+
"--prompt-template",
|
|
40
|
+
"--theme",
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const PI_MODES = new Set(["text", "json", "rpc"]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* True when argv is a oneshot pi invocation (`-p` / `--print` / `--mode json`).
|
|
47
|
+
* Session extras must not register — watchers and sockets keep the Node event
|
|
48
|
+
* loop alive after the reply.
|
|
49
|
+
*
|
|
50
|
+
* Mirrors pi `parseArgs` + `resolveAppMode` (without TTY): last valid
|
|
51
|
+
* `--mode <text|json|rpc>` wins; rpc is never oneshot; json is; print flag
|
|
52
|
+
* is oneshot unless last mode is rpc. `--mode=json` / `--mode=rpc` are unknown
|
|
53
|
+
* flags in pi (not mode). Value-aware so `--mode -p` does not treat `-p`
|
|
54
|
+
* as the print flag. No `--` end-of-options (parseArgs has none).
|
|
55
|
+
*
|
|
56
|
+
* Debug log uses isDebugEnabled() (same log_oneshot / PI_LOG_ONESHOT sources
|
|
57
|
+
* as fileLog). Level is cached 30s so register/argv scan does not re-stat.
|
|
58
|
+
*
|
|
59
|
+
* CLI/ACPX providers still load. Non-TTY stdin or stdout print (`echo | pi`,
|
|
60
|
+
* `pi | cat`) without those flags is not detected here — argv has no flags;
|
|
61
|
+
* use `ctx.mode` after session_start.
|
|
62
|
+
*/
|
|
63
|
+
export function isPiOneshotInvocation(argv: string[] = process.argv): boolean {
|
|
64
|
+
const args = argv.slice(2);
|
|
65
|
+
let lastMode: string | undefined;
|
|
66
|
+
let print = false;
|
|
67
|
+
for (let i = 0; i < args.length; i++) {
|
|
68
|
+
const arg = args[i];
|
|
69
|
+
if (arg === "-p" || arg === "--print") {
|
|
70
|
+
print = true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (arg === "--mode") {
|
|
74
|
+
const value = args[i + 1];
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
i += 1;
|
|
77
|
+
if (PI_MODES.has(value)) lastMode = value;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (ONESHOT_VALUE_FLAGS.has(arg) && args[i + 1] !== undefined) {
|
|
82
|
+
i += 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const oneshot = lastMode === "rpc" ? false : lastMode === "json" || print;
|
|
86
|
+
if (log.isDebugEnabled()) {
|
|
87
|
+
log.debug("oneshot argv", { oneshot, lastMode, print });
|
|
88
|
+
}
|
|
89
|
+
return oneshot;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** True when pitasks/pidash/pidiff/coms should skip register. Caller returns if true. */
|
|
93
|
+
export function shouldSkipOneshotRegister(
|
|
94
|
+
logger: { info: (msg: string) => void },
|
|
95
|
+
argv: string[] = process.argv,
|
|
96
|
+
): boolean {
|
|
97
|
+
if (!isPiOneshotInvocation(argv)) return false;
|
|
98
|
+
logger.info("skip register: oneshot print/json");
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Shutdown dream runs `runDreamAsync` → `spawnAsyncAgent` (not detached/unref'd).
|
|
104
|
+
* Skip when argv is oneshot OR session `mode` is print/json so `pi -p` can exit.
|
|
105
|
+
* Does not skip rpc/tui unless argv is oneshot (`-p` + `--mode rpc` is not oneshot).
|
|
106
|
+
*/
|
|
107
|
+
export function shouldSkipOneshotShutdownDream(
|
|
108
|
+
mode?: string | null,
|
|
109
|
+
argv: string[] = process.argv,
|
|
110
|
+
): boolean {
|
|
111
|
+
const oneshot = isPiOneshotInvocation(argv);
|
|
112
|
+
const printOrJson = mode === "print" || mode === "json";
|
|
113
|
+
const skip = oneshot || printOrJson;
|
|
114
|
+
log.debug("skip shutdown dream?", { skip, oneshot, mode });
|
|
115
|
+
return skip;
|
|
116
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared binary resolution helpers.
|
|
3
|
+
*
|
|
4
|
+
* In-process PATH scan for resolving CLI binaries. Avoids spawnSync("which")
|
|
5
|
+
* on auth/filter hot paths. Used by both CLI and ACPX provider drivers.
|
|
6
|
+
*
|
|
7
|
+
* Moved from cli-provider/shared/discover-cache.ts so both provider families
|
|
8
|
+
* can import without cross-provider dependencies.
|
|
9
|
+
*
|
|
10
|
+
* @module shared/resolve-binary
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
accessSync,
|
|
15
|
+
constants,
|
|
16
|
+
realpathSync,
|
|
17
|
+
statSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { extname, isAbsolute, join, posix, win32 } from "node:path";
|
|
20
|
+
|
|
21
|
+
/** Cache keyed by `${platform}\0${binary}\0${PATH}\0${PATHEXT}` — successful resolves only. */
|
|
22
|
+
const resolveBinaryCache = new Map<string, string>();
|
|
23
|
+
|
|
24
|
+
export type ResolveBinaryEnv = {
|
|
25
|
+
PATH?: string;
|
|
26
|
+
PATHEXT?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Parse PATHEXT into uppercase extensions (e.g. ".EXE"). Exported for tests. */
|
|
30
|
+
export function parsePathext(pathextEnv?: string): string[] {
|
|
31
|
+
const raw = pathextEnv ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
|
|
32
|
+
return raw
|
|
33
|
+
.split(";")
|
|
34
|
+
.map((s) => s.trim())
|
|
35
|
+
.filter(Boolean)
|
|
36
|
+
.map((s) => (s.startsWith(".") ? s : `.${s}`).toUpperCase());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* True when filePath has an extension listed in PATHEXT (case-insensitive).
|
|
41
|
+
* Extensionless files are not CreateProcess-launchable on Windows.
|
|
42
|
+
*/
|
|
43
|
+
export function isLaunchableWin32(
|
|
44
|
+
filePath: string,
|
|
45
|
+
pathextEnv?: string,
|
|
46
|
+
): boolean {
|
|
47
|
+
const ext = extname(filePath);
|
|
48
|
+
if (!ext) return false;
|
|
49
|
+
return parsePathext(pathextEnv).includes(ext.toUpperCase());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Suffixes to try when scanning PATH for `binary`.
|
|
54
|
+
* On win32: if `binary` already has a PATHEXT extension, try as-is only;
|
|
55
|
+
* otherwise try each PATHEXT suffix (no bare/empty suffix).
|
|
56
|
+
*/
|
|
57
|
+
export function candidateSuffixesFor(
|
|
58
|
+
binary: string,
|
|
59
|
+
platform: NodeJS.Platform = process.platform,
|
|
60
|
+
pathextEnv?: string,
|
|
61
|
+
): string[] {
|
|
62
|
+
if (platform !== "win32") return [""];
|
|
63
|
+
const ext = extname(binary);
|
|
64
|
+
if (ext && parsePathext(pathextEnv).includes(ext.toUpperCase())) {
|
|
65
|
+
return [""];
|
|
66
|
+
}
|
|
67
|
+
return parsePathext(pathextEnv);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* PATH list separator for the *resolved* platform (not the host OS).
|
|
72
|
+
* Needed so win32 simulation on Unix splits semicolon-separated PATH correctly.
|
|
73
|
+
*/
|
|
74
|
+
export function pathDelimiterFor(platform: NodeJS.Platform): string {
|
|
75
|
+
return platform === "win32" ? win32.delimiter : posix.delimiter;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Exported for tests — regular file + platform launchability rules. */
|
|
79
|
+
export function isExecutableForPlatform(
|
|
80
|
+
filePath: string,
|
|
81
|
+
platform: NodeJS.Platform,
|
|
82
|
+
pathextEnv?: string,
|
|
83
|
+
): boolean {
|
|
84
|
+
let st;
|
|
85
|
+
try {
|
|
86
|
+
st = statSync(filePath);
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
// Directories are often X_OK (searchable); only regular files are binaries.
|
|
91
|
+
if (!st.isFile()) return false;
|
|
92
|
+
// Win32: never treat any regular file as executable — require PATHEXT.
|
|
93
|
+
if (platform === "win32") return isLaunchableWin32(filePath, pathextEnv);
|
|
94
|
+
try {
|
|
95
|
+
accessSync(filePath, constants.X_OK);
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function finalizeResolved(filePath: string): string {
|
|
103
|
+
try {
|
|
104
|
+
return realpathSync(filePath);
|
|
105
|
+
} catch {
|
|
106
|
+
return filePath;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function cacheKeyFor(
|
|
111
|
+
binary: string,
|
|
112
|
+
platform: NodeJS.Platform,
|
|
113
|
+
pathEnv: string,
|
|
114
|
+
pathextEnv: string,
|
|
115
|
+
): string {
|
|
116
|
+
return `${platform}\0${binary}\0${pathEnv}\0${pathextEnv}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a binary for an explicit platform (injectable for win32 tests on Unix).
|
|
121
|
+
* Absolute paths still require a launchable PATHEXT extension on win32.
|
|
122
|
+
*/
|
|
123
|
+
export function resolveBinaryForPlatform(
|
|
124
|
+
binary: string,
|
|
125
|
+
platform: NodeJS.Platform,
|
|
126
|
+
env: ResolveBinaryEnv = {},
|
|
127
|
+
): string | null {
|
|
128
|
+
if (!binary) return null;
|
|
129
|
+
|
|
130
|
+
const pathEnv = env.PATH ?? process.env.PATH ?? "";
|
|
131
|
+
const pathextEnv =
|
|
132
|
+
env.PATHEXT ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
|
|
133
|
+
const cacheKey = cacheKeyFor(binary, platform, pathEnv, pathextEnv);
|
|
134
|
+
const cached = resolveBinaryCache.get(cacheKey);
|
|
135
|
+
if (cached !== undefined) {
|
|
136
|
+
if (isExecutableForPlatform(cached, platform, pathextEnv)) return cached;
|
|
137
|
+
resolveBinaryCache.delete(cacheKey);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const isExec = (p: string) =>
|
|
141
|
+
isExecutableForPlatform(p, platform, pathextEnv);
|
|
142
|
+
|
|
143
|
+
// Absolute or explicit relative path — check directly (mirrors `which /path`).
|
|
144
|
+
if (isAbsolute(binary) || binary.includes("/") || binary.includes("\\")) {
|
|
145
|
+
if (!isExec(binary)) return null;
|
|
146
|
+
const resolved = finalizeResolved(binary);
|
|
147
|
+
resolveBinaryCache.set(cacheKey, resolved);
|
|
148
|
+
return resolved;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const suffixes = candidateSuffixesFor(binary, platform, pathextEnv);
|
|
152
|
+
const envDelimiter = pathDelimiterFor(platform);
|
|
153
|
+
for (const dir of pathEnv.split(envDelimiter)) {
|
|
154
|
+
if (!dir) continue;
|
|
155
|
+
for (const suffix of suffixes) {
|
|
156
|
+
// PATHEXT is typically uppercase; Windows FS is case-insensitive.
|
|
157
|
+
// On case-sensitive hosts (win32 simulation in tests), also try lowercase.
|
|
158
|
+
const variants =
|
|
159
|
+
platform === "win32" && suffix && suffix !== suffix.toLowerCase()
|
|
160
|
+
? [binary + suffix, binary + suffix.toLowerCase()]
|
|
161
|
+
: [binary + suffix];
|
|
162
|
+
for (const name of variants) {
|
|
163
|
+
const candidate = join(dir, name);
|
|
164
|
+
if (!isExec(candidate)) continue;
|
|
165
|
+
const resolved = finalizeResolved(candidate);
|
|
166
|
+
resolveBinaryCache.set(cacheKey, resolved);
|
|
167
|
+
return resolved;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Do not cache misses — mid-session install with same PATH must rediscover.
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve a binary name (or absolute path) via an in-process PATH scan.
|
|
178
|
+
* Avoids spawnSync("which") on auth/filter hot paths.
|
|
179
|
+
* Returns realpath when possible, null if missing.
|
|
180
|
+
*/
|
|
181
|
+
export function resolveBinary(binary: string): string | null {
|
|
182
|
+
return resolveBinaryForPlatform(binary, process.platform, {
|
|
183
|
+
PATH: process.env.PATH,
|
|
184
|
+
PATHEXT: process.env.PATHEXT,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Clear PATH resolve cache (tests / PATH mutation). */
|
|
189
|
+
export function clearResolveBinaryCache(): void {
|
|
190
|
+
resolveBinaryCache.clear();
|
|
191
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-orchestrator-config",
|
|
3
|
-
"version": "4.2
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "Orchestrator pattern for pi: specialist subagents, enforcement, code review loop, workflow commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
7
7
|
],
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"workspaces": [
|
|
9
|
+
"workspaces": [
|
|
10
|
+
"packages/pi-sidecar",
|
|
11
|
+
"packages/pi-vertex-claude"
|
|
12
|
+
],
|
|
10
13
|
"files": [
|
|
11
14
|
"extensions/acpx-provider/",
|
|
12
15
|
"extensions/cli-provider/",
|
|
@@ -17,17 +20,20 @@
|
|
|
17
20
|
"extensions/image-gen/",
|
|
18
21
|
"extensions/pidash/*.ts",
|
|
19
22
|
"extensions/pidiff/*.ts",
|
|
23
|
+
"extensions/pitasks/",
|
|
20
24
|
"agents/",
|
|
21
25
|
"rules/",
|
|
22
26
|
"prompts/",
|
|
23
27
|
"templates/",
|
|
24
|
-
"skills/"
|
|
28
|
+
"skills/",
|
|
29
|
+
"settings-keys.json"
|
|
25
30
|
],
|
|
26
31
|
"dependencies": {
|
|
27
32
|
"@huggingface/transformers": "^4.2.0",
|
|
28
33
|
"acpx": ">=0.12.0 <1",
|
|
29
34
|
"chokidar": "^5.0.0",
|
|
30
35
|
"discord.js": "^14.0.0",
|
|
36
|
+
"strip-json-comments": "^5.0.3",
|
|
31
37
|
"ws": "^8.0.0"
|
|
32
38
|
},
|
|
33
39
|
"devDependencies": {
|
|
@@ -72,6 +72,8 @@ This outputs JSON to stdout. Parse the result:
|
|
|
72
72
|
|
|
73
73
|
- If `rate_limited` is `false` — notify user "Not rate limited" and exit
|
|
74
74
|
- If `rate_limited` is `true` — read `wait_seconds` and proceed to Phase 3
|
|
75
|
+
- If `fallback` is `true`, the wait time is **estimated** from the comment timestamp
|
|
76
|
+
(1-hour window), not parsed from the comment body. Warn the user that timing is approximate.
|
|
75
77
|
|
|
76
78
|
### Phase 3: Wait and Trigger Review
|
|
77
79
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
{{IF:orchestrator_edit_write_block}}
|
|
12
|
+
|
|
11
13
|
## Delegation Model
|
|
12
14
|
|
|
13
15
|
The orchestrator delegates — never implements directly.
|
|
@@ -28,6 +30,7 @@ Outside slash commands, never use edit, write, or bash directly (except `mcpl`)
|
|
|
28
30
|
**Never delegate:** slash commands — execute them directly (see slash command rules).
|
|
29
31
|
|
|
30
32
|
---
|
|
33
|
+
{{/IF}}
|
|
31
34
|
|
|
32
35
|
## Before Implementation (MANDATORY)
|
|
33
36
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
| Debugging | `debugger` |
|
|
20
20
|
| API docs | `api-documenter` |
|
|
21
21
|
| External repo security audit | `security-auditor` |
|
|
22
|
-
| External AI agents (cursor, codex, gemini, claude, copilot, etc.) | `/acpx-prompt` |
|
|
22
|
+
| {{IF:external_ai_agents}}External AI agents (cursor, codex, gemini, claude, copilot, etc.){{/IF}} | {{IF:external_ai_agents}}`/acpx-prompt`{{/IF}} |
|
|
23
23
|
| External library/framework docs (React, FastAPI, Django, etc.) | `docs-fetcher` |
|
|
24
24
|
|
|
25
25
|
## Routing by Intent, Not Tool
|
|
@@ -56,3 +56,14 @@ Some agents are dispatched internally by rules or prompt templates, not by the r
|
|
|
56
56
|
## Fallback
|
|
57
57
|
|
|
58
58
|
**Fallback:** No specialist? → `worker` agent
|
|
59
|
+
|
|
60
|
+
## Model Override
|
|
61
|
+
|
|
62
|
+
When the user asks to run an agent with a specific model:
|
|
63
|
+
|
|
64
|
+
1. Use `list_models` tool to discover available providers and models.
|
|
65
|
+
2. Pass the model to `subagent(model="provider/model-id")`.
|
|
66
|
+
3. Format: `provider/model-id` (e.g., `litellm/claude-opus-4-6-1m`) or just `model-id` (overrides model only; provider follows normal resolution chain).
|
|
67
|
+
4. For parallel tasks, set `model` per-task in the tasks array. Top-level `model` is used as fallback for tasks without their own.
|
|
68
|
+
5. Chain steps share the top-level `model` — no per-step override.
|
|
69
|
+
6. Explicit `model` param overrides agent_overrides, frontmatter, and settings.
|
|
@@ -4,6 +4,7 @@ After ANY code change, send to ALL 6 agents (5 reviewers + test-automator) IN PA
|
|
|
4
4
|
|
|
5
5
|
## Commit enforcement
|
|
6
6
|
|
|
7
|
+
{{IF:review_loop_enforcement}}
|
|
7
8
|
When `review_loop_enforcement` is enabled, the enforcement rule in `enforcement.ts` blocks `git commit` unless:
|
|
8
9
|
|
|
9
10
|
- Review status is `clean` (all reviewers returned 0 findings)
|
|
@@ -17,23 +18,26 @@ call `addReviewerPending`/`recordReviewerResult` from bash, write to the JSON fi
|
|
|
17
18
|
or use any method to fake a clean state. The enforcement system detects and blocks this.
|
|
18
19
|
The ONLY way to reach `clean` status is to run the actual review agents.
|
|
19
20
|
|
|
20
|
-
Resolution
|
|
21
|
+
Resolution is handled automatically via project settings injection.
|
|
21
22
|
|
|
22
23
|
- MUST loop until clean or the max cycle cap is reached — see "Cycle Definition & Max Cycles" below.
|
|
24
|
+
{{/IF}}
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
{{IFNOT:review_loop_enforcement}}
|
|
27
|
+
Single review pass is sufficient — no commit blocking from the review loop.
|
|
28
|
+
Step 5a (fix/explain every finding) is optional: fix what you agree with, skip what you don't.
|
|
29
|
+
{{/IFNOT}}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
- No commit blocking
|
|
31
|
+
**All 6 agents are always called.** `review_loop_enforcement` controls whether the loop repeats.
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
`review_loop_max_cycles` caps **total** cycles including the first (see Cycle Definition below).
|
|
33
|
+
{{IF:review_loop_enforcement}}
|
|
34
|
+
When enabled, `review_loop_max_cycles` caps **total** cycles including the first (see Cycle Definition below).
|
|
31
35
|
|
|
32
36
|
### Cycle Definition & Max Cycles
|
|
33
37
|
|
|
34
38
|
One **cycle** = call all 6 reviewers (in parallel) → fix/explain findings → end of cycle.
|
|
35
39
|
|
|
36
|
-
**The cycle count IS the `cycle` field in `pi-config-review-state.
|
|
40
|
+
**The cycle count IS the `cycle` field in `pi-config-review-state.jsonl`** — the same counter shown by
|
|
37
41
|
`/review-status` and the review UI status card. It increments each time reviewers are (re-)dispatched
|
|
38
42
|
for a fresh pass. Step 5's branches below MUST compare against that same counter — do not track cycles
|
|
39
43
|
mentally or separately.
|
|
@@ -46,8 +50,8 @@ Disable the review loop via `review_loop_enforcement: false` — not via max_cyc
|
|
|
46
50
|
Cap check is after 5a; it only blocks `go to 2` (re-dispatch of step 2 / all 6 agents,
|
|
47
51
|
including test-automator), not completing the current cycle's fix/explain.
|
|
48
52
|
|
|
49
|
-
**After 5a completes on the max cycle, two outcomes
|
|
50
|
-
|
|
53
|
+
**After 5a completes on the max cycle, two outcomes** (report them; commit may still be
|
|
54
|
+
allowed via the max-cycle path — see step 6):
|
|
51
55
|
|
|
52
56
|
- **Not fixed** (explained why not) → outstanding — report them.
|
|
53
57
|
- **Fixed** (verification blocked by the cap) → cannot re-dispatch to confirm clean — report that
|
|
@@ -64,24 +68,30 @@ cycle3: dispatch → findings → 5a (fix|explain) → cycle >= max → stop (no
|
|
|
64
68
|
```text
|
|
65
69
|
1. Specialist writes/fixes code
|
|
66
70
|
2. Send ALL 6 agents IN PARALLEL (async): 5 reviewers + test-automator
|
|
71
|
+
**On cycle 2+: MANDATORY — include prior cycle findings + responses in each reviewer's prompt (see Step 5a format).**
|
|
72
|
+
Reviewers without this context will blindly repeat the same findings. This is not optional.
|
|
67
73
|
3. Wait for all 6 to complete
|
|
68
74
|
4. Merge & deduplicate review findings
|
|
69
75
|
5. Has findings OR tests failed?
|
|
70
76
|
── Neither? ↓ status: clean, tests_passed: true
|
|
71
77
|
── Findings and/or tests failed? → ALWAYS run 5a first: fix code OR explain why not, for EACH finding;
|
|
72
|
-
fix code for failing tests. Then check state.cycle (from `pi-config-review-state.
|
|
78
|
+
fix code for failing tests. Then check state.cycle (from `pi-config-review-state.jsonl`):
|
|
73
79
|
· cycle < {{REVIEW_LOOP_MAX_CYCLES}}? → go to 2 (re-run all 6 with prior findings + responses)
|
|
74
80
|
· cycle >= {{REVIEW_LOOP_MAX_CYCLES}}? → **STOP** (terminal): report the two-outcome result
|
|
75
81
|
(see "Cycle Definition & Max Cycles" above). Do NOT proceed to step 6.
|
|
76
|
-
6. NOW you can commit — the pre-commit hook will pass
|
|
77
|
-
|
|
82
|
+
6. NOW you can commit — the pre-commit hook will pass when:
|
|
83
|
+
- status is `clean` AND `tests_passed: true`, OR
|
|
84
|
+
- max cycles exhausted (`has_findings` or `clean`, no pending reviewers, `cycle >= review_loop_max_cycles` — no `tests_passed` check)
|
|
85
|
+
✅ DONE — commit/push allowed
|
|
78
86
|
```
|
|
79
87
|
|
|
80
|
-
🚨 **Step 6 applies
|
|
88
|
+
🚨 **Step 6 applies when clean OR max cycles exhausted.** Do NOT attempt `git commit` before that.
|
|
81
89
|
The enforcement rule blocks commits until this is satisfied — if it blocks you, run the reviewers.
|
|
82
90
|
**After a cap stop:** report the two-outcome result (**Not fixed** → outstanding, **Fixed** → verification
|
|
83
|
-
blocked);
|
|
91
|
+
blocked); commit is allowed when status is `has_findings` or `clean`, no reviewers pending, and
|
|
92
|
+
`cycle >= review_loop_max_cycles` (no `tests_passed` requirement); do **not**
|
|
84
93
|
return to step 2 (re-dispatch of all 6 agents, including test-automator).
|
|
94
|
+
{{/IF}}
|
|
85
95
|
|
|
86
96
|
## Review Agents
|
|
87
97
|
|
|
@@ -100,7 +110,7 @@ Six agents run in parallel for comprehensive coverage:
|
|
|
100
110
|
Do NOT block waiting for results — continue working while they run.**
|
|
101
111
|
|
|
102
112
|
Send reviewers "Review the code changes" — never mention `git diff HEAD` in the task prompt.
|
|
103
|
-
Reviewers get `$PI_REVIEW_BASE_BRANCH` env var and use `git diff origin/$PI_REVIEW_BASE_BRANCH
|
|
113
|
+
Reviewers get `$PI_REVIEW_BASE_BRANCH` env var and use `git diff origin/$PI_REVIEW_BASE_BRANCH` themselves (includes uncommitted changes).
|
|
104
114
|
|
|
105
115
|
Reviewers return structured JSON: `{"findings": [{"severity": "...", "file": "...", "line": N, "description": "...", "suggestion": "..."}]}`.
|
|
106
116
|
The async runner validates the output is valid JSON and retries if not (up to 3 times).
|
|
@@ -118,7 +128,9 @@ reviewers, even on the same file/line. A quality finding about *how* code is wri
|
|
|
118
128
|
address *whether* the code matches the spec. Always keep both. Spec findings CAN still be
|
|
119
129
|
deduplicated against other spec findings when they are truly the same issue.
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
{{IF:review_loop_enforcement}}
|
|
132
|
+
|
|
133
|
+
## Step 5a: Respond to Findings
|
|
122
134
|
|
|
123
135
|
For each finding from step 4, do ONE of:
|
|
124
136
|
|
|
@@ -137,7 +149,7 @@ The following findings were raised in review cycle {N}. Each has been either fix
|
|
|
137
149
|
explained. Do NOT re-raise findings that were adequately addressed. Only re-raise if:
|
|
138
150
|
- The code fix is incorrect or incomplete
|
|
139
151
|
- The explanation is invalid or factually wrong
|
|
140
|
-
- You disagree with the reasoning (
|
|
152
|
+
- You disagree with the reasoning (state why in the finding description field)
|
|
141
153
|
|
|
142
154
|
Findings with valid explanations that you accept → do not re-raise.
|
|
143
155
|
Findings that were fixed in code → verify the fix, do not re-raise if correct.
|
|
@@ -151,29 +163,38 @@ Findings that were fixed in code → verify the fix, do not re-raise if correct.
|
|
|
151
163
|
- If a finding was fixed → verify the fix is correct, don't re-raise
|
|
152
164
|
- If a finding was explained with a valid technical reason → accept it, don't re-raise
|
|
153
165
|
- If the explanation is wrong or the fix is incomplete → re-raise with specific pushback
|
|
154
|
-
|
|
155
|
-
**When `review_loop_enforcement` is disabled:** Step 5a is optional. Single review pass
|
|
156
|
-
is sufficient — fix what you agree with, skip what you don't. No need to explain skips.
|
|
166
|
+
{{/IF}}
|
|
157
167
|
|
|
158
168
|
## Key Rules
|
|
159
169
|
|
|
160
170
|
Never skip code review — all 6 agents always run (5 reviewers + test-automator).
|
|
171
|
+
Exception: on `chore/bump-version` branches (release version bumps), review dirty-tracking and commit blocking are skipped.
|
|
172
|
+
{{IF:review_loop_enforcement}}
|
|
161
173
|
When `review_loop_enforcement` is enabled: loop, respond to each finding (fix or explain), and re-run all 6 from step 2
|
|
162
174
|
until clean or the cycle cap is reached — see "Cycle Definition & Max Cycles" above for the exact stop conditions.
|
|
163
|
-
|
|
175
|
+
{{/IF}}
|
|
176
|
+
{{IFNOT:review_loop_enforcement}}
|
|
177
|
+
When `review_loop_enforcement` is disabled: single pass is sufficient; no mandatory re-loop or explanations.
|
|
178
|
+
{{/IFNOT}}
|
|
164
179
|
Minor test/config-only fixes skip re-review (go to step 5); substantive code changes require full re-review.
|
|
165
180
|
|
|
166
181
|
## Test Tracking
|
|
167
182
|
|
|
168
|
-
Test results are tracked in `pi-config-review-state.
|
|
169
|
-
|
|
183
|
+
Test results are tracked in `pi-config-review-state.jsonl` via the `tests_passed` field.
|
|
184
|
+
|
|
185
|
+
{{IF:review_loop_enforcement}}
|
|
186
|
+
This is **code-enforced** — commit/push is blocked unless `tests_passed: true`.
|
|
187
|
+
{{/IF}}
|
|
188
|
+
{{IFNOT:review_loop_enforcement}}
|
|
189
|
+
Tests still run and `tests_passed` is still recorded; commit is not blocked by review state when enforcement is off.
|
|
190
|
+
{{/IFNOT}}
|
|
170
191
|
|
|
171
192
|
**How `tests_passed` gets set:**
|
|
172
193
|
|
|
173
194
|
- **Bash hook detection:** When any agent runs a test command (`pytest`, `npm test`, `npx tsx --test`, `tox`, `go test`, `vitest`, `jest`, `mocha`),
|
|
174
195
|
the enforcement hook detects it and auto-marks `tests_passed` based on exit code.
|
|
175
196
|
- **Agent completion:** When `test-automator` or `test-runner` agents complete, their result auto-marks `tests_passed`.
|
|
176
|
-
- **Reset on edit:** Any file edit triggers `markNeedsReview()
|
|
197
|
+
- **Reset on edit:** Any file edit triggers `markNeedsReview()` (except on `chore/bump-version` branches), which resets `tests_passed: false` —
|
|
177
198
|
preventing stale results.
|
|
178
199
|
|
|
179
200
|
**Duplicate test run avoidance:** If a specialist already ran tests before the review loop, `tests_passed` may already be `true`.
|
|
@@ -215,20 +236,26 @@ If both `git apply` methods fail, skip baseline comparison and note "baseline co
|
|
|
215
236
|
For automated review flows (autorabbit, autoqodo), use **two-stage order** instead of parallel:
|
|
216
237
|
|
|
217
238
|
1. **Stage 1 — Spec Compliance:** Does code meet requirements? All deliverables implemented? No scope creep?
|
|
239
|
+
{{IF:review_loop_enforcement}}
|
|
218
240
|
Loop Stage 1 until passed OR the shared cycle cap is reached.
|
|
241
|
+
{{/IF}}
|
|
219
242
|
2. **Stage 2 — Code Quality:** Quality, security, guidelines adherence.
|
|
243
|
+
{{IF:review_loop_enforcement}}
|
|
220
244
|
Loop Stage 2 until passed OR the shared cycle cap is reached.
|
|
245
|
+
{{/IF}}
|
|
221
246
|
|
|
247
|
+
{{IF:review_loop_enforcement}}
|
|
222
248
|
**Staged cycle definition:** one **cycle** = one pass of the *current* stage's reviewers (in parallel)
|
|
223
249
|
→ fix/explain that stage's findings → end of cycle. This mirrors the parallel-mode cycle definition
|
|
224
250
|
(see "Cycle Definition & Max Cycles" above), scoped to one stage's reviewers instead of all 6.
|
|
225
251
|
|
|
226
252
|
**The `review_loop_max_cycles` cap is shared across both stages — one total budget, not one per stage.**
|
|
227
253
|
Stage 1 and Stage 2 both draw from and increment the same `cycle` counter in
|
|
228
|
-
`pi-config-review-state.
|
|
254
|
+
`pi-config-review-state.jsonl`. Cap check, 5a-before-cap, and two-outcome reporting follow
|
|
229
255
|
"Cycle Definition & Max Cycles" above. Stage-specific stops: if Stage 1 is **not clean** when the cap is hit, stop entirely — do not proceed
|
|
230
256
|
to Stage 2 (covers both **Not fixed** and **Fixed** (verification blocked)). If hit during Stage 2,
|
|
231
257
|
stop looping Stage 2.
|
|
258
|
+
{{/IF}}
|
|
232
259
|
|
|
233
260
|
Don't polish code that doesn't meet spec — it wastes work.
|
|
234
261
|
Parallel mode (all 6 agents at once) remains default for manual reviews.
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
3. **The prompt decides** when to run directly and when to delegate to agents
|
|
8
8
|
4. If the prompt says "run this bash command" — run it directly
|
|
9
9
|
5. If the prompt says "delegate to X agent" — delegate
|
|
10
|
+
{{IF:orchestrator_edit_write_block}}
|
|
10
11
|
6. If the prompt doesn't specify — follow normal orchestrator rules (delegate to specialists)
|
|
12
|
+
{{/IF}}
|
|
11
13
|
|
|
12
14
|
## Key Rules
|
|
13
15
|
|
|
14
16
|
- The orchestrator **maintains control** of the prompt workflow
|
|
17
|
+
{{IF:orchestrator_edit_write_block}}
|
|
15
18
|
- The prompt template's instructions **override** general delegation rules when they conflict
|
|
19
|
+
{{/IF}}
|
|
16
20
|
- **NEVER** delegate the `/command` itself to an agent — only delegate sub-tasks when the prompt says to
|
|
17
21
|
|
|
18
22
|
❌ **WRONG**: `/mycommand` → delegate entire prompt to an agent
|
package/rules/35-memory.md
CHANGED
|
@@ -56,7 +56,7 @@ Search past conversation summaries for references to previous sessions. Returns
|
|
|
56
56
|
|
|
57
57
|
```text
|
|
58
58
|
.pi/memory/
|
|
59
|
-
├── memory-scores.
|
|
59
|
+
├── memory-scores.jsonl # Scoring backend (auto-managed)
|
|
60
60
|
└── topics/
|
|
61
61
|
├── preferences.md # [preference] entries
|
|
62
62
|
├── lessons.md # [lesson] entries
|
|
@@ -228,8 +228,8 @@ On reinforce crossing a threshold, a promotion pass runs automatically.
|
|
|
228
228
|
|
|
229
229
|
`memory_add` / `memory_edit` accept optional `sourceSession`, `derivedFrom`, `informs`.
|
|
230
230
|
Preference extractor sets `sourceSession` when the session id is available.
|
|
231
|
-
Dream may write `.pi/memory/provenance-pending.
|
|
232
|
-
Stored in `memory-scores.
|
|
231
|
+
Dream may write `.pi/memory/provenance-pending.jsonl`; merge happens on dream complete.
|
|
232
|
+
Stored in `memory-scores.jsonl` only — not injected into the situation report.
|
|
233
233
|
Shown in `memory_search` / `memory_reflect` when present.
|
|
234
234
|
|
|
235
235
|
---
|
|
@@ -49,6 +49,8 @@ Always pass `cwd` when delegating to subagents in all modes — omitting it caus
|
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
|
|
52
|
+
{{IF:use_worktrees}}
|
|
53
|
+
|
|
52
54
|
## Multi-PR / Multi-Branch Work
|
|
53
55
|
|
|
54
56
|
**NEVER switch branches in the main worktree** when working on multiple PRs — other agents may be running there.
|
|
@@ -60,6 +62,7 @@ git worktree remove .worktrees/pr-42 # when done
|
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
---
|
|
65
|
+
{{/IF}}
|
|
63
66
|
|
|
64
67
|
## User Interaction
|
|
65
68
|
|