@ask-llm/plugin 0.15.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 (37) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.cursor-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +966 -0
  4. package/README.md +2 -0
  5. package/agents/brainstorm-coordinator.md +1 -1
  6. package/agents/gemini-reviewer.md +1 -1
  7. package/dist/antigravity-run.js +0 -0
  8. package/dist/brainstorm-run.js +0 -0
  9. package/dist/codex-run.js +0 -0
  10. package/dist/grok-run.js +0 -0
  11. package/dist/ollama-run.js +0 -0
  12. package/dist/run.js +0 -0
  13. package/package.json +14 -14
  14. package/pi/extensions/provider-tools.ts +1 -1
  15. package/scripts/benchmark/README.md +114 -0
  16. package/scripts/benchmark/fixtures/README.md +29 -0
  17. package/scripts/codex-pair-debounce-worker.mjs +0 -0
  18. package/scripts/codex-pair-log.mjs +4 -13
  19. package/scripts/codex-pair-prompt-drain.mjs +1 -1
  20. package/scripts/codex-pair-session.mjs +2 -2
  21. package/scripts/codex-pair-stop-gate.mjs +8 -8
  22. package/scripts/codex-pair-watch.mjs +20 -39
  23. package/skills/gemini-review/SKILL.md +1 -1
  24. package/scripts/lib/broker-lifecycle.mjs +0 -575
  25. package/scripts/lib/broker-rpc.mjs +0 -203
  26. package/scripts/lib/broker-transport.mjs +0 -407
  27. package/scripts/lib/broker.mjs +0 -537
  28. package/scripts/lib/debounce-state.mjs +0 -208
  29. package/scripts/lib/parser.d.mts +0 -12
  30. package/scripts/lib/parser.mjs +0 -229
  31. package/scripts/lib/process.mjs +0 -56
  32. package/scripts/lib/prompt.d.mts +0 -8
  33. package/scripts/lib/prompt.mjs +0 -41
  34. package/scripts/lib/session-registry.mjs +0 -162
  35. package/scripts/lib/state.d.mts +0 -58
  36. package/scripts/lib/state.mjs +0 -733
  37. package/scripts/lib/stop-gate.mjs +0 -134
