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
|
@@ -109,6 +109,8 @@ export class OverlayListDashboard<
|
|
|
109
109
|
> implements Component {
|
|
110
110
|
private closed = false;
|
|
111
111
|
private ticker: ReturnType<typeof setInterval>;
|
|
112
|
+
private cachedWidth: number | undefined;
|
|
113
|
+
private cachedLines: string[] | undefined;
|
|
112
114
|
|
|
113
115
|
constructor(
|
|
114
116
|
private tui: TUI,
|
|
@@ -118,7 +120,7 @@ export class OverlayListDashboard<
|
|
|
118
120
|
private selection: OverlaySelection<TId>,
|
|
119
121
|
private done: (value: TId | null) => void,
|
|
120
122
|
) {
|
|
121
|
-
this.ticker = setInterval(() => this.tui.requestRender(), 1000);
|
|
123
|
+
this.ticker = setInterval(() => { this.invalidate(); this.tui.requestRender(); }, 1000);
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
private items(): TItem[] {
|
|
@@ -169,7 +171,8 @@ export class OverlayListDashboard<
|
|
|
169
171
|
this.selection.index =
|
|
170
172
|
(this.selection.index - 1 + items.length) % items.length;
|
|
171
173
|
this.selection.id = items[this.selection.index]?.id;
|
|
172
|
-
this.
|
|
174
|
+
this.invalidate();
|
|
175
|
+
this.tui.requestRender(true);
|
|
173
176
|
}
|
|
174
177
|
return;
|
|
175
178
|
}
|
|
@@ -181,7 +184,8 @@ export class OverlayListDashboard<
|
|
|
181
184
|
if (items.length > 0) {
|
|
182
185
|
this.selection.index = (this.selection.index + 1) % items.length;
|
|
183
186
|
this.selection.id = items[this.selection.index]?.id;
|
|
184
|
-
this.
|
|
187
|
+
this.invalidate();
|
|
188
|
+
this.tui.requestRender(true);
|
|
185
189
|
}
|
|
186
190
|
return;
|
|
187
191
|
}
|
|
@@ -193,12 +197,15 @@ export class OverlayListDashboard<
|
|
|
193
197
|
this.close(null);
|
|
194
198
|
return;
|
|
195
199
|
}
|
|
196
|
-
this.
|
|
200
|
+
this.invalidate();
|
|
201
|
+
this.tui.requestRender(true);
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
}
|
|
200
205
|
|
|
201
206
|
render(width: number): string[] {
|
|
207
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
208
|
+
|
|
202
209
|
const theme = this.theme;
|
|
203
210
|
const items = this.items();
|
|
204
211
|
reconcileSelection(this.selection, items);
|
|
@@ -243,6 +250,8 @@ export class OverlayListDashboard<
|
|
|
243
250
|
truncateToWidth(theme.fg("dim", ` ${this.spec.footerHints}`), width),
|
|
244
251
|
);
|
|
245
252
|
|
|
253
|
+
this.cachedLines = lines;
|
|
254
|
+
this.cachedWidth = width;
|
|
246
255
|
return lines;
|
|
247
256
|
}
|
|
248
257
|
|
|
@@ -290,7 +299,10 @@ export class OverlayListDashboard<
|
|
|
290
299
|
return out;
|
|
291
300
|
}
|
|
292
301
|
|
|
293
|
-
invalidate(): void {
|
|
302
|
+
invalidate(): void {
|
|
303
|
+
this.cachedWidth = undefined;
|
|
304
|
+
this.cachedLines = undefined;
|
|
305
|
+
}
|
|
294
306
|
}
|
|
295
307
|
|
|
296
308
|
export class OverlayScrollDetail implements Component {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Parse 'provider/model-id' into separate provider and model. Plain 'model-id' returns undefined provider. */
|
|
2
|
+
export function parseModelOverride(modelStr: string | undefined): { model?: string; provider?: string } | undefined {
|
|
3
|
+
if (!modelStr) return undefined;
|
|
4
|
+
const trimmed = modelStr.trim();
|
|
5
|
+
if (!trimmed) return undefined;
|
|
6
|
+
const slashIdx = trimmed.indexOf("/");
|
|
7
|
+
if (slashIdx > 0) {
|
|
8
|
+
const provider = trimmed.slice(0, slashIdx).trim();
|
|
9
|
+
const model = trimmed.slice(slashIdx + 1).trim();
|
|
10
|
+
if (!provider || !model) return undefined;
|
|
11
|
+
return { provider, model };
|
|
12
|
+
}
|
|
13
|
+
return { model: trimmed };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Merge task-level model override with top-level explicit fallback. Task fields win. */
|
|
17
|
+
export function mergeModelOverride(
|
|
18
|
+
taskModel: string | undefined,
|
|
19
|
+
explicit: { model?: string; provider?: string } | undefined,
|
|
20
|
+
): { model?: string; provider?: string } | undefined {
|
|
21
|
+
const taskParsed = parseModelOverride(taskModel);
|
|
22
|
+
if (taskParsed) {
|
|
23
|
+
return {
|
|
24
|
+
model: taskParsed.model || explicit?.model,
|
|
25
|
+
provider: taskParsed.provider || explicit?.provider,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return explicit;
|
|
29
|
+
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Review state machine — tracks code review loop status.
|
|
3
|
-
* State stored in <worktree-root>/.pi/data/pi-config-review-state.
|
|
3
|
+
* State stored in <worktree-root>/.pi/data/pi-config-review-state.jsonl (per-worktree, not shared).
|
|
4
4
|
* Each worktree gets its own state file via resolveWorktreeRoot (--show-toplevel).
|
|
5
5
|
* Used by enforcement to block git commit until all reviewers approve.
|
|
6
|
+
*
|
|
7
|
+
* Persistence: append-only JSONL via JsonlStateStore (issue #724).
|
|
8
|
+
* Each state mutation appends a single JSON line. The latest valid line
|
|
9
|
+
* is the current state. Auto-compaction prevents unbounded growth.
|
|
10
|
+
* On first access, migrates any legacy pi-config-review-state.json automatically.
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
|
-
import {
|
|
13
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
14
|
import { join } from "node:path";
|
|
15
|
+
import { getSetting } from "./project-settings.js";
|
|
16
|
+
import { createCachedStore } from "./state-jsonl.js";
|
|
17
|
+
import type { JsonlStateStore } from "./state-jsonl.js";
|
|
10
18
|
import { resolveWorktreeRoot, getProjectDataDir } from "./utils.js";
|
|
11
19
|
|
|
12
20
|
type StateTransitionCallback = (state: ReviewState) => void;
|
|
@@ -22,7 +30,7 @@ export function onStateTransition(cb: StateTransitionCallback): void {
|
|
|
22
30
|
function notifyTransition(state: ReviewState): void {
|
|
23
31
|
if (!onTransitionCb) return;
|
|
24
32
|
try { onTransitionCb({ ...state, reviewers_pending: [...state.reviewers_pending] }); }
|
|
25
|
-
catch
|
|
33
|
+
catch { /* transition callback failed — fire-and-forget, never propagate */ }
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
export interface ReviewState {
|
|
@@ -37,14 +45,16 @@ export interface ReviewState {
|
|
|
37
45
|
tests_passed: boolean;
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
const
|
|
48
|
+
const STATE_FILE_JSONL = "pi-config-review-state.jsonl";
|
|
49
|
+
const LEGACY_STATE_FILE = "pi-config-review-state.json";
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
function getStore(cwd: string): JsonlStateStore<ReviewState> {
|
|
52
|
+
const dataDir = getProjectDataDir(resolveWorktreeRoot(cwd));
|
|
53
|
+
return createCachedStore<ReviewState>(dataDir, STATE_FILE_JSONL, LEGACY_STATE_FILE);
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
function
|
|
47
|
-
getProjectDataDir(resolveWorktreeRoot(cwd));
|
|
56
|
+
export function statePath(cwd: string): string {
|
|
57
|
+
return join(getProjectDataDir(resolveWorktreeRoot(cwd)), STATE_FILE_JSONL);
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
function defaultState(): ReviewState {
|
|
@@ -61,29 +71,35 @@ function defaultState(): ReviewState {
|
|
|
61
71
|
};
|
|
62
72
|
}
|
|
63
73
|
|
|
74
|
+
/** Normalize a raw parsed object into a valid ReviewState with defaults for missing fields. */
|
|
75
|
+
function normalizeRawState(raw: any): ReviewState {
|
|
76
|
+
return {
|
|
77
|
+
status: raw.status ?? "none",
|
|
78
|
+
cycle: typeof raw.cycle === "number" ? raw.cycle : 0,
|
|
79
|
+
reviewers_pending: Array.isArray(raw.reviewers_pending) ? raw.reviewers_pending : [],
|
|
80
|
+
reviewers_total: typeof raw.reviewers_total === "number" ? raw.reviewers_total : 0,
|
|
81
|
+
findings_count: typeof raw.findings_count === "number" ? raw.findings_count : 0,
|
|
82
|
+
last_edit_at: typeof raw.last_edit_at === "string" ? raw.last_edit_at : null,
|
|
83
|
+
last_clean_at: typeof raw.last_clean_at === "string" ? raw.last_clean_at : null,
|
|
84
|
+
edited_during_cycle: raw.edited_during_cycle === true,
|
|
85
|
+
tests_passed: raw.tests_passed === true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
64
89
|
export function readReviewState(cwd: string): ReviewState {
|
|
65
|
-
const
|
|
66
|
-
|
|
90
|
+
const store = getStore(cwd);
|
|
91
|
+
const raw = store.read();
|
|
92
|
+
if (raw === null) return defaultState();
|
|
67
93
|
try {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
cycle: typeof raw.cycle === "number" ? raw.cycle : 0,
|
|
72
|
-
reviewers_pending: Array.isArray(raw.reviewers_pending) ? raw.reviewers_pending : [],
|
|
73
|
-
reviewers_total: typeof raw.reviewers_total === "number" ? raw.reviewers_total : 0,
|
|
74
|
-
findings_count: typeof raw.findings_count === "number" ? raw.findings_count : 0,
|
|
75
|
-
last_edit_at: typeof raw.last_edit_at === "string" ? raw.last_edit_at : null,
|
|
76
|
-
last_clean_at: typeof raw.last_clean_at === "string" ? raw.last_clean_at : null,
|
|
77
|
-
edited_during_cycle: raw.edited_during_cycle === true,
|
|
78
|
-
tests_passed: raw.tests_passed === true,
|
|
79
|
-
};
|
|
80
|
-
} catch (e: any) {
|
|
81
|
-
console.debug("[pi-config-review-state] failed to parse state:", e?.message);
|
|
94
|
+
return normalizeRawState(raw);
|
|
95
|
+
} catch {
|
|
96
|
+
// failed to parse state — return defaults
|
|
82
97
|
return defaultState();
|
|
83
98
|
}
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
function lockPath(cwd: string): string {
|
|
102
|
+
// Lock file sits next to the JSONL state file
|
|
87
103
|
return statePath(cwd) + ".lock";
|
|
88
104
|
}
|
|
89
105
|
|
|
@@ -92,7 +108,7 @@ function acquireLock(cwd: string): boolean {
|
|
|
92
108
|
const maxRetries = 100;
|
|
93
109
|
for (let i = 0; i < maxRetries; i++) {
|
|
94
110
|
try {
|
|
95
|
-
|
|
111
|
+
getProjectDataDir(resolveWorktreeRoot(cwd));
|
|
96
112
|
writeFileSync(lock, String(process.pid), { flag: "wx" });
|
|
97
113
|
return true;
|
|
98
114
|
} catch {
|
|
@@ -125,15 +141,11 @@ function releaseLock(cwd: string): void {
|
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
function writeState(cwd: string, state: ReviewState): void {
|
|
128
|
-
const p = statePath(cwd);
|
|
129
144
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
renameSync(tmp, p);
|
|
135
|
-
} catch (e: any) {
|
|
136
|
-
console.debug("[pi-config-review-state] write failed:", e?.message);
|
|
145
|
+
const store = getStore(cwd);
|
|
146
|
+
store.write(state);
|
|
147
|
+
} catch {
|
|
148
|
+
// write failed — non-fatal, state file may be stale
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
|
|
@@ -254,6 +266,18 @@ export function isReviewClean(cwd: string): boolean {
|
|
|
254
266
|
return true;
|
|
255
267
|
}
|
|
256
268
|
|
|
269
|
+
/** Check if commit is allowed — clean review, no tracking, or max cycles exhausted. */
|
|
270
|
+
export function isCommitAllowed(cwd: string): boolean {
|
|
271
|
+
if (isReviewClean(cwd)) return true;
|
|
272
|
+
const state = readReviewState(cwd);
|
|
273
|
+
// Allow commit when max review cycles exhausted — cap reached, no reviewers running
|
|
274
|
+
if ((state.status === "has_findings" || state.status === "clean" || state.status === "needs_review") && state.reviewers_pending.length === 0) {
|
|
275
|
+
const maxCycles = getSetting(cwd, "review_loop_max_cycles");
|
|
276
|
+
if (state.cycle >= maxCycles) return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
257
281
|
/** Mark that tests have passed. Only meaningful when review status is being tracked. */
|
|
258
282
|
export function markTestsPassed(cwd: string): void {
|
|
259
283
|
withStateLock(cwd, (state) => {
|