diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index bbd3689..498b122 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -42,7 +42,7 @@ "name": "fable-advisor", "source": "./plugins/fable-advisor", "description": "Get a second opinion from Claude Fable 5 before big decisions.", - "version": "1.3.1", + "version": "1.4.0", "author": { "name": "Fatih C. Akyon" }, @@ -57,7 +57,7 @@ "name": "codex-advisor", "source": "./plugins/codex-advisor", "description": "Get a second opinion from GPT via Codex CLI before big decisions.", - "version": "1.1.0", + "version": "1.1.1", "author": { "name": "Fatih C. Akyon" }, diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 4144116..26b7601 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -20,14 +20,14 @@ "name": "fable-advisor", "source": "./plugins/fable-advisor", "description": "Get a second opinion from Claude Fable 5 before big decisions.", - "version": "1.3.1", + "version": "1.4.0", "license": "Apache-2.0" }, { "name": "codex-advisor", "source": "./plugins/codex-advisor", "description": "Get a second opinion from GPT via Codex CLI before big decisions.", - "version": "1.1.0", + "version": "1.1.1", "license": "Apache-2.0" }, { diff --git a/plugins/codex-advisor/.claude-plugin/plugin.json b/plugins/codex-advisor/.claude-plugin/plugin.json index ddccc12..e2152f4 100644 --- a/plugins/codex-advisor/.claude-plugin/plugin.json +++ b/plugins/codex-advisor/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-advisor", - "version": "1.1.0", + "version": "1.1.1", "description": "Get a second opinion from GPT via Codex CLI before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/codex-advisor/.codex-plugin/plugin.json b/plugins/codex-advisor/.codex-plugin/plugin.json index f54c6a9..75f844d 100644 --- a/plugins/codex-advisor/.codex-plugin/plugin.json +++ b/plugins/codex-advisor/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-advisor", - "version": "1.1.0", + "version": "1.1.1", "description": "Get a second opinion from GPT via Codex CLI before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/codex-advisor/.cursor-plugin/plugin.json b/plugins/codex-advisor/.cursor-plugin/plugin.json index f54c6a9..75f844d 100644 --- a/plugins/codex-advisor/.cursor-plugin/plugin.json +++ b/plugins/codex-advisor/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-advisor", - "version": "1.1.0", + "version": "1.1.1", "description": "Get a second opinion from GPT via Codex CLI before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/codex-advisor/claude-hooks/scripts/stage_transcript.mjs b/plugins/codex-advisor/claude-hooks/scripts/stage_transcript.mjs index c0f3fdd..1461bf6 100644 --- a/plugins/codex-advisor/claude-hooks/scripts/stage_transcript.mjs +++ b/plugins/codex-advisor/claude-hooks/scripts/stage_transcript.mjs @@ -1,8 +1,6 @@ #!/usr/bin/env node -// SubagentStart hook: stages the recent conversation to a file and hands the codex-advisor -// relay the exact command to run. Staging beats inlining here because the reviewer is a -// separate process, so the relay never has to retype the history. -// Node-only, cross-platform, exits 0 on any failure. +// SubagentStart hook: stages the recent conversation and hands the codex-advisor relay the +// command to run, so it never retypes the history. Node-only, exits 0 on any failure. import { mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -54,8 +52,7 @@ const renderBlock = (role, b) => { } }; -// Any failure here leaves lines empty, which still stages the transcript path so the -// reviewer keeps its escape hatch instead of ruling on the caller's prompt alone. +// On failure lines stays empty, but the path is still staged so the escape hatch survives. let transcript; let lines = []; try { @@ -70,7 +67,7 @@ for (; i >= 0 && records.length < 80; i--) { if (!lines[i]) continue; let r; try { - r = JSON.parse(lines[i]); // skip a half-written or malformed line + r = JSON.parse(lines[i]); // the transcript is appended to while this runs } catch { continue; } @@ -89,13 +86,11 @@ for (; i >= 0 && records.length < 80; i--) { } records.reverse(); -// Keep the newest characters so a very chatty session cannot flood the reviewer. let recent = records.join("\n\n---\n\n"); const MAX = 160000; if (recent.length > MAX) recent = "…[older turns truncated for length]\n" + recent.slice(-MAX); -// The record cap is silent otherwise, and a reviewer that cannot tell a whole session from -// its tail never knows there is anything older to go looking for. +// Without this the record cap is silent, and the tail of a session reads as the whole of it. if (i >= 0) recent = `…[this is only the newest ${records.length} turns of a longer session]\n` + recent; diff --git a/plugins/codex-advisor/gemini-extension.json b/plugins/codex-advisor/gemini-extension.json index 714f38f..6d99cd3 100644 --- a/plugins/codex-advisor/gemini-extension.json +++ b/plugins/codex-advisor/gemini-extension.json @@ -1,5 +1,5 @@ { "name": "codex-advisor", - "version": "1.1.0", + "version": "1.1.1", "description": "Get a second opinion from GPT via Codex CLI before big decisions." } diff --git a/plugins/fable-advisor/.claude-plugin/plugin.json b/plugins/fable-advisor/.claude-plugin/plugin.json index 3c08a00..5796208 100644 --- a/plugins/fable-advisor/.claude-plugin/plugin.json +++ b/plugins/fable-advisor/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "fable-advisor", - "version": "1.3.1", + "version": "1.4.0", "description": "Get a second opinion from Claude Fable 5 before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/fable-advisor/.codex-plugin/plugin.json b/plugins/fable-advisor/.codex-plugin/plugin.json index 9559ffd..61de618 100644 --- a/plugins/fable-advisor/.codex-plugin/plugin.json +++ b/plugins/fable-advisor/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "fable-advisor", - "version": "1.3.1", + "version": "1.4.0", "description": "Get a second opinion from Claude Fable 5 before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/fable-advisor/.cursor-plugin/plugin.json b/plugins/fable-advisor/.cursor-plugin/plugin.json index 9559ffd..61de618 100644 --- a/plugins/fable-advisor/.cursor-plugin/plugin.json +++ b/plugins/fable-advisor/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "fable-advisor", - "version": "1.3.1", + "version": "1.4.0", "description": "Get a second opinion from Claude Fable 5 before big decisions.", "author": { "name": "Fatih C. Akyon" diff --git a/plugins/fable-advisor/claude-agents/fable-advisor.md b/plugins/fable-advisor/claude-agents/fable-advisor.md index cdff038..01a8d72 100644 --- a/plugins/fable-advisor/claude-agents/fable-advisor.md +++ b/plugins/fable-advisor/claude-agents/fable-advisor.md @@ -11,7 +11,7 @@ You are a second opinion, the same role as Claude Code's built-in advisor, consu You are usually handed a path to the recent conversation. Read that file before anything else, treat its block as the primary context, and treat the caller's prompt as the specific question it answers. -Open your reply with one marker line: "context: recent-conversation received" once you have read that file, or "context: caller prompt only" when you were given no path. +Open your reply with one marker line: "context: recent-conversation received ", quoting the confirmation token from the file you read, or "context: caller prompt only" when you got no such file. Never invent the token. You have read-only access. Verify the load-bearing claims: when a detail one rests on is shortened or missing, read the file or grep the transcript for that term, symbol, or number instead of guessing. You cannot mutate state, run tests, or reach the network. If what you need was never written down anywhere, say so and name it rather than ruling anyway. A second opinion is worth minutes, not a fresh investigation. diff --git a/plugins/fable-advisor/claude-hooks/scripts/inject_transcript.mjs b/plugins/fable-advisor/claude-hooks/scripts/inject_transcript.mjs index 201326d..113da24 100644 --- a/plugins/fable-advisor/claude-hooks/scripts/inject_transcript.mjs +++ b/plugins/fable-advisor/claude-hooks/scripts/inject_transcript.mjs @@ -1,7 +1,8 @@ #!/usr/bin/env node // SubagentStart hook: feeds the fable-advisor subagent the recent conversation -// (it otherwise sees only the caller's prompt). Node-only, cross-platform, exits 0 on any failure. +// (it otherwise sees only the caller's prompt). Node-only, exits 0 on any failure. +import { randomUUID } from "node:crypto"; import { mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -32,8 +33,7 @@ const renderBlock = (role, b) => { } }; -// Any failure here leaves lines empty, which downgrades the review to the caller's prompt -// with a warning attached rather than letting the reviewer rule on thin context unknowingly. +// On failure lines stays empty, so the reviewer is told it has only the caller's prompt. let transcriptPath; let lines = []; try { @@ -48,7 +48,7 @@ for (; i >= 0 && records.length < 80; i--) { if (!lines[i]) continue; let r; try { - r = JSON.parse(lines[i]); // skip a half-written or malformed line + r = JSON.parse(lines[i]); // the transcript is appended to while this runs } catch { continue; } @@ -67,16 +67,14 @@ const source = transcriptPath ? ` Full session: ${transcriptPath}, one JSON record per line, newest last. Grep it for a term, number, or phrase you are chasing; never read it whole.` : ""; -// Keep the newest characters so a very chatty session cannot flood the reviewer. let recent = records.join("\n\n---\n\n"); const MAX = 160000; if (recent.length > MAX) recent = "…[older turns truncated for length]\n" + recent.slice(-MAX); -// The record cap is silent otherwise, and a reviewer that cannot tell a whole session from -// its tail never knows there is anything older to go looking for. +// Without this the record cap is silent, and the tail of a session reads as the whole of it. if (i >= 0) recent = `…[this is only the newest ${records.length} turns of a longer session]\n` + recent; -// Staged to a file rather than inlined, because the harness spills a large additionalContext -// to disk and previews it: the reviewer then sees less than a pointer would have given it. +// Staged, not inlined: the harness previews a large additionalContext instead of passing it. +// The token exists only in the file, so echoing it proves the reviewer read it. let contextFile; try { if (records.length) { @@ -85,18 +83,20 @@ try { staged, `You are reviewing an in-progress Claude Code session. Below is the recent conversation, most recent last, the same history the built-in advisor tool would see. Weigh the specific question in the caller's prompt against this actual context, not just the caller's summary of it. +Confirmation token: ${randomUUID().slice(0, 8)} + Long turns are shortened and marked \`…[truncated]\`.${source} ${recent} ` ); - contextFile = staged; // only after the write returned, so the path always resolves + contextFile = staged; } } catch {} emit( contextFile - ? `Read ${contextFile} before answering. It holds the recent conversation your question is about, and the caller's prompt is only a summary of it.` + ? `Read ${contextFile} before answering. It holds the recent conversation your question is about and the caller's prompt is only a summary of it.` : `The recent conversation could not be reconstructed, so you see only the caller's prompt. Name the context you need.${source}` ); diff --git a/plugins/fable-advisor/gemini-extension.json b/plugins/fable-advisor/gemini-extension.json index 38cd42c..36e9cea 100644 --- a/plugins/fable-advisor/gemini-extension.json +++ b/plugins/fable-advisor/gemini-extension.json @@ -1,5 +1,5 @@ { "name": "fable-advisor", - "version": "1.3.1", + "version": "1.4.0", "description": "Get a second opinion from Claude Fable 5 before big decisions." }