@@ -1,134 +0,0 @@
1
- // scripts/lib/stop-gate.mjs
2
- // Pure, I/O-free gate logic for the codex-pair Stop hook (#142, ADR-118).
3
- // No workspace imports — the hook ships without node_modules.
4
-
5
- import { isAbsolute, join, relative } from "node:path";
6
- import { hashConcernBody } from "./state.mjs";
7
-
8
- // Parse `git status --porcelain=v1 -z` into a Set of ABSOLUTE paths that are
9
- // modified or untracked relative to HEAD. repoRoot = `git rev-parse
10
- // --show-toplevel`. NUL mode disables git's C-style path quoting and reverses
11
- // rename fields: the first record is the destination path, followed by a second
12
- // source-path record. Only the destination is dirty on disk.
13
- export function parseGitPorcelain(stdout, repoRoot) {
14
- const dirty = new Set();
15
- const records = stdout.split("\0");
16
- for (let i = 0; i < records.length; i++) {
17
- const record = records[i];
18
- if (record.length < 4) continue;
19
- const status = record.slice(0, 2);
20
- const path = record.slice(3);
21
- dirty.add(isAbsolute(path) ? path : join(repoRoot, path));
22
- // In -z mode, rename/copy source paths are the next bare NUL record.
23
- if (status.includes("R") || status.includes("C")) i++;
24
- }
25
- return dirty;
26
- }
27
-
28
- // Verdicts that don't carry a trustworthy final-state review. `skipped`/`error`
29
- // carry a `file` field, so they can be a file's latest entry → [C] fail-opens on
30
- // them. `retried`/`broker_fallback` are logged WITHOUT a `file` (transient
31
- // per-attempt markers), so selectLatestEntries already excludes them and the
32
- // file's true latest review (concerns/none/cached) is used — they're listed here
33
- // for completeness/future-proofing, not because they're currently reachable.
34
- const INDETERMINATE = new Set(["skipped", "error", "retried", "broker_fallback"]);
35
-
36
- // Reconcile latest-per-file entries against present reality, returning the
37
- // unacked HIGH findings that should block turn-end.
38
- // entries Map<file, latestEntry> (from selectLatestEntries)
39
- // acks { [hash]: {reason, ts} } (from readAcks)
40
- // existsFn (absFile) => boolean (injected fs.existsSync)
41
- // gitDirty Set<absPath> | null (null = no git filter)
42
- // markerDir project root for relPath ack identity
43
- export function collectBlockingHighs({ entries, acks, existsFn, gitDirty, markerDir }) {
44
- const blocking = [];
45
- for (const [file, entry] of entries) {
46
- if (!existsFn(file)) continue; // [A] deleted/renamed
47
- if (INDETERMINATE.has(entry.verdict)) continue; // [C] indeterminate latest → fail-open
48
- if (gitDirty && !gitDirty.has(file)) continue; // [B] clean vs HEAD
49
- const highs = Array.isArray(entry.concerns?.high) ? entry.concerns.high : [];
50
- for (const text of highs) {
51
- const hash = hashConcernBody(`${relative(markerDir, file)}:${text}`); // [E] file-scoped
52
- if (!acks[hash]) blocking.push({ file, text, hash });
53
- }
54
- }
55
- return blocking;
56
- }
57
-
58
- // Build the Stop-hook block reason. Short hashes (first 6) are the ack ids.
59
- export function formatBlockMessage(blocking, markerDir) {
60
- const lines = blocking.map((b) => {
61
- const rel = relative(markerDir, b.file);
62
- return ` [${b.hash}] ${rel}\n ${b.text}`;
63
- });
64
- return (
65
- `🚫 codex-pair: ${blocking.length} unaddressed HIGH finding(s) (blockOn: HIGH). ` +
66
- `Fix them, or defer each with /codex-pair-ack.\n\n` +
67
- `${lines.join("\n")}\n\n` +
68
- `To defer a finding (stale / pre-existing / out-of-scope), run /codex-pair-ack with its [hash] from the list above — e.g.:\n` +
69
- ` /codex-pair-ack ${blocking[0].hash} "<reason>"\n` +
70
- `(If you fixed a finding by editing a DIFFERENT file, make a real edit to the ` +
71
- `flagged file so it gets re-reviewed — an identical re-touch hits the review cache.)`
72
- );
73
- }
74
-
75
- // In-flight review detection (2026-07-02 seamless-pairing design). A turn can
76
- // end while a debounce worker is still settling (record.reviewedGen <
77
- // record.generation) or a codex call is mid-review (fresh inflight lock) —
78
- // the log-based gate would pass and the verdict would land after "done".
79
- // Pure over pre-read inputs; the script does the I/O.
80
- // records parsed debounce-record JSONs (malformed entries tolerated)
81
- // lockMtimes mtimeMs of files under state/inflight/
82
- // now clock
83
- // freshMs lock age beyond which a lock is crash junk, not a live review
84
- // staleMs burst age beyond which an unconsumed record is a crashed
85
- // worker's orphan (sweepStaleDebounce fodder), not a live settle
86
- export function collectInFlight({ records, lockMtimes, now, freshMs, staleMs = 600_000 }) {
87
- const settling = [];
88
- for (const r of records) {
89
- if (!r || typeof r !== "object" || typeof r.file !== "string") continue;
90
- if (!(typeof r.generation === "number" && typeof r.reviewedGen === "number")) continue;
91
- if (r.reviewedGen >= r.generation) continue;
92
- if (Number.isFinite(r.burstStartedAt) && now - r.burstStartedAt > staleMs) continue;
93
- settling.push(r.file);
94
- }
95
- const reviewing = lockMtimes.filter((m) => Number.isFinite(m) && now - m < freshMs).length;
96
- return { settling, reviewing, any: settling.length > 0 || reviewing > 0 };
97
- }
98
-
99
- // Block reason for the in-flight case: tell Claude HOW to wait productively
100
- // instead of just refusing the stop.
101
- const MAX_SETTLING_LISTED = 5;
102
- export function formatInFlightMessage({ settling, reviewing }, markerDir) {
103
- const parts = [];
104
- if (settling.length > 0) {
105
- const listed = settling.slice(0, MAX_SETTLING_LISTED).map((f) => relative(markerDir, f));
106
- const extra = settling.length > MAX_SETTLING_LISTED ? ` (+${settling.length - MAX_SETTLING_LISTED} more)` : "";
107
- parts.push(`${settling.length} edited file(s) awaiting review: ${listed.join(", ")}${extra}`);
108
- }
109
- if (reviewing > 0) parts.push(`${reviewing} review(s) running`);
110
- return (
111
- `⏳ codex-pair: review(s) still in flight — ${parts.join("; ")}. ` +
112
- `Verdicts land in ${join(markerDir, ".codex-pair", "log.jsonl")}. ` +
113
- `Wait for them (e.g. \`sleep 45\`), read the newest entries for the files you edited, ` +
114
- `address any HIGH findings, then end the turn.`
115
- );
116
- }
117
-
118
- // Parse log.jsonl text → Map<file, latestEntry>. Last write per file wins
119
- // (the log is append-only; the final entry is the file's latest review).
120
- export function selectLatestEntries(logText) {
121
- const latest = new Map();
122
- for (const line of logText.split("\n")) {
123
- const t = line.trim();
124
- if (!t) continue;
125
- let entry;
126
- try {
127
- entry = JSON.parse(t);
128
- } catch {
129
- continue;
130
- }
131
- if (entry && typeof entry.file === "string") latest.set(entry.file, entry);
132
- }
133
- return latest;
134
- }