@ask-llm/plugin 0.15.0 → 0.16.3
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/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +979 -0
- package/README.md +2 -0
- package/agents/brainstorm-coordinator.md +1 -1
- package/agents/gemini-reviewer.md +1 -1
- package/dist/antigravity-run.js +0 -0
- package/dist/brainstorm-run.js +0 -0
- package/dist/codex-run.js +0 -0
- package/dist/grok-run.js +0 -0
- package/dist/ollama-run.js +0 -0
- package/dist/run.js +0 -0
- package/package.json +14 -14
- package/pi/extensions/provider-tools.ts +1 -1
- package/scripts/benchmark/README.md +114 -0
- package/scripts/benchmark/fixtures/README.md +29 -0
- package/scripts/codex-pair-debounce-worker.mjs +0 -0
- package/scripts/codex-pair-log.mjs +4 -13
- package/scripts/codex-pair-prompt-drain.mjs +1 -1
- package/scripts/codex-pair-session.mjs +2 -2
- package/scripts/codex-pair-stop-gate.mjs +8 -8
- package/scripts/codex-pair-watch.mjs +20 -39
- package/skills/gemini-review/SKILL.md +1 -1
- package/scripts/lib/broker-lifecycle.mjs +0 -575
- package/scripts/lib/broker-rpc.mjs +0 -203
- package/scripts/lib/broker-transport.mjs +0 -407
- package/scripts/lib/broker.mjs +0 -537
- package/scripts/lib/debounce-state.mjs +0 -208
- package/scripts/lib/parser.d.mts +0 -12
- package/scripts/lib/parser.mjs +0 -229
- package/scripts/lib/process.mjs +0 -56
- package/scripts/lib/prompt.d.mts +0 -8
- package/scripts/lib/prompt.mjs +0 -41
- package/scripts/lib/session-registry.mjs +0 -162
- package/scripts/lib/state.d.mts +0 -58
- package/scripts/lib/state.mjs +0 -733
- package/scripts/lib/stop-gate.mjs +0 -134
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
// Session-scoped marker registry (ADR-131, issue #209).
|
|
2
|
-
//
|
|
3
|
-
// The watch hook (PostToolUse) knows both the session_id and the EDITED repo's
|
|
4
|
-
// markerDir; the Stop / UserPromptSubmit hooks know only session_id + cwd. This
|
|
5
|
-
// registry bridges them: the watch hook records every project marker it touches
|
|
6
|
-
// this session, and the turn/prompt-scoped hooks read the set back so they drain
|
|
7
|
-
// + gate every repo active this session, not just cwd.
|
|
8
|
-
//
|
|
9
|
-
// Storage: <tmpdir>/codex-pair-sessions/<sha256(session)[:16]>/<sha256(marker)[:16]>.json
|
|
10
|
-
// One independent file per (session, project) — registration is a single
|
|
11
|
-
// idempotent write, so parallel watch fires for DIFFERENT repos never race
|
|
12
|
-
// (mirrors the per-file sharding of inflight/ and pending/, ADR-087/097).
|
|
13
|
-
//
|
|
14
|
-
// Zero workspace imports (marketplace git-subdir install has no node_modules).
|
|
15
|
-
// Every export is best-effort and MUST NOT throw (ADR-077).
|
|
16
|
-
|
|
17
|
-
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
18
|
-
import { createHash } from "node:crypto";
|
|
19
|
-
import { tmpdir } from "node:os";
|
|
20
|
-
import { join } from "node:path";
|
|
21
|
-
|
|
22
|
-
export const SESSION_REGISTRY_DIRNAME = "codex-pair-sessions";
|
|
23
|
-
// 7-day default. The sweep only reclaims crash-orphaned dirs (SessionEnd clears
|
|
24
|
-
// them normally), so a long TTL is cheap — and it shrinks the window in which one
|
|
25
|
-
// session's sweep could delete a *concurrent* live-but-idle session's registry
|
|
26
|
-
// (see the accepted-limitation note in ADR-131; the case self-corrects on that
|
|
27
|
-
// session's next edit). Override via CODEX_PAIR_SESSION_REGISTRY_TTL_MS.
|
|
28
|
-
const DEFAULT_SESSION_REGISTRY_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
29
|
-
// Guard a non-numeric env override: Number("nope") is NaN, which would make
|
|
30
|
-
// sweepStaleSessions' `newest < (now - NaN)` always false and silently disable
|
|
31
|
-
// the sweep (slow tmpdir leak). Mirrors the stop-gate's inflightFreshMs guard.
|
|
32
|
-
const ttlOverride = Number(process.env.CODEX_PAIR_SESSION_REGISTRY_TTL_MS);
|
|
33
|
-
export const SESSION_REGISTRY_TTL_MS =
|
|
34
|
-
Number.isFinite(ttlOverride) && ttlOverride > 0 ? ttlOverride : DEFAULT_SESSION_REGISTRY_TTL_MS;
|
|
35
|
-
|
|
36
|
-
// Registry root: always `<base>/codex-pair-sessions`, where <base> is
|
|
37
|
-
// CODEX_PAIR_SESSION_REGISTRY_ROOT (tests point it at an isolated fixture dir;
|
|
38
|
-
// containerized setups relocate it) or os.tmpdir() by default. The env var is a
|
|
39
|
-
// BASE dir, never used raw as the sweep root — sweepStaleSessions rmSyncs stale
|
|
40
|
-
// child dirs, so pointing the root straight at /tmp, $HOME, or a repo would let
|
|
41
|
-
// it delete unrelated data. Appending the dedicated SESSION_REGISTRY_DIRNAME
|
|
42
|
-
// confines every sweep to a codex-pair-owned subdir it can't escape.
|
|
43
|
-
export const sessionRegistryRoot = () =>
|
|
44
|
-
join(process.env.CODEX_PAIR_SESSION_REGISTRY_ROOT || tmpdir(), SESSION_REGISTRY_DIRNAME);
|
|
45
|
-
|
|
46
|
-
function hash16(value) {
|
|
47
|
-
return createHash("sha256").update(String(value)).digest("hex").slice(0, 16);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const sessionDir = (sessionId) => join(sessionRegistryRoot(), hash16(sessionId));
|
|
51
|
-
|
|
52
|
-
const markerEntryPath = (sessionId, markerDir) =>
|
|
53
|
-
join(sessionDir(sessionId), `${hash16(markerDir)}.json`);
|
|
54
|
-
|
|
55
|
-
// Record that `markerDir` saw activity in `sessionId`. Idempotent, best-effort.
|
|
56
|
-
// No-op when either argument is falsy (e.g. payload lacked session_id).
|
|
57
|
-
export function registerMarker(sessionId, markerDir) {
|
|
58
|
-
if (!sessionId || !markerDir) return;
|
|
59
|
-
try {
|
|
60
|
-
mkdirSync(sessionDir(sessionId), { recursive: true });
|
|
61
|
-
const p = markerEntryPath(sessionId, markerDir);
|
|
62
|
-
// Atomic tmp+rename (ADR-086/091, matching state.mjs/debounce-state.mjs): a
|
|
63
|
-
// plain writeFileSync truncates the target before the new bytes are durable,
|
|
64
|
-
// so an interrupted write or a concurrent readRegisteredMarkers could see an
|
|
65
|
-
// empty/partial JSON file — which readRegisteredMarkers skips as malformed,
|
|
66
|
-
// silently dropping that repo from the session set (the exact gap this
|
|
67
|
-
// registry closes). rename is atomic, so readers see old-or-new, never torn.
|
|
68
|
-
// Overwrite is idempotent; per-(session,project) files mean concurrent
|
|
69
|
-
// registrations of DIFFERENT repos never clobber each other.
|
|
70
|
-
const tmp = `${p}.tmp.${process.pid}`;
|
|
71
|
-
writeFileSync(tmp, JSON.stringify({ markerDir, at: new Date().toISOString() }));
|
|
72
|
-
renameSync(tmp, p);
|
|
73
|
-
} catch {
|
|
74
|
-
// best-effort (ADR-077) — a registry write failure must never affect review
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Deduped set of markerDirs registered for `sessionId`. Tolerant of missing dir
|
|
79
|
-
// / malformed entries. Returns [] when sessionId is falsy. Runs a probabilistic
|
|
80
|
-
// TTL sweep (~5%) so a crash that skipped SessionEnd can't leak dirs forever.
|
|
81
|
-
export function readRegisteredMarkers(sessionId) {
|
|
82
|
-
if (!sessionId) return [];
|
|
83
|
-
const dir = sessionDir(sessionId);
|
|
84
|
-
const markers = new Set();
|
|
85
|
-
let names;
|
|
86
|
-
try {
|
|
87
|
-
names = readdirSync(dir);
|
|
88
|
-
} catch {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
for (const name of names) {
|
|
92
|
-
if (!name.endsWith(".json")) continue;
|
|
93
|
-
try {
|
|
94
|
-
const { markerDir } = JSON.parse(readFileSync(join(dir, name), "utf8"));
|
|
95
|
-
if (typeof markerDir === "string" && markerDir.length > 0) markers.add(markerDir);
|
|
96
|
-
} catch {
|
|
97
|
-
// skip malformed / vanished entry
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Pass sessionId as exceptSessionId so a read NEVER sweeps its own live session
|
|
101
|
-
// (a session idle >TTL since its last edit would otherwise erase its registry
|
|
102
|
-
// mid-session and silently fall back to cwd-only — reopening the #209 gap).
|
|
103
|
-
if (Math.random() < 0.05) sweepStaleSessions(Date.now(), SESSION_REGISTRY_TTL_MS, sessionId);
|
|
104
|
-
return [...markers];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// The one place both drain hooks agree on "which markers to act on this turn":
|
|
108
|
-
// the cwd-resolved marker (may be null) unioned with the session-registered set.
|
|
109
|
-
export function collectSessionMarkers(cwdMarker, sessionId) {
|
|
110
|
-
const set = new Set();
|
|
111
|
-
if (cwdMarker) set.add(cwdMarker);
|
|
112
|
-
for (const m of readRegisteredMarkers(sessionId)) set.add(m);
|
|
113
|
-
return [...set];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Drop the whole registry entry for a session (SessionEnd). Best-effort.
|
|
117
|
-
export function clearSession(sessionId) {
|
|
118
|
-
if (!sessionId) return;
|
|
119
|
-
try {
|
|
120
|
-
rmSync(sessionDir(sessionId), { recursive: true, force: true });
|
|
121
|
-
} catch {
|
|
122
|
-
// already gone
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Drop session dirs whose NEWEST entry mtime is older than ttlMs — a backstop
|
|
127
|
-
// for sessions whose SessionEnd never fired (crash). Skips `exceptSessionId` so a
|
|
128
|
-
// live session's own read can never sweep it. Best-effort; never throws.
|
|
129
|
-
export function sweepStaleSessions(now, ttlMs, exceptSessionId) {
|
|
130
|
-
const skip = exceptSessionId ? hash16(exceptSessionId) : null;
|
|
131
|
-
let sessions;
|
|
132
|
-
try {
|
|
133
|
-
sessions = readdirSync(sessionRegistryRoot());
|
|
134
|
-
} catch {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const cutoff = now - ttlMs;
|
|
138
|
-
for (const s of sessions) {
|
|
139
|
-
if (s === skip) continue; // never sweep the live session that's reading us
|
|
140
|
-
const sdir = join(sessionRegistryRoot(), s);
|
|
141
|
-
try {
|
|
142
|
-
const names = readdirSync(sdir);
|
|
143
|
-
// An EMPTY dir is either mid-registration (a concurrent registerMarker
|
|
144
|
-
// caught between its mkdirSync and its first writeFileSync) or a transient
|
|
145
|
-
// leftover — never sweep it, or we'd delete the dir out from under that
|
|
146
|
-
// pending write and lose the marker (the write ENOENTs and best-effort-
|
|
147
|
-
// swallows). Non-empty dirs age out by their newest entry mtime.
|
|
148
|
-
if (names.length === 0) continue;
|
|
149
|
-
let newest = 0;
|
|
150
|
-
for (const name of names) {
|
|
151
|
-
try {
|
|
152
|
-
newest = Math.max(newest, statSync(join(sdir, name)).mtimeMs);
|
|
153
|
-
} catch {
|
|
154
|
-
// entry vanished between readdir and stat
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (newest < cutoff) rmSync(sdir, { recursive: true, force: true });
|
|
158
|
-
} catch {
|
|
159
|
-
// skip unreadable / racing session dir
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
package/scripts/lib/state.d.mts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export const AUTOPAUSE_FAILURE_THRESHOLD: number;
|
|
2
|
-
export const INFLIGHT_TTL_MIN_MS: number;
|
|
3
|
-
export const pausePath: (markerDir: string) => string;
|
|
4
|
-
export const stateRoot: (markerDir: string) => string;
|
|
5
|
-
export const logPath: (markerDir: string) => string;
|
|
6
|
-
export function addAck(markerDir: string, hash: string, options: { reason: string }): void;
|
|
7
|
-
export function appendLog(markerDir: string, entry: Record<string, unknown>): Promise<void>;
|
|
8
|
-
export function clearAutoPause(markerDir: string, expected?: PauseInfo): boolean;
|
|
9
|
-
export function clearReviewFailures(markerDir: string): void;
|
|
10
|
-
export function computeCacheKey(options: {
|
|
11
|
-
model: string;
|
|
12
|
-
prompt: string;
|
|
13
|
-
fileContent: string;
|
|
14
|
-
surfaceThreshold: string;
|
|
15
|
-
}): string;
|
|
16
|
-
export function getCachedConcerns(
|
|
17
|
-
markerDir: string,
|
|
18
|
-
cacheKey: string,
|
|
19
|
-
): Promise<{ high: string[]; med: string[]; low: string[]; durationMs?: number } | null>;
|
|
20
|
-
export function hashConcernBody(body: string): string;
|
|
21
|
-
export function isPaused(markerDir: string): boolean;
|
|
22
|
-
export type PauseInfo =
|
|
23
|
-
| { manual: true }
|
|
24
|
-
| {
|
|
25
|
-
kind: "quota" | "failures";
|
|
26
|
-
reason: string;
|
|
27
|
-
at?: string;
|
|
28
|
-
pluginVersion?: string;
|
|
29
|
-
resetHint?: string;
|
|
30
|
-
};
|
|
31
|
-
export function readAcks(markerDir: string): Record<string, unknown>;
|
|
32
|
-
export function readPauseInfo(markerDir: string): PauseInfo | null;
|
|
33
|
-
export function readPluginVersion(): string | null;
|
|
34
|
-
export function recordReviewFailure(markerDir: string, reason: string): number;
|
|
35
|
-
export function resolveAutoResume(
|
|
36
|
-
pauseInfo: PauseInfo | null,
|
|
37
|
-
options?: {
|
|
38
|
-
now?: number;
|
|
39
|
-
currentVersion?: string | null;
|
|
40
|
-
quotaTtlMs?: number;
|
|
41
|
-
failuresTtlMs?: number;
|
|
42
|
-
},
|
|
43
|
-
): { resume: boolean; why?: string };
|
|
44
|
-
export function releaseInflightLock(lockPath?: string): void;
|
|
45
|
-
export function setCachedConcerns(
|
|
46
|
-
markerDir: string,
|
|
47
|
-
cacheKey: string,
|
|
48
|
-
value: { high: string[]; med: string[]; low: string[]; durationMs: number },
|
|
49
|
-
): Promise<void>;
|
|
50
|
-
export function tryAcquireInflightLock(
|
|
51
|
-
markerDir: string,
|
|
52
|
-
filePath: string,
|
|
53
|
-
ttlMs: number,
|
|
54
|
-
): { acquired: boolean; lockPath: string; reason?: string };
|
|
55
|
-
export function writeAutoPause(
|
|
56
|
-
markerDir: string,
|
|
57
|
-
value: { kind: "quota" | "failures"; reason: string; resetHint?: string },
|
|
58
|
-
): boolean;
|