@ask-llm/plugin 0.14.0 → 0.16.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.
Files changed (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.cursor-plugin/plugin.json +25 -0
  3. package/CHANGELOG.md +966 -0
  4. package/README.md +49 -9
  5. package/agents/brainstorm-coordinator.md +70 -25
  6. package/agents/gemini-reviewer.md +1 -1
  7. package/dist/antigravity-run.js +0 -0
  8. package/dist/brainstorm-panel.d.ts +46 -0
  9. package/dist/brainstorm-panel.d.ts.map +1 -0
  10. package/dist/brainstorm-panel.js +247 -0
  11. package/dist/brainstorm-panel.js.map +1 -0
  12. package/dist/brainstorm-run.d.ts +3 -0
  13. package/dist/brainstorm-run.d.ts.map +1 -0
  14. package/dist/brainstorm-run.js +51 -0
  15. package/dist/brainstorm-run.js.map +1 -0
  16. package/dist/codex-run.js +0 -0
  17. package/dist/grok-run.js +0 -0
  18. package/dist/ollama-run.js +0 -0
  19. package/dist/run.js +0 -0
  20. package/mcp.json +11 -0
  21. package/package.json +18 -15
  22. package/pi/extensions/provider-tools.ts +8 -1
  23. package/scripts/benchmark/README.md +114 -0
  24. package/scripts/benchmark/fixtures/README.md +29 -0
  25. package/scripts/codex-pair-debounce-worker.mjs +0 -0
  26. package/scripts/codex-pair-log.mjs +4 -13
  27. package/scripts/codex-pair-prompt-drain.mjs +1 -1
  28. package/scripts/codex-pair-session.mjs +2 -2
  29. package/scripts/codex-pair-stop-gate.mjs +8 -8
  30. package/scripts/codex-pair-watch.mjs +20 -39
  31. package/skills/brainstorm/SKILL.md +62 -39
  32. package/skills/codex-pair/SKILL.md +44 -2
  33. package/skills/gemini-review/SKILL.md +1 -1
  34. package/skills/grok-pair/SKILL.md +115 -0
  35. package/skills/pairing-contract.md +40 -0
  36. package/scripts/lib/broker-lifecycle.mjs +0 -575
  37. package/scripts/lib/broker-rpc.mjs +0 -203
  38. package/scripts/lib/broker-transport.mjs +0 -407
  39. package/scripts/lib/broker.mjs +0 -537
  40. package/scripts/lib/debounce-state.mjs +0 -208
  41. package/scripts/lib/parser.d.mts +0 -12
  42. package/scripts/lib/parser.mjs +0 -229
  43. package/scripts/lib/process.mjs +0 -56
  44. package/scripts/lib/prompt.d.mts +0 -8
  45. package/scripts/lib/prompt.mjs +0 -41
  46. package/scripts/lib/session-registry.mjs +0 -162
  47. package/scripts/lib/state.d.mts +0 -58
  48. package/scripts/lib/state.mjs +0 -733
  49. 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
- }
@@ -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;