github-router 0.3.131 → 0.3.136
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/dist/browser-ext/manifest.json +1 -1
- package/dist/engine-BrbdG9Cp.js +6 -0
- package/dist/{lifecycle-CbtmGbjI.js → lifecycle-C0Y_e0zA.js} +2 -2
- package/dist/{lifecycle-BoId1aMF.js → lifecycle-C8t7-5pU.js} +2 -2
- package/dist/{lifecycle-Cyxwmj1c.js → lifecycle-CeVDX6av.js} +2 -2
- package/dist/{lifecycle-Cyxwmj1c.js.map → lifecycle-CeVDX6av.js.map} +1 -1
- package/dist/{lifecycle-DTJ2Ugqf.js → lifecycle-Cqe8OQVX.js} +2 -2
- package/dist/{lifecycle-DTJ2Ugqf.js.map → lifecycle-Cqe8OQVX.js.map} +1 -1
- package/dist/main.js +350 -14
- package/dist/main.js.map +1 -1
- package/dist/paths-Bljq3UJC.js +3 -0
- package/dist/{paths-CNgpeaWd.js → paths-Cn5OzmYL.js} +34 -4
- package/dist/{paths-CNgpeaWd.js.map → paths-Cn5OzmYL.js.map} +1 -1
- package/dist/{peer-mcp-personas-CH2gmdPN.js → peer-mcp-personas-Dmx4S0_5.js} +285 -76
- package/dist/peer-mcp-personas-Dmx4S0_5.js.map +1 -0
- package/package.json +2 -1
- package/dist/engine-DCsTvsSw.js +0 -6
- package/dist/paths-B-ATynF7.js +0 -3
- package/dist/peer-mcp-personas-CH2gmdPN.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
3
|
-
import { a as removeOwnClaudeConfigMirror, i as isUnderClaudeConfigMirror, l as
|
|
4
|
-
import { c as killManagedTree, d as runCommandCapture, f as runCommandVoid, l as parseBoolEnv, s as killChildProcessTree, u as resolveExecutable } from "./lifecycle-
|
|
5
|
-
import { a as sweepRegistry } from "./lifecycle-
|
|
2
|
+
import { $ as readIteratorWithTimeout, $t as state, At as generateRandomPort, B as toolbeltSkipSet, Bt as filterBetaHeader, C as repoRoot, Ct as toolbeltPathOverride, D as resolveSealedGate, Dt as DEFAULT_PORT, E as trustRepo, Et as DEFAULT_CODEX_MODEL_FALLBACKS, Ft as setupGitHubToken, G as ADVISOR_INTERNAL_TOOL_NAME, Gt as getModels, H as TOOLBELT_TOOLS, Ht as resolveCodexModel, It as tryRefreshAndRetry, J as injectAdvisorTool, Jt as forwardError, K as ADVISOR_TOOL_INSTRUCTIONS, Kt as fetchWithTransientRetry, L as availableToolCommands, Lt as cacheCopilotVersion, Mt as getPackageVersion, Nt as withInstallLock, O as liveExec, Ot as UPSTREAM_FETCH_TIMEOUT_MS, Pt as setupCopilotToken, Q as logStreamError, Qt as githubHeaders, R as buildToolbeltAwareness, Rt as cacheModels, S as repoFingerprint, St as collapsePathKeys, T as stopReviewStateDir, Tt as DEFAULT_CODEX_MODEL, U as assetFor, Ut as resolveModel, V as vscodeRipgrepPath, Vt as isNullish, W as searchWeb, Wt as sleep, X as buildOpenAIErrorEvent, Xt as copilotBaseUrl, Y as isAdvisorRequested, Yt as GITHUB_API_BASE_URL, Z as isControllerClosedError, Zt as copilotHeaders, _ as fileBaselineStore, _t as provisionAndIndexColbert, a as buildPeerAwarenessSnippet, at as standInToolEnabled, b as fileReviewDebounce, bt as shouldUseInsecureTls, c as buildSessionBindHookCommand, ct as createMessages, d as decideStopHook, dt as createChatCompletions, et as relayAnthropicStream, f as fileBlockBudget, ft as MAX_RESPONSE_BODY_BYTES, g as stopReviewEnabled, gt as hasSupportedBrowserInstalled, h as stopGateId, ht as provisionBrowserAssets, i as buildAgentPrompt, it as fleetToolsEnabled, jt as pickClaudeDefault, kt as UPSTREAM_INACTIVITY_TIMEOUT_MS, l as buildStopHookCommand, lt as getTokenCount, m as launchBaselineKey, mt as parseJsonOrDiagnose, n as MCP_GROUPS, nt as handleMcpPost, o as personasFor, ot as workerToolsEnabled, p as injectStopHookIntoSettingsFile, pt as readResponseBodyCapped, q as buildAdvisorStream, qt as HTTPError, r as assertMcpToolSurfaceConsistent, rt as browserToolsEnabled, s as buildArtifactOpenHookCommand, st as countTokens, t as GROUP_META, tt as handleMcpDelete, u as captureLaunchBaseline, ut as createResponses, v as fileFindingsStore, vt as extractTarGzMember, w as stopGateEnabledForRepo, wt as DEFAULT_CLAUDE_MODEL_FALLBACKS, x as isSubagentContext, xt as ArtifactClient, y as fileLastPromptStore, yt as extractZipMember, z as toolbeltEnabled, zt as cacheVSCodeVersion } from "./peer-mcp-personas-Dmx4S0_5.js";
|
|
3
|
+
import { a as removeOwnClaudeConfigMirror, i as isUnderClaudeConfigMirror, l as writeArtifactCredsToMirror, n as ensureClaudeConfigMirror, r as ensurePaths, t as PATHS, u as writeRuntimeFileSecure } from "./paths-Cn5OzmYL.js";
|
|
4
|
+
import { c as killManagedTree, d as runCommandCapture, f as runCommandVoid, l as parseBoolEnv, s as killChildProcessTree, u as resolveExecutable } from "./lifecycle-Cqe8OQVX.js";
|
|
5
|
+
import { a as sweepRegistry } from "./lifecycle-CeVDX6av.js";
|
|
6
6
|
import { defineCommand, runMain } from "citty";
|
|
7
7
|
import consola from "consola";
|
|
8
8
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
9
9
|
import fs, { chmod, copyFile, link, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
10
|
-
import os, { tmpdir } from "node:os";
|
|
10
|
+
import os, { homedir, tmpdir } from "node:os";
|
|
11
11
|
import nodePath from "node:path";
|
|
12
12
|
import process$1 from "node:process";
|
|
13
13
|
import { execFileSync, spawn } from "node:child_process";
|
|
@@ -19,6 +19,7 @@ import { getProxyForUrl } from "proxy-from-env";
|
|
|
19
19
|
import { Hono } from "hono";
|
|
20
20
|
import { cors } from "hono/cors";
|
|
21
21
|
import { clearTimeout as clearTimeout$1, setTimeout as setTimeout$1 } from "node:timers";
|
|
22
|
+
import { Marked } from "marked";
|
|
22
23
|
import clipboard from "clipboardy";
|
|
23
24
|
|
|
24
25
|
//#region src/auth.ts
|
|
@@ -579,7 +580,8 @@ const STRIPPED_PARENT_ENV_KEYS = [
|
|
|
579
580
|
"OPENAI_BASE_URL",
|
|
580
581
|
"CODEX_HOME",
|
|
581
582
|
"AIORDIE_CLAUDE_BIND",
|
|
582
|
-
"AIORDIE_TOKEN"
|
|
583
|
+
"AIORDIE_TOKEN",
|
|
584
|
+
"AIORDIE_INSECURE_TLS"
|
|
583
585
|
];
|
|
584
586
|
/**
|
|
585
587
|
* Strip auth-related keys from a parent-process env object. The result
|
|
@@ -2034,7 +2036,7 @@ async function discoverGateCommands(cwd, opts) {
|
|
|
2034
2036
|
if (files.length === 0) return null;
|
|
2035
2037
|
let result;
|
|
2036
2038
|
try {
|
|
2037
|
-
const { runWorkerAgent } = await import("./engine-
|
|
2039
|
+
const { runWorkerAgent } = await import("./engine-BrbdG9Cp.js");
|
|
2038
2040
|
result = await runWorkerAgent({
|
|
2039
2041
|
mode: "explore",
|
|
2040
2042
|
workspace: root,
|
|
@@ -2590,6 +2592,41 @@ Return a compact brief, not the whole research dump:
|
|
|
2590
2592
|
`
|
|
2591
2593
|
};
|
|
2592
2594
|
|
|
2595
|
+
//#endregion
|
|
2596
|
+
//#region src/lib/injected-skills/artifact-review-skill.ts
|
|
2597
|
+
const ARTIFACT_REVIEW_SKILL = {
|
|
2598
|
+
name: "gh-artifact-review",
|
|
2599
|
+
md: `---
|
|
2600
|
+
name: gh-artifact-review
|
|
2601
|
+
description: Review plans and artifacts in the ai-or-die panel. Default to authoring a self-contained HTML artifact (rich, annotatable) and opening THAT for the human, then poll for feedback, revise, and end the loop. Use when running inside an ai-or-die tab and you have a plan, comparison, diagram, table, diff, or report the user should see before proceeding.
|
|
2602
|
+
user-invocable: true
|
|
2603
|
+
---
|
|
2604
|
+
|
|
2605
|
+
# gh-artifact-review: human review in the ai-or-die panel
|
|
2606
|
+
|
|
2607
|
+
Use this when you finish a plan or produce something a user should review and you are inside an ai-or-die tab (the \`mcp__peers__artifact_*\` tools drive a live panel). The human can click any block or select text to attach a comment that comes back to you.
|
|
2608
|
+
|
|
2609
|
+
## Default: present HTML, not raw markdown
|
|
2610
|
+
|
|
2611
|
+
HTML is the canonical review artifact — it renders richly and is annotatable element-by-element. When the content is anything visual or structured (a comparison, table, diagram, diff, dashboard, or a plan you want to look polished), **author a self-contained \`.html\` file** (inline CSS, no external deps, readable typography) and open THAT.
|
|
2612
|
+
|
|
2613
|
+
- Plan-mode plans: the panel auto-opens them already rendered to HTML — you do not need to convert them by hand.
|
|
2614
|
+
- Do not paste raw markdown into an \`.html\`; write real HTML (headings, lists, tables, \`<pre>\` for code). Opening a raw \`.md\` still renders, but a purpose-built HTML artifact reads better and annotates cleanly.
|
|
2615
|
+
|
|
2616
|
+
## Loop
|
|
2617
|
+
|
|
2618
|
+
1. Open: \`mcp__peers__artifact_open\` with the absolute path of the \`.html\` (or the file). Relay the returned \`viewUrl\` and tell the user to review in the panel — they can click a block or select text to comment.
|
|
2619
|
+
2. Poll: \`mcp__peers__artifact_poll\`. If status is waiting, poll again. Each returned prompt may carry a \`selector\`, quoted \`text\`, and \`sourceLine\` pinpointing what the comment is about — act on that exact spot.
|
|
2620
|
+
3. Apply: make the requested edits, then \`mcp__peers__artifact_reply\` with a concise summary of what changed.
|
|
2621
|
+
4. Repeat 2-3 until the user is satisfied; then \`mcp__peers__artifact_end\`.
|
|
2622
|
+
|
|
2623
|
+
## Honest limits
|
|
2624
|
+
|
|
2625
|
+
- If a tool errors (e.g. \`NOT_IN_AIORDIE_TAB\`, \`UNREACHABLE\`), report the code/message verbatim; do not claim the panel opened.
|
|
2626
|
+
- The panel is a review surface, not an approver: outward/irreversible actions still need explicit user confirmation.
|
|
2627
|
+
`
|
|
2628
|
+
};
|
|
2629
|
+
|
|
2593
2630
|
//#endregion
|
|
2594
2631
|
//#region src/lib/claude-md-injection.ts
|
|
2595
2632
|
/**
|
|
@@ -2609,6 +2646,9 @@ const STYLE_MARKER_OPEN = "<!-- gh-router style directive — auto-injected, reg
|
|
|
2609
2646
|
const STYLE_MARKER_CLOSE = "<!-- /gh-router style directive -->";
|
|
2610
2647
|
const TOOLBELT_MARKER_OPEN = "<!-- gh-router toolbelt awareness — auto-injected, regenerated per launch -->";
|
|
2611
2648
|
const TOOLBELT_MARKER_CLOSE = "<!-- /gh-router toolbelt awareness -->";
|
|
2649
|
+
const ARTIFACT_MARKER_OPEN = "<!-- gh-router artifact-panel directive — auto-injected when in an ai-or-die tab -->";
|
|
2650
|
+
const ARTIFACT_MARKER_CLOSE = "<!-- /gh-router artifact-panel directive -->";
|
|
2651
|
+
const ARTIFACT_PANEL_DIRECTIVE = "## Review plans in the artifact panel\n\nYou are running inside an ai-or-die tab, so the `mcp__peers__artifact_open/poll/reply/end` tools drive a live human-review panel. When you finish a plan or produce something the user should review, open it in the panel: `mcp__peers__artifact_open`, tell the user to review (they can click a block or select text to comment), then `mcp__peers__artifact_poll` for feedback, revise, and `mcp__peers__artifact_end` when done. Prefer HTML: for anything rich or structured, author a self-contained `.html` artifact and open THAT (the panel renders and annotates HTML natively). Plan-mode plans are auto-rendered to HTML for you; raw markdown is only a fallback. Default to this for plans before asking the user to proceed.";
|
|
2612
2652
|
/**
|
|
2613
2653
|
* Writing / communication style directive injected at the TOP of the
|
|
2614
2654
|
* mirrored CLAUDE.md so every spawned agent (main, Agent-tool subagent,
|
|
@@ -2971,6 +3011,21 @@ async function appendToolbeltAwarenessToMirroredClaudeMd(snippet) {
|
|
|
2971
3011
|
label: "toolbelt-awareness"
|
|
2972
3012
|
});
|
|
2973
3013
|
}
|
|
3014
|
+
/**
|
|
3015
|
+
* Prepend the artifact-panel review directive to the TOP of the mirrored
|
|
3016
|
+
* CLAUDE.md so plans/artifacts are reviewed in the ai-or-die panel by default.
|
|
3017
|
+
* Gated by the caller on AIORDIE_SESSION_ID (only useful inside a tab). Separate
|
|
3018
|
+
* marker fence; best-effort like the style/peer blocks.
|
|
3019
|
+
*/
|
|
3020
|
+
async function prependArtifactPanelDirectiveToMirroredClaudeMd(directive = ARTIFACT_PANEL_DIRECTIVE) {
|
|
3021
|
+
await injectMarkerBlock({
|
|
3022
|
+
snippet: directive,
|
|
3023
|
+
markerOpen: ARTIFACT_MARKER_OPEN,
|
|
3024
|
+
markerClose: ARTIFACT_MARKER_CLOSE,
|
|
3025
|
+
position: "top",
|
|
3026
|
+
label: "artifact-panel-directive"
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
2974
3029
|
|
|
2975
3030
|
//#endregion
|
|
2976
3031
|
//#region src/lib/injected-skills/write.ts
|
|
@@ -3513,7 +3568,7 @@ function initProxyFromEnv() {
|
|
|
3513
3568
|
//#endregion
|
|
3514
3569
|
//#region package.json
|
|
3515
3570
|
var name = "github-router";
|
|
3516
|
-
var version$1 = "0.3.
|
|
3571
|
+
var version$1 = "0.3.136";
|
|
3517
3572
|
|
|
3518
3573
|
//#endregion
|
|
3519
3574
|
//#region src/lib/approval.ts
|
|
@@ -5716,6 +5771,20 @@ const claude = defineCommand({
|
|
|
5716
5771
|
} catch (err) {
|
|
5717
5772
|
consola.warn(`Could not register the ai-or-die session-bind hook: ${String(err)}`);
|
|
5718
5773
|
}
|
|
5774
|
+
if ((process$1.env.AIORDIE_SESSION_ID ?? "").trim().length > 0) {
|
|
5775
|
+
await writeInjectedSkill(ARTIFACT_REVIEW_SKILL.name, ARTIFACT_REVIEW_SKILL.md).catch(() => ({ written: false }));
|
|
5776
|
+
try {
|
|
5777
|
+
await prependArtifactPanelDirectiveToMirroredClaudeMd();
|
|
5778
|
+
} catch (err) {
|
|
5779
|
+
consola.warn(`Artifact-panel directive prepend failed: ${String(err)}`);
|
|
5780
|
+
}
|
|
5781
|
+
try {
|
|
5782
|
+
await writeArtifactCredsToMirror(shouldUseInsecureTls(process$1.env.AIORDIE_BASE_URL ?? ""));
|
|
5783
|
+
await injectStopHookIntoSettingsFile(nodePath.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json"), buildArtifactOpenHookCommand(process$1.execPath, process$1.argv[1]), "PostToolUse", void 0, "ExitPlanMode");
|
|
5784
|
+
} catch (err) {
|
|
5785
|
+
consola.warn(`Could not register the artifact auto-open hook: ${String(err)}`);
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5719
5788
|
if (args["trust-gate"] === true) try {
|
|
5720
5789
|
const root = await trustRepo(sessionCwd);
|
|
5721
5790
|
process$1.stderr.write(`Structural gate trusted for this repo (${root}); it will run on launch here from now on.\n`);
|
|
@@ -6115,7 +6184,7 @@ async function postJson(url, payload, opts) {
|
|
|
6115
6184
|
* no such handle. Hooks always receive piped/redirected stdin, so this never
|
|
6116
6185
|
* blocks (guarded against an interactive TTY, and any error -> "").
|
|
6117
6186
|
*/
|
|
6118
|
-
function readStdin$
|
|
6187
|
+
function readStdin$3() {
|
|
6119
6188
|
try {
|
|
6120
6189
|
if (process.stdin.isTTY) return "";
|
|
6121
6190
|
return readFileSync(0, "utf8");
|
|
@@ -6146,7 +6215,7 @@ const internalPromptSubmit = defineCommand({
|
|
|
6146
6215
|
},
|
|
6147
6216
|
async run() {
|
|
6148
6217
|
try {
|
|
6149
|
-
const stdin = readStdin$
|
|
6218
|
+
const stdin = readStdin$3();
|
|
6150
6219
|
const steerEnabled = parseBoolEnv(process.env.GH_ROUTER_DISABLE_PROMPT_STEER) !== true;
|
|
6151
6220
|
const runtime = hookMcpRuntimeFromEnv();
|
|
6152
6221
|
let decision;
|
|
@@ -6206,7 +6275,7 @@ const internalPromptSubmit = defineCommand({
|
|
|
6206
6275
|
* teardown and trips a `uv_async_send` assertion; a synchronous read has no such
|
|
6207
6276
|
* handle. Hooks always receive piped stdin (guarded against a TTY; any error -> "").
|
|
6208
6277
|
*/
|
|
6209
|
-
function readStdin$
|
|
6278
|
+
function readStdin$2() {
|
|
6210
6279
|
try {
|
|
6211
6280
|
if (process.stdin.isTTY) return "";
|
|
6212
6281
|
return readFileSync(0, "utf8");
|
|
@@ -6296,13 +6365,279 @@ const internalSessionBind = defineCommand({
|
|
|
6296
6365
|
try {
|
|
6297
6366
|
const out = typeof args.out === "string" ? args.out.trim() : "";
|
|
6298
6367
|
if (!out) return;
|
|
6299
|
-
const record = decodeSessionBind(readStdin$
|
|
6368
|
+
const record = decodeSessionBind(readStdin$2());
|
|
6300
6369
|
if (record) writeSidecar(out, record);
|
|
6301
6370
|
} catch {}
|
|
6302
6371
|
process.exitCode = 0;
|
|
6303
6372
|
}
|
|
6304
6373
|
});
|
|
6305
6374
|
|
|
6375
|
+
//#endregion
|
|
6376
|
+
//#region src/lib/artifact/plan-html.ts
|
|
6377
|
+
function escapeHtml(s) {
|
|
6378
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6379
|
+
}
|
|
6380
|
+
/** Decode the HTML character references a browser would resolve in an attribute
|
|
6381
|
+
* value, so an entity-obfuscated scheme (`javascript:`, `javascript:`)
|
|
6382
|
+
* cannot slip past the scheme check below. Numeric refs cover any letter; the
|
|
6383
|
+
* small named map covers the scheme-relevant punctuation. */
|
|
6384
|
+
const NAMED_REFS = {
|
|
6385
|
+
colon: ":",
|
|
6386
|
+
tab: " ",
|
|
6387
|
+
newline: "\n",
|
|
6388
|
+
sol: "/",
|
|
6389
|
+
semi: ";",
|
|
6390
|
+
amp: "&",
|
|
6391
|
+
lpar: "(",
|
|
6392
|
+
rpar: ")"
|
|
6393
|
+
};
|
|
6394
|
+
function decodeEntities(s) {
|
|
6395
|
+
return s.replace(/&(#[xX][0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);?/g, (m, body) => {
|
|
6396
|
+
if (body[0] === "#") {
|
|
6397
|
+
const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
|
|
6398
|
+
return Number.isFinite(code) && code > 0 && code <= 1114111 ? String.fromCodePoint(code) : m;
|
|
6399
|
+
}
|
|
6400
|
+
return NAMED_REFS[body.toLowerCase()] ?? m;
|
|
6401
|
+
});
|
|
6402
|
+
}
|
|
6403
|
+
/** Block script-bearing URL schemes (defense-in-depth on top of HTML escaping). */
|
|
6404
|
+
function isDangerousUrl(url, isImage) {
|
|
6405
|
+
const v = Array.from(decodeEntities(url)).filter((c) => c.charCodeAt(0) > 32).join("").toLowerCase();
|
|
6406
|
+
if (v.startsWith("javascript:") || v.startsWith("vbscript:")) return true;
|
|
6407
|
+
if (v.startsWith("data:")) return !(isImage && v.startsWith("data:image/") && !v.startsWith("data:image/svg"));
|
|
6408
|
+
return false;
|
|
6409
|
+
}
|
|
6410
|
+
/** A marked instance that escapes raw HTML (raw markup never becomes live). */
|
|
6411
|
+
function makeMarked() {
|
|
6412
|
+
const m = new Marked({
|
|
6413
|
+
gfm: true,
|
|
6414
|
+
breaks: false
|
|
6415
|
+
});
|
|
6416
|
+
m.use({ renderer: { html(token) {
|
|
6417
|
+
return escapeHtml(typeof token === "string" ? token : token.text ?? "");
|
|
6418
|
+
} } });
|
|
6419
|
+
return m;
|
|
6420
|
+
}
|
|
6421
|
+
/**
|
|
6422
|
+
* Recursively neutralise script-bearing link/image hrefs in the lexed token tree.
|
|
6423
|
+
* Done as an explicit walk (not marked's `walkTokens` hook) because we render via
|
|
6424
|
+
* the low-level `lexer`+`parser` split, which does not run registered walkTokens.
|
|
6425
|
+
*/
|
|
6426
|
+
function neutralizeUrls(tokens) {
|
|
6427
|
+
for (const t of tokens) {
|
|
6428
|
+
if ((t.type === "link" || t.type === "image") && typeof t.href === "string") {
|
|
6429
|
+
if (isDangerousUrl(t.href, t.type === "image")) t.href = "#";
|
|
6430
|
+
}
|
|
6431
|
+
if (t.tokens) neutralizeUrls(t.tokens);
|
|
6432
|
+
if (t.items) neutralizeUrls(t.items);
|
|
6433
|
+
if (t.header) {
|
|
6434
|
+
for (const cell of t.header) if (cell.tokens) neutralizeUrls(cell.tokens);
|
|
6435
|
+
}
|
|
6436
|
+
if (t.rows) {
|
|
6437
|
+
for (const row of t.rows) for (const cell of row) if (cell.tokens) neutralizeUrls(cell.tokens);
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
/** Add `data-source-line="N"` to the first opening tag of a rendered block. */
|
|
6442
|
+
function tagSourceLine(html, line) {
|
|
6443
|
+
return html.replace(/^(\s*)<([a-zA-Z][\w-]*)/, `$1<$2 data-source-line="${line}"`);
|
|
6444
|
+
}
|
|
6445
|
+
/**
|
|
6446
|
+
* Render markdown to inner HTML, tagging each top-level block with its 1-based
|
|
6447
|
+
* source line so an annotation in the panel can map back to a plan line.
|
|
6448
|
+
*/
|
|
6449
|
+
function renderMarkdownBody(source) {
|
|
6450
|
+
const m = makeMarked();
|
|
6451
|
+
let tokens;
|
|
6452
|
+
try {
|
|
6453
|
+
tokens = m.lexer(source);
|
|
6454
|
+
} catch {
|
|
6455
|
+
return `<pre>${escapeHtml(source)}</pre>`;
|
|
6456
|
+
}
|
|
6457
|
+
neutralizeUrls(tokens);
|
|
6458
|
+
let line = 1;
|
|
6459
|
+
let out = "";
|
|
6460
|
+
for (const tok of tokens) {
|
|
6461
|
+
let html = "";
|
|
6462
|
+
try {
|
|
6463
|
+
html = m.parser([tok]);
|
|
6464
|
+
} catch {
|
|
6465
|
+
html = "";
|
|
6466
|
+
}
|
|
6467
|
+
if (html.trim()) out += tagSourceLine(html, line);
|
|
6468
|
+
line += tok.raw?.match(/\n/g)?.length ?? 0;
|
|
6469
|
+
}
|
|
6470
|
+
return out || `<pre>${escapeHtml(source)}</pre>`;
|
|
6471
|
+
}
|
|
6472
|
+
/**
|
|
6473
|
+
* Wrap rendered markdown in a complete, self-contained HTML document with
|
|
6474
|
+
* readable typography (works even before ai-or-die injects its annotation SDK).
|
|
6475
|
+
*/
|
|
6476
|
+
function renderPlanHtml(source, title = "Plan") {
|
|
6477
|
+
const body = renderMarkdownBody(source);
|
|
6478
|
+
return `<!doctype html>
|
|
6479
|
+
<html lang="en">
|
|
6480
|
+
<head>
|
|
6481
|
+
<meta charset="utf-8">
|
|
6482
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6483
|
+
<title>${escapeHtml(title)}</title>
|
|
6484
|
+
<style>
|
|
6485
|
+
:root {
|
|
6486
|
+
--bg: #0f1115; --fg: #f7f3ea; --muted: #aeb6c6; --border: #2a2f3a;
|
|
6487
|
+
--accent: #f4c95d; --code-bg: #171a21;
|
|
6488
|
+
--serif: 'Iowan Old Style', Georgia, 'Times New Roman', serif;
|
|
6489
|
+
--sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
6490
|
+
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
6491
|
+
}
|
|
6492
|
+
* { box-sizing: border-box; }
|
|
6493
|
+
html, body { margin: 0; background: var(--bg); color: var(--fg); }
|
|
6494
|
+
body { font-family: var(--serif); font-size: 16px; line-height: 1.6;
|
|
6495
|
+
padding: 32px clamp(16px, 5vw, 56px); }
|
|
6496
|
+
main { max-width: 78ch; margin: 0 auto; }
|
|
6497
|
+
h1, h2, h3, h4 { font-family: var(--sans); line-height: 1.25; margin: 1.6em 0 0.5em; }
|
|
6498
|
+
h1 { font-size: 1.9em; } h2 { font-size: 1.45em; padding-bottom: 0.2em;
|
|
6499
|
+
border-bottom: 1px solid var(--border); } h3 { font-size: 1.2em; }
|
|
6500
|
+
p, li { color: var(--fg); }
|
|
6501
|
+
a { color: var(--accent); }
|
|
6502
|
+
code { font-family: var(--mono); font-size: 0.9em; background: var(--code-bg);
|
|
6503
|
+
padding: 0.12em 0.35em; border-radius: 5px; }
|
|
6504
|
+
pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 8px;
|
|
6505
|
+
padding: 12px 14px; overflow: auto; }
|
|
6506
|
+
pre code { background: none; padding: 0; }
|
|
6507
|
+
blockquote { margin: 1em 0; padding: 0.2em 1em; border-left: 3px solid var(--accent);
|
|
6508
|
+
color: var(--muted); }
|
|
6509
|
+
table { border-collapse: collapse; width: 100%; margin: 1em 0; font-family: var(--sans);
|
|
6510
|
+
font-size: 0.95em; }
|
|
6511
|
+
th, td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
|
|
6512
|
+
th { background: var(--code-bg); }
|
|
6513
|
+
hr { border: 0; border-top: 1px solid var(--border); margin: 1.5em 0; }
|
|
6514
|
+
ul, ol { padding-left: 1.4em; }
|
|
6515
|
+
</style>
|
|
6516
|
+
</head>
|
|
6517
|
+
<body>
|
|
6518
|
+
<main>
|
|
6519
|
+
${body}
|
|
6520
|
+
</main>
|
|
6521
|
+
</body>
|
|
6522
|
+
</html>
|
|
6523
|
+
`;
|
|
6524
|
+
}
|
|
6525
|
+
|
|
6526
|
+
//#endregion
|
|
6527
|
+
//#region src/internal-artifact-open.ts
|
|
6528
|
+
function readStdin$1() {
|
|
6529
|
+
try {
|
|
6530
|
+
if (process.stdin.isTTY) return "";
|
|
6531
|
+
return readFileSync(0, "utf8");
|
|
6532
|
+
} catch {
|
|
6533
|
+
return "";
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
function credsPath() {
|
|
6537
|
+
const dir = process.env.CLAUDE_CONFIG_DIR || nodePath.join(homedir(), ".claude");
|
|
6538
|
+
return nodePath.join(dir, ".aiordie-artifact.json");
|
|
6539
|
+
}
|
|
6540
|
+
function isSubagent(payload) {
|
|
6541
|
+
try {
|
|
6542
|
+
return isSubagentContext(payload);
|
|
6543
|
+
} catch {
|
|
6544
|
+
return false;
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
/** Pull the per-session plan path + markdown content out of the hook payload. */
|
|
6548
|
+
function parseExitPlanPayload(raw) {
|
|
6549
|
+
try {
|
|
6550
|
+
const ti = JSON.parse(raw).tool_input ?? {};
|
|
6551
|
+
return {
|
|
6552
|
+
planFilePath: typeof ti.planFilePath === "string" && ti.planFilePath.trim() ? ti.planFilePath : void 0,
|
|
6553
|
+
planMarkdown: typeof ti.plan === "string" && ti.plan.trim() ? ti.plan : void 0
|
|
6554
|
+
};
|
|
6555
|
+
} catch {
|
|
6556
|
+
return {};
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
function readCreds() {
|
|
6560
|
+
try {
|
|
6561
|
+
const parsed = JSON.parse(readFileSync(credsPath(), "utf8"));
|
|
6562
|
+
if (!parsed.baseUrl || !parsed.token || !parsed.sessionId) return void 0;
|
|
6563
|
+
return {
|
|
6564
|
+
baseUrl: parsed.baseUrl,
|
|
6565
|
+
token: parsed.token,
|
|
6566
|
+
sessionId: parsed.sessionId,
|
|
6567
|
+
insecureTLS: parsed.insecureTLS === true
|
|
6568
|
+
};
|
|
6569
|
+
} catch {
|
|
6570
|
+
return;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
/**
|
|
6574
|
+
* Resolve the plan markdown + a title, preferring the file at `planFilePath`
|
|
6575
|
+
* (the source of truth, may be newer than the payload's snapshot), falling back
|
|
6576
|
+
* to the inline `tool_input.plan` content.
|
|
6577
|
+
*/
|
|
6578
|
+
function resolvePlanMarkdown(p) {
|
|
6579
|
+
if (p.planFilePath) try {
|
|
6580
|
+
const md = readFileSync(p.planFilePath, "utf8");
|
|
6581
|
+
if (md.trim()) return {
|
|
6582
|
+
markdown: md,
|
|
6583
|
+
title: nodePath.basename(p.planFilePath, nodePath.extname(p.planFilePath)),
|
|
6584
|
+
nearPath: p.planFilePath
|
|
6585
|
+
};
|
|
6586
|
+
} catch {}
|
|
6587
|
+
if (p.planMarkdown) return {
|
|
6588
|
+
markdown: p.planMarkdown,
|
|
6589
|
+
title: "Plan"
|
|
6590
|
+
};
|
|
6591
|
+
}
|
|
6592
|
+
/**
|
|
6593
|
+
* Write the rendered HTML next to the plan (an allowed root) when we have a real
|
|
6594
|
+
* path, else into the plans dir under CLAUDE_CONFIG_DIR. Returns the realpath of
|
|
6595
|
+
* the written `.html`, or undefined on failure.
|
|
6596
|
+
*/
|
|
6597
|
+
function writePlanHtml(html, nearPath) {
|
|
6598
|
+
let target;
|
|
6599
|
+
if (nearPath) target = `${nearPath.replace(/\.(md|markdown)$/i, "")}.aiordie.html`;
|
|
6600
|
+
else {
|
|
6601
|
+
const dir = nodePath.join(process.env.CLAUDE_CONFIG_DIR || nodePath.join(homedir(), ".claude"), "plans");
|
|
6602
|
+
target = nodePath.join(dir, `aiordie-plan-${process.pid}.html`);
|
|
6603
|
+
}
|
|
6604
|
+
try {
|
|
6605
|
+
writeFileSync(target, html, "utf8");
|
|
6606
|
+
try {
|
|
6607
|
+
return realpathSync.native(target);
|
|
6608
|
+
} catch {
|
|
6609
|
+
return target;
|
|
6610
|
+
}
|
|
6611
|
+
} catch {
|
|
6612
|
+
return;
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
const internalArtifactOpen = defineCommand({
|
|
6616
|
+
meta: {
|
|
6617
|
+
name: "internal-artifact-open",
|
|
6618
|
+
description: "Open the finalized plan in the ai-or-die panel"
|
|
6619
|
+
},
|
|
6620
|
+
async run() {
|
|
6621
|
+
try {
|
|
6622
|
+
const raw = readStdin$1();
|
|
6623
|
+
let payload;
|
|
6624
|
+
try {
|
|
6625
|
+
payload = JSON.parse(raw);
|
|
6626
|
+
} catch {
|
|
6627
|
+
payload = void 0;
|
|
6628
|
+
}
|
|
6629
|
+
if (isSubagent(payload)) return;
|
|
6630
|
+
const creds = readCreds();
|
|
6631
|
+
if (!creds) return;
|
|
6632
|
+
const plan = resolvePlanMarkdown(parseExitPlanPayload(raw));
|
|
6633
|
+
if (!plan) return;
|
|
6634
|
+
const htmlPath = writePlanHtml(renderPlanHtml(plan.markdown, plan.title), plan.nearPath);
|
|
6635
|
+
if (!htmlPath) return;
|
|
6636
|
+
await new ArtifactClient(creds).open(htmlPath);
|
|
6637
|
+
} catch {}
|
|
6638
|
+
}
|
|
6639
|
+
});
|
|
6640
|
+
|
|
6306
6641
|
//#endregion
|
|
6307
6642
|
//#region src/internal-stop-hook.ts
|
|
6308
6643
|
/**
|
|
@@ -6861,7 +7196,7 @@ process.on("uncaughtException", (error) => {
|
|
|
6861
7196
|
const version = getPackageVersion();
|
|
6862
7197
|
const argv = process.argv.slice(2);
|
|
6863
7198
|
const isVersionFlag = argv.includes("--version");
|
|
6864
|
-
const isInternalHook = argv[0] === "internal-stop-hook" || argv[0] === "internal-prompt-submit" || argv[0] === "internal-stop-review" || argv[0] === "internal-session-bind";
|
|
7199
|
+
const isInternalHook = argv[0] === "internal-stop-hook" || argv[0] === "internal-prompt-submit" || argv[0] === "internal-stop-review" || argv[0] === "internal-session-bind" || argv[0] === "internal-artifact-open";
|
|
6865
7200
|
if (!isVersionFlag && !isInternalHook) consola.info(`github-router v${version}`);
|
|
6866
7201
|
await runMain(defineCommand({
|
|
6867
7202
|
meta: {
|
|
@@ -6880,7 +7215,8 @@ await runMain(defineCommand({
|
|
|
6880
7215
|
"internal-stop-hook": internalStopHook,
|
|
6881
7216
|
"internal-prompt-submit": internalPromptSubmit,
|
|
6882
7217
|
"internal-stop-review": internalStopReview,
|
|
6883
|
-
"internal-session-bind": internalSessionBind
|
|
7218
|
+
"internal-session-bind": internalSessionBind,
|
|
7219
|
+
"internal-artifact-open": internalArtifactOpen
|
|
6884
7220
|
}
|
|
6885
7221
|
}));
|
|
6886
7222
|
|