@ashx-j/lunr 0.2.2 → 0.2.4

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 (63) hide show
  1. package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -0
  2. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  3. package/dist/builtin-extensions/ashxj-thinking.js +19 -7
  4. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  5. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  6. package/dist/builtin-extensions/pi-web-access/index.js +18 -13
  7. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  8. package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts +2 -0
  9. package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts.map +1 -1
  10. package/dist/builtin-extensions/pi-web-access/render-search-chrome.js +12 -0
  11. package/dist/builtin-extensions/pi-web-access/render-search-chrome.js.map +1 -1
  12. package/dist/core/extensions/types.d.ts +4 -0
  13. package/dist/core/extensions/types.d.ts.map +1 -1
  14. package/dist/core/extensions/types.js.map +1 -1
  15. package/dist/core/tools/bash.d.ts.map +1 -1
  16. package/dist/core/tools/bash.js +14 -5
  17. package/dist/core/tools/bash.js.map +1 -1
  18. package/dist/core/tools/edit.d.ts.map +1 -1
  19. package/dist/core/tools/edit.js +13 -7
  20. package/dist/core/tools/edit.js.map +1 -1
  21. package/dist/core/tools/find.d.ts.map +1 -1
  22. package/dist/core/tools/find.js +10 -5
  23. package/dist/core/tools/find.js.map +1 -1
  24. package/dist/core/tools/grep.d.ts.map +1 -1
  25. package/dist/core/tools/grep.js +10 -5
  26. package/dist/core/tools/grep.js.map +1 -1
  27. package/dist/core/tools/ls.d.ts.map +1 -1
  28. package/dist/core/tools/ls.js +10 -3
  29. package/dist/core/tools/ls.js.map +1 -1
  30. package/dist/core/tools/read.d.ts.map +1 -1
  31. package/dist/core/tools/read.js +24 -15
  32. package/dist/core/tools/read.js.map +1 -1
  33. package/dist/core/tools/render-utils.d.ts +21 -0
  34. package/dist/core/tools/render-utils.d.ts.map +1 -1
  35. package/dist/core/tools/render-utils.js +32 -0
  36. package/dist/core/tools/render-utils.js.map +1 -1
  37. package/dist/core/tools/write.d.ts.map +1 -1
  38. package/dist/core/tools/write.js +19 -4
  39. package/dist/core/tools/write.js.map +1 -1
  40. package/dist/gateway/commands.d.ts.map +1 -1
  41. package/dist/gateway/commands.js +1 -0
  42. package/dist/gateway/commands.js.map +1 -1
  43. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  44. package/dist/modes/interactive/components/settings-selector.js +5 -5
  45. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  46. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/thinking-selector.js +5 -5
  48. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  49. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  50. package/dist/modes/interactive/components/tool-execution.js +16 -4
  51. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  52. package/docs/keybindings.md +1 -1
  53. package/docs/quickstart.md +1 -1
  54. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  55. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  56. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  57. package/examples/extensions/gondolin/package-lock.json +2 -2
  58. package/examples/extensions/gondolin/package.json +1 -1
  59. package/examples/extensions/sandbox/package-lock.json +2 -2
  60. package/examples/extensions/sandbox/package.json +1 -1
  61. package/examples/extensions/with-deps/package-lock.json +2 -2
  62. package/examples/extensions/with-deps/package.json +1 -1
  63. package/package.json +4 -4
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * `/thinking [level]` slash command to view or set the reasoning level,
5
5
  * plus `/thinking show|hide|toggle` for thinking-block visibility.
6
+ * `/effort` and `/reasoning` are full-parity aliases.
6
7
  *
7
8
  * Loaded by pi via jiti — no build step, plain TypeScript.
8
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ashxj-thinking.d.ts","sourceRoot":"","sources":["../../src/builtin-extensions/ashxj-thinking.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAUH,qEAAqE;AAErE,KAAK,aAAa,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAErF,iEAAiE;AACjE,UAAU,gBAAgB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wEAAsE;AACtE,UAAU,SAAS;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACjD;AAED,8EAA4E;AAC5E,UAAU,oBAAoB;IAC7B,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,EAAE,EAAE;QACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;QACnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,WAAW,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;KACzH,CAAC;CACF;AAED,qFAAmF;AACnF,UAAU,2BAA4B,SAAQ,oBAAoB;IACjE,gBAAgB,IAAI,aAAa,CAAC;IAClC;;;kFAG4E;IAC5E,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,oEAAkE;AAClE,UAAU,YAAY;IACrB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QACtC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,sBAAsB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QACxG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9E,GAAG,IAAI,CAAC;IACT,gBAAgB,IAAI,aAAa,CAAC;IAClC,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAmLD,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,GAAG,IAAI,CAgH/C","sourcesContent":["// @ts-nocheck\n// Vendored from https://github.com/ashx-j/ashxj-thinking @ 0330a34\n// (\"Remove custom footer — keep only /thinking command\"), single-file ashxj\n// convention. lunR patches are marked `// lunr:`.\n/**\n * batxj-thinking extension\n *\n * `/thinking [level]` slash command to view or set the reasoning level,\n * plus `/thinking show|hide|toggle` for thinking-block visibility.\n *\n * Loaded by pi via jiti — no build step, plain TypeScript.\n *\n * NOTE on type imports: this file deliberately declares structural types\n * rather than importing from `@ashx-j/lunr` (or\n * `@ashx-j/lunr-tui`). pi-coding-agent's `index.d.ts` re-exports\n * from internal `.d.ts` files that contain subpath imports like\n * `@ashx-j/lunr-ai/compat`, which fail to resolve under the default\n * `tsc` settings when a file argument is passed (the spec'd\n * `tsc --noEmit index.ts` script). Declaring the types we use inline\n * keeps the typecheck self-contained.\n */\n\nimport { readFileSync, writeFileSync, existsSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Minimal structural types (see file header for why these are inline)\n// ---------------------------------------------------------------------------\n\n/** Mirrored from `@ashx-j/lunr-agent` `ThinkingLevel`. */\n// lunr: added \"max\" — lunR's ThinkingLevel union includes it.\ntype ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\n/** Mirrored from `@ashx-j/lunr-tui` `AutocompleteItem`. */\ninterface AutocompleteItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\n/** A trimmed view of a `Model` — only the fields the picker reads. */\ninterface ModelLike {\n\tid?: string;\n\treasoning?: boolean;\n\tthinkingLevelMap?: Record<string, string | null>;\n}\n\n/** A trimmed view of `ExtensionContext` — only the methods/props we use. */\ninterface ExtensionContextLike {\n\tmode: \"tui\" | \"rpc\" | \"json\" | \"print\";\n\tmodel: ModelLike | undefined;\n\tui: {\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void;\n\t\tselect(title: string, options: string[], opts?: { signal?: AbortSignal; timeout?: number }): Promise<string | undefined>;\n\t};\n}\n\n/** A trimmed view of `ExtensionCommandContext` — only the methods/props we use. */\ninterface ExtensionCommandContextLike extends ExtensionContextLike {\n\tgetThinkingLevel(): ThinkingLevel;\n\t/** Reload extensions, skills, prompts, and themes. Re-reads settings.json\n\t * from disk (session.reload() → settingsManager.reload()), so persisted\n\t * runtime settings like `hideThinkingBlock` are live-applied. The captured\n\t * `ctx`/`pi` are stale after this resolves — do not use them afterward. */\n\treload(): Promise<void>;\n}\n\n/** A trimmed view of `ExtensionAPI` — only the methods we use. */\ninterface ExtensionAPI {\n\tregisterCommand(name: string, options: {\n\t\tdescription?: string;\n\t\tgetArgumentCompletions?(prefix: string): AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;\n\t\thandler(args: string, ctx: ExtensionCommandContextLike): Promise<void> | void;\n\t}): void;\n\tgetThinkingLevel(): ThinkingLevel;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\n// ---------------------------------------------------------------------------\n// Thinking level config\n// ---------------------------------------------------------------------------\n\n/** All known levels, in display order. */\n// lunr: \"max\" appended (lunR supports it; filtered per-model below when unsupported).\nconst ALL_LEVELS_7: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n/** Default 5 — used when the active model is undefined (we don't know whether `xhigh`/`max` are supported). */\nconst ALL_LEVELS_5: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\n/** Per-level descriptions. Mirrored from `THINKING_DESCRIPTIONS` in settings-selector.ts. */\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\t// lunr: added — mirrors settings-selector.ts.\n\tmax: \"Maximum reasoning\",\n};\n\n/** Returns the levels valid for the given model, in display order. */\nfunction availableLevelsFor(model: ModelLike | undefined): readonly ThinkingLevel[] {\n\tif (!model) return ALL_LEVELS_5;\n\tif (model.reasoning === false) return [\"off\" as const];\n\t// Filter out levels the model marks as unsupported (null entry).\n\treturn ALL_LEVELS_7.filter((lvl) => {\n\t\tconst m = model.thinkingLevelMap;\n\t\tif (m && Object.prototype.hasOwnProperty.call(m, lvl) && (m as Record<string, string | null>)[lvl] === null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n}\n\n/** Builds the inline `<padded level> — <description>` label for a level. */\nfunction formatLevelLabel(level: ThinkingLevel): string {\n\treturn `${level.padEnd(8)} \\u2014 ${THINKING_DESCRIPTIONS[level]}`;\n}\n\n// ---------------------------------------------------------------------------\n// Persisted thinking-block visibility (hideThinkingBlock)\n//\n// pi has no public extension API to toggle thinking-block visibility live.\n// The live in-session toggle is `app.thinking.toggle` (default Ctrl+T) wired to\n// `toggleThinkingBlockVisibility()` in interactive-mode.js, which calls\n// `SettingsManager.setHideThinkingBlock()` + rebuilds the chat. That method is\n// not exposed on ExtensionContext / ExtensionCommandContext / ExtensionAPI.\n//\n// What extensions CAN do is persist the `hideThinkingBlock` boolean to the\n// global agent settings file (`~/.lunr/agent/settings.json`); the\n// `SettingsManager` reads it on startup. And because `ctx.reload()` drives\n// `session.reload()` → `settingsManager.reload()` (re-reads disk) →\n// `restoreChatBeforeSessionStart()` copies the new value into\n// `this.hideThinkingBlock` and calls `rebuildChatFromMessages()` (which\n// reconstructs every AssistantMessageComponent with the new hide flag), the\n// persisted value IS live-applied within the session by `await ctx.reload()`\n// (verified against pi 0.80.3). In non-TUI modes `ctx.reload()` is a no-op,\n// so the setting simply applies on next start.\n//\n// The instant keybinding path (Ctrl+T) still works and is the lowest-latency\n// toggle; the command is the discoverable, persistable, completions-friendly\n// equivalent.\n// ---------------------------------------------------------------------------\n\n/** Returns the path to lunR's global agent settings file.\n * lunr: lunR's agent dir is `~/.lunr/agent` (upstream hardcoded `~/.pi/agent`).\n * Honors the `PI_CODING_AGENT_DIR` env override like core config.ts does;\n * a custom agent dir is not exposed to extensions, so this is the only\n * location we can write to be picked up by\n * `SettingsManager.tryLoadFromStorage(\"global\")`. */\nfunction agentSettingsPath(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\t// Match core normalizePath(): expand a leading ~ (~/ and, on win32, ~\\).\n\t\tconst home = homedir();\n\t\tconst expanded =\n\t\t\tenvDir === \"~\"\n\t\t\t\t? home\n\t\t\t\t: envDir.startsWith(\"~/\") || (process.platform === \"win32\" && envDir.startsWith(\"~\\\\\"))\n\t\t\t\t\t? join(home, envDir.slice(2))\n\t\t\t\t\t: envDir;\n\t\treturn join(expanded, \"settings.json\");\n\t}\n\treturn join(homedir(), \".lunr\", \"agent\", \"settings.json\");\n}\n\n// ---------------------------------------------------------------------------\n// lunR customize bridge for persisted thinking-block visibility.\n// When the bridge is present (TUI / gateway sessions), reads/writes go through\n// SettingsManager (lockfile + merge). Falls back to direct file I/O when the\n// extension loads in a context without the bridge (e.g. upstream pi).\n// ---------------------------------------------------------------------------\ninterface CustomizeBridge {\n\tgetHideThinkingBlock(): boolean;\n\tsetHideThinkingBlock(hide: boolean): void;\n}\n\nfunction getCustomizeBridge(): CustomizeBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/customize\")] as CustomizeBridge | undefined;\n}\n\n/** Returns the persisted `hideThinkingBlock` value (default false, matching\n * `getHideThinkingBlock()` which returns `this.settings.hideThinkingBlock ?? false`).\n * Never throws — on any error (missing file, bad JSON) returns false. */\nfunction readHideThinkingBlock(): boolean {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) return bridge.getHideThinkingBlock();\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tif (!existsSync(p)) return false;\n\t\tconst cfg = JSON.parse(readFileSync(p, \"utf8\")) as { hideThinkingBlock?: boolean };\n\t\treturn cfg.hideThinkingBlock === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Persist `hideThinkingBlock` to the global agent settings file (safe merge).\n *\n * - Preserves all other keys (merge, never overwrite the whole file).\n * - Matches pi's 2-space indentation + trailing newline.\n * - On invalid JSON in the existing file, backs it up to `settings.json.bak`\n * before writing a fresh file, so a corrupted file is never written back.\n * - Never throws; on any error `ok` is false and the caller notifies the user.\n *\n * `createdFresh` is true when the file didn't exist (or was corrupt and backed\n * up) before this write — the handler uses it to warn that a global settings\n * file was created here.\n */\nfunction writeHideThinkingBlock(hide: boolean): {\n\tok: boolean;\n\tcreatedFresh: boolean;\n} {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) {\n\t\ttry {\n\t\t\tbridge.setHideThinkingBlock(hide);\n\t\t\treturn { ok: true, createdFresh: false };\n\t\t} catch {\n\t\t\treturn { ok: false, createdFresh: false };\n\t\t}\n\t}\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tlet cfg: Record<string, unknown> = {};\n\t\tlet createdFresh = false;\n\t\tif (existsSync(p)) {\n\t\t\ttry {\n\t\t\t\tcfg = JSON.parse(readFileSync(p, \"utf8\")) as Record<string, unknown>;\n\t\t\t} catch {\n\t\t\t\t// Existing file is corrupt — back it up, then start fresh.\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(p, `${p}.bak`);\n\t\t\t\t} catch {\n\t\t\t\t\t// If backup fails, still proceed to overwrite (best effort).\n\t\t\t\t}\n\t\t\t\tcfg = {};\n\t\t\t\tcreatedFresh = true;\n\t\t\t}\n\t\t} else {\n\t\t\tcreatedFresh = true;\n\t\t}\n\t\tcfg.hideThinkingBlock = hide;\n\t\twriteFileSync(p, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf8\");\n\t\treturn { ok: true, createdFresh };\n\t} catch {\n\t\treturn { ok: false, createdFresh: false };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Extension entry point\n// ---------------------------------------------------------------------------\n\nexport default function (pi: ExtensionAPI): void {\n\tpi.registerCommand(\"thinking\", {\n\t\tdescription: \"View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle\",\n\t\tgetArgumentCompletions(prefix: string): AutocompleteItem[] {\n\t\t\tconst lower = prefix.toLowerCase();\n\t\t\t// Visibility sub-options first, filtered by prefix, alongside the levels.\n\t\t\tconst visibility: AutocompleteItem[] = [\n\t\t\t\t{ value: \"show\", label: \"show\", description: \"Show thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"hide\", label: \"hide\", description: \"Hide thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"toggle\", label: \"toggle\", description: \"Toggle thinking blocks (persisted to settings.json)\" },\n\t\t\t];\n\t\t\tconst levels = availableLevelsFor(currentModel(pi));\n\t\t\tconst levelItems = levels\n\t\t\t\t.filter((lvl) => lvl.startsWith(lower))\n\t\t\t\t.map((lvl) => ({ value: lvl, label: lvl, description: THINKING_DESCRIPTIONS[lvl] }));\n\t\t\treturn [\n\t\t\t\t...visibility.filter((v) => v.value.startsWith(lower)),\n\t\t\t\t...levelItems,\n\t\t\t];\n\t\t},\n\t\thandler: async (args: string, ctx: ExtensionCommandContextLike) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst lower = trimmed.toLowerCase();\n\n\t\t\t// ---- Visibility sub-options: /thinking show | hide | toggle ----\n\t\t\t// Caught first so `hide`/`show`/`toggle` are never parsed as levels.\n\t\t\t// Works in every mode: only uses ctx.ui.notify + file I/O + ctx.reload().\n\t\t\tif (lower === \"show\" || lower === \"hide\" || lower === \"toggle\") {\n\t\t\t\tconst current = readHideThinkingBlock();\n\t\t\t\tconst next = lower === \"toggle\" ? !current : lower === \"hide\";\n\t\t\t\tif (next === current) {\n\t\t\t\t\tctx.ui.notify(`Thinking blocks already ${current ? \"hidden\" : \"shown\"}.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = writeHideThinkingBlock(next);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\t// lunr: settings path is lunR's (see agentSettingsPath).\n\t\t\t\t\tctx.ui.notify(`Failed to update ${agentSettingsPath()}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// The notify MUST run before `await ctx.reload()`: after reload the\n\t\t\t\t// loader invalidates this `ctx`/`pi` as stale. ctx.reload() live-\n\t\t\t\t// applies the setting (verified against pi 0.80.3: it drives\n\t\t\t\t// session.reload() → settingsManager.reload() re-reads disk →\n\t\t\t\t// restoreChatBeforeSessionStart copies the new value into\n\t\t\t\t// this.hideThinkingBlock → rebuildChatFromMessages() rebuilds every\n\t\t\t\t// AssistantMessageComponent with the new hide flag). In non-TUI\n\t\t\t\t// modes ctx.reload() is a no-op; the setting then applies on next\n\t\t\t\t// start. The instant keybinding path (Ctrl+T) also still works.\n\t\t\t\tconst verb = next ? \"hidden\" : \"shown\";\n\t\t\t\tconst note = result.createdFresh\n\t\t\t\t\t? `Thinking blocks ${verb}. (Created ${agentSettingsPath()} with this setting.)`\n\t\t\t\t\t: `Thinking blocks ${verb}.`;\n\t\t\t\tctx.ui.notify(note, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst levels = availableLevelsFor(ctx.model);\n\t\t\tconst currentLevel: ThinkingLevel = pi.getThinkingLevel();\n\t\t\tconst validSet = new Set<ThinkingLevel>(levels);\n\n\t\t\t// No-arg form → open a picker. Skip in non-TUI modes — same pattern\n\t\t\t// as batxj-animations per AGENTS.md §5.\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(`Thinking level: ${currentLevel}`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (levels.length === 0) {\n\t\t\t\t\tctx.ui.notify(\"No thinking levels available for this model.\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst options = levels.map((lvl) =>\n\t\t\t\t\t(lvl === currentLevel ? \"\\u25CF \" : \" \") + formatLevelLabel(lvl),\n\t\t\t\t);\n\t\t\t\tconst chosen = await ctx.ui.select(\n\t\t\t\t\t`Thinking level (current: ${currentLevel})`,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\tif (chosen === undefined) return;\n\t\t\t\t// Chosen string starts with the current-marker (\"● \") or two-space\n\t\t\t\t// gutter; strip the prefix and extract the first token (the level).\n\t\t\t\tconst stripped = chosen.replace(/^[\\u25CF ]\\s?/, \"\").trimStart();\n\t\t\t\tconst level = stripped.split(/\\s+/)[0] as ThinkingLevel;\n\t\t\t\tif (!validSet.has(level)) {\n\t\t\t\t\tctx.ui.notify(`Unsupported thinking level: ${level}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (level === currentLevel) return; // no-op\n\t\t\t\tpi.setThinkingLevel(level);\n\t\t\t\tctx.ui.notify(`Thinking level: ${level}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Direct-set form: `/thinking <level>`\n\t\t\tconst requested = trimmed.toLowerCase() as ThinkingLevel;\n\t\t\tif (!validSet.has(requested)) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Invalid thinking level: \"${trimmed}\". Valid: ${levels.join(\", \")}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (requested === currentLevel) {\n\t\t\t\tctx.ui.notify(`Thinking level: ${requested} (unchanged)`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpi.setThinkingLevel(requested);\n\t\t\tctx.ui.notify(`Thinking level: ${requested}`, \"info\");\n\t\t},\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read the current model from the ExtensionAPI runtime. The spec mentions\n * `pi.getModel()`; pi doesn't expose that name on the API object itself, but\n * the bound `ExtensionContextActions` (which the runtime decorates onto\n * events) does include `getModel()`. We duck-type against the loader so this\n * file stays decoupled from non-public internals. When unavailable (e.g.\n * during autocomplete, before any session has bound), returns undefined and\n * the caller falls back to the default 5-level set.\n */\nfunction currentModel(pi: ExtensionAPI): ModelLike | undefined {\n\tconst fn = (pi as unknown as { getModel?: () => unknown }).getModel;\n\tif (typeof fn === \"function\") {\n\t\treturn fn.call(pi) as ModelLike | undefined;\n\t}\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"ashxj-thinking.d.ts","sourceRoot":"","sources":["../../src/builtin-extensions/ashxj-thinking.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAUH,qEAAqE;AAErE,KAAK,aAAa,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAErF,iEAAiE;AACjE,UAAU,gBAAgB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wEAAsE;AACtE,UAAU,SAAS;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACjD;AAED,8EAA4E;AAC5E,UAAU,oBAAoB;IAC7B,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,EAAE,EAAE;QACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;QACnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,WAAW,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;KACzH,CAAC;CACF;AAED,qFAAmF;AACnF,UAAU,2BAA4B,SAAQ,oBAAoB;IACjE,gBAAgB,IAAI,aAAa,CAAC;IAClC;;;kFAG4E;IAC5E,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,oEAAkE;AAClE,UAAU,YAAY;IACrB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QACtC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,sBAAsB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QACxG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9E,GAAG,IAAI,CAAC;IACT,gBAAgB,IAAI,aAAa,CAAC;IAClC,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7C;AAsLD,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,GAAG,IAAI,CA2H/C","sourcesContent":["// @ts-nocheck\n// Vendored from https://github.com/ashx-j/ashxj-thinking @ 0330a34\n// (\"Remove custom footer — keep only /thinking command\"), single-file ashxj\n// convention. lunR patches are marked `// lunr:`.\n/**\n * batxj-thinking extension\n *\n * `/thinking [level]` slash command to view or set the reasoning level,\n * plus `/thinking show|hide|toggle` for thinking-block visibility.\n * `/effort` and `/reasoning` are full-parity aliases.\n *\n * Loaded by pi via jiti — no build step, plain TypeScript.\n *\n * NOTE on type imports: this file deliberately declares structural types\n * rather than importing from `@ashx-j/lunr` (or\n * `@ashx-j/lunr-tui`). pi-coding-agent's `index.d.ts` re-exports\n * from internal `.d.ts` files that contain subpath imports like\n * `@ashx-j/lunr-ai/compat`, which fail to resolve under the default\n * `tsc` settings when a file argument is passed (the spec'd\n * `tsc --noEmit index.ts` script). Declaring the types we use inline\n * keeps the typecheck self-contained.\n */\n\nimport { readFileSync, writeFileSync, existsSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Minimal structural types (see file header for why these are inline)\n// ---------------------------------------------------------------------------\n\n/** Mirrored from `@ashx-j/lunr-agent` `ThinkingLevel`. */\n// lunr: added \"max\" — lunR's ThinkingLevel union includes it.\ntype ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\n/** Mirrored from `@ashx-j/lunr-tui` `AutocompleteItem`. */\ninterface AutocompleteItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\n/** A trimmed view of a `Model` — only the fields the picker reads. */\ninterface ModelLike {\n\tid?: string;\n\treasoning?: boolean;\n\tthinkingLevelMap?: Record<string, string | null>;\n}\n\n/** A trimmed view of `ExtensionContext` — only the methods/props we use. */\ninterface ExtensionContextLike {\n\tmode: \"tui\" | \"rpc\" | \"json\" | \"print\";\n\tmodel: ModelLike | undefined;\n\tui: {\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void;\n\t\tselect(title: string, options: string[], opts?: { signal?: AbortSignal; timeout?: number }): Promise<string | undefined>;\n\t};\n}\n\n/** A trimmed view of `ExtensionCommandContext` — only the methods/props we use. */\ninterface ExtensionCommandContextLike extends ExtensionContextLike {\n\tgetThinkingLevel(): ThinkingLevel;\n\t/** Reload extensions, skills, prompts, and themes. Re-reads settings.json\n\t * from disk (session.reload() → settingsManager.reload()), so persisted\n\t * runtime settings like `hideThinkingBlock` are live-applied. The captured\n\t * `ctx`/`pi` are stale after this resolves — do not use them afterward. */\n\treload(): Promise<void>;\n}\n\n/** A trimmed view of `ExtensionAPI` — only the methods we use. */\ninterface ExtensionAPI {\n\tregisterCommand(name: string, options: {\n\t\tdescription?: string;\n\t\tgetArgumentCompletions?(prefix: string): AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;\n\t\thandler(args: string, ctx: ExtensionCommandContextLike): Promise<void> | void;\n\t}): void;\n\tgetThinkingLevel(): ThinkingLevel;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\n// ---------------------------------------------------------------------------\n// Thinking level config\n// ---------------------------------------------------------------------------\n\n/** All known levels, in display order. */\n// lunr: \"max\" appended (lunR supports it; filtered per-model below when unsupported).\nconst ALL_LEVELS_7: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n/** Default 5 — used when the active model is undefined (we don't know whether `xhigh`/`max` are supported). */\nconst ALL_LEVELS_5: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\n/** Per-level descriptions. Mirrored from `THINKING_DESCRIPTIONS` in settings-selector.ts. */\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning\",\n\tlow: \"Light reasoning\",\n\tmedium: \"Moderate reasoning\",\n\thigh: \"Deep reasoning\",\n\txhigh: \"Extra-high reasoning\",\n\t// lunr: added — mirrors settings-selector.ts.\n\tmax: \"Maximum reasoning\",\n};\n\n/** Returns the levels valid for the given model, in display order. */\nfunction availableLevelsFor(model: ModelLike | undefined): readonly ThinkingLevel[] {\n\tif (!model) return ALL_LEVELS_5;\n\tif (model.reasoning === false) return [\"off\" as const];\n\t// Filter out levels the model marks as unsupported (null entry).\n\treturn ALL_LEVELS_7.filter((lvl) => {\n\t\tconst m = model.thinkingLevelMap;\n\t\tif (m && Object.prototype.hasOwnProperty.call(m, lvl) && (m as Record<string, string | null>)[lvl] === null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n}\n\n/** Builds the inline `<padded level> — <description>` label for a level. */\nfunction formatLevelLabel(level: ThinkingLevel): string {\n\treturn `${level.padEnd(8)} \\u2014 ${THINKING_DESCRIPTIONS[level]}`;\n}\n\n// ---------------------------------------------------------------------------\n// Persisted thinking-block visibility (hideThinkingBlock)\n//\n// pi has no public extension API to toggle thinking-block visibility live.\n// The live in-session toggle is `app.thinking.toggle` (default Ctrl+T) wired to\n// `toggleThinkingBlockVisibility()` in interactive-mode.js, which calls\n// `SettingsManager.setHideThinkingBlock()` + rebuilds the chat. That method is\n// not exposed on ExtensionContext / ExtensionCommandContext / ExtensionAPI.\n//\n// What extensions CAN do is persist the `hideThinkingBlock` boolean to the\n// global agent settings file (`~/.lunr/agent/settings.json`); the\n// `SettingsManager` reads it on startup. And because `ctx.reload()` drives\n// `session.reload()` → `settingsManager.reload()` (re-reads disk) →\n// `restoreChatBeforeSessionStart()` copies the new value into\n// `this.hideThinkingBlock` and calls `rebuildChatFromMessages()` (which\n// reconstructs every AssistantMessageComponent with the new hide flag), the\n// persisted value IS live-applied within the session by `await ctx.reload()`\n// (verified against pi 0.80.3). In non-TUI modes `ctx.reload()` is a no-op,\n// so the setting simply applies on next start.\n//\n// The instant keybinding path (Ctrl+T) still works and is the lowest-latency\n// toggle; the command is the discoverable, persistable, completions-friendly\n// equivalent.\n// ---------------------------------------------------------------------------\n\n/** Returns the path to lunR's global agent settings file.\n * lunr: lunR's agent dir is `~/.lunr/agent` (upstream hardcoded `~/.pi/agent`).\n * Honors the `PI_CODING_AGENT_DIR` env override like core config.ts does;\n * a custom agent dir is not exposed to extensions, so this is the only\n * location we can write to be picked up by\n * `SettingsManager.tryLoadFromStorage(\"global\")`. */\nfunction agentSettingsPath(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\t// Match core normalizePath(): expand a leading ~ (~/ and, on win32, ~\\).\n\t\tconst home = homedir();\n\t\tconst expanded =\n\t\t\tenvDir === \"~\"\n\t\t\t\t? home\n\t\t\t\t: envDir.startsWith(\"~/\") || (process.platform === \"win32\" && envDir.startsWith(\"~\\\\\"))\n\t\t\t\t\t? join(home, envDir.slice(2))\n\t\t\t\t\t: envDir;\n\t\treturn join(expanded, \"settings.json\");\n\t}\n\treturn join(homedir(), \".lunr\", \"agent\", \"settings.json\");\n}\n\n// ---------------------------------------------------------------------------\n// lunR customize bridge for persisted thinking-block visibility.\n// When the bridge is present (TUI / gateway sessions), reads/writes go through\n// SettingsManager (lockfile + merge). Falls back to direct file I/O when the\n// extension loads in a context without the bridge (e.g. upstream pi).\n// ---------------------------------------------------------------------------\ninterface CustomizeBridge {\n\tgetHideThinkingBlock(): boolean;\n\tsetHideThinkingBlock(hide: boolean): void;\n}\n\nfunction getCustomizeBridge(): CustomizeBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/customize\")] as CustomizeBridge | undefined;\n}\n\n/** Returns the persisted `hideThinkingBlock` value (default false, matching\n * `getHideThinkingBlock()` which returns `this.settings.hideThinkingBlock ?? false`).\n * Never throws — on any error (missing file, bad JSON) returns false. */\nfunction readHideThinkingBlock(): boolean {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) return bridge.getHideThinkingBlock();\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tif (!existsSync(p)) return false;\n\t\tconst cfg = JSON.parse(readFileSync(p, \"utf8\")) as { hideThinkingBlock?: boolean };\n\t\treturn cfg.hideThinkingBlock === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Persist `hideThinkingBlock` to the global agent settings file (safe merge).\n *\n * - Preserves all other keys (merge, never overwrite the whole file).\n * - Matches pi's 2-space indentation + trailing newline.\n * - On invalid JSON in the existing file, backs it up to `settings.json.bak`\n * before writing a fresh file, so a corrupted file is never written back.\n * - Never throws; on any error `ok` is false and the caller notifies the user.\n *\n * `createdFresh` is true when the file didn't exist (or was corrupt and backed\n * up) before this write — the handler uses it to warn that a global settings\n * file was created here.\n */\nfunction writeHideThinkingBlock(hide: boolean): {\n\tok: boolean;\n\tcreatedFresh: boolean;\n} {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) {\n\t\ttry {\n\t\t\tbridge.setHideThinkingBlock(hide);\n\t\t\treturn { ok: true, createdFresh: false };\n\t\t} catch {\n\t\t\treturn { ok: false, createdFresh: false };\n\t\t}\n\t}\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tlet cfg: Record<string, unknown> = {};\n\t\tlet createdFresh = false;\n\t\tif (existsSync(p)) {\n\t\t\ttry {\n\t\t\t\tcfg = JSON.parse(readFileSync(p, \"utf8\")) as Record<string, unknown>;\n\t\t\t} catch {\n\t\t\t\t// Existing file is corrupt — back it up, then start fresh.\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(p, `${p}.bak`);\n\t\t\t\t} catch {\n\t\t\t\t\t// If backup fails, still proceed to overwrite (best effort).\n\t\t\t\t}\n\t\t\t\tcfg = {};\n\t\t\t\tcreatedFresh = true;\n\t\t\t}\n\t\t} else {\n\t\t\tcreatedFresh = true;\n\t\t}\n\t\tcfg.hideThinkingBlock = hide;\n\t\twriteFileSync(p, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf8\");\n\t\treturn { ok: true, createdFresh };\n\t} catch {\n\t\treturn { ok: false, createdFresh: false };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Extension entry point\n// ---------------------------------------------------------------------------\n\nconst THINKING_COMMAND_DESCRIPTION =\n\t\"View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle\";\n\nexport default function (pi: ExtensionAPI): void {\n\tconst thinkingSpec = {\n\t\tdescription: THINKING_COMMAND_DESCRIPTION,\n\t\tgetArgumentCompletions(prefix: string): AutocompleteItem[] {\n\t\t\tconst lower = prefix.toLowerCase();\n\t\t\t// Visibility sub-options first, filtered by prefix, alongside the levels.\n\t\t\tconst visibility: AutocompleteItem[] = [\n\t\t\t\t{ value: \"show\", label: \"show\", description: \"Show thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"hide\", label: \"hide\", description: \"Hide thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"toggle\", label: \"toggle\", description: \"Toggle thinking blocks (persisted to settings.json)\" },\n\t\t\t];\n\t\t\tconst levels = availableLevelsFor(currentModel(pi));\n\t\t\tconst levelItems = levels\n\t\t\t\t.filter((lvl) => lvl.startsWith(lower))\n\t\t\t\t.map((lvl) => ({ value: lvl, label: lvl, description: THINKING_DESCRIPTIONS[lvl] }));\n\t\t\treturn [\n\t\t\t\t...visibility.filter((v) => v.value.startsWith(lower)),\n\t\t\t\t...levelItems,\n\t\t\t];\n\t\t},\n\t\thandler: async (args: string, ctx: ExtensionCommandContextLike) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst lower = trimmed.toLowerCase();\n\n\t\t\t// ---- Visibility sub-options: /thinking show | hide | toggle ----\n\t\t\t// Caught first so `hide`/`show`/`toggle` are never parsed as levels.\n\t\t\t// Works in every mode: only uses ctx.ui.notify + file I/O + ctx.reload().\n\t\t\tif (lower === \"show\" || lower === \"hide\" || lower === \"toggle\") {\n\t\t\t\tconst current = readHideThinkingBlock();\n\t\t\t\tconst next = lower === \"toggle\" ? !current : lower === \"hide\";\n\t\t\t\tif (next === current) {\n\t\t\t\t\tctx.ui.notify(`Thinking blocks already ${current ? \"hidden\" : \"shown\"}.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = writeHideThinkingBlock(next);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\t// lunr: settings path is lunR's (see agentSettingsPath).\n\t\t\t\t\tctx.ui.notify(`Failed to update ${agentSettingsPath()}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// The notify MUST run before `await ctx.reload()`: after reload the\n\t\t\t\t// loader invalidates this `ctx`/`pi` as stale. ctx.reload() live-\n\t\t\t\t// applies the setting (verified against pi 0.80.3: it drives\n\t\t\t\t// session.reload() → settingsManager.reload() re-reads disk →\n\t\t\t\t// restoreChatBeforeSessionStart copies the new value into\n\t\t\t\t// this.hideThinkingBlock → rebuildChatFromMessages() rebuilds every\n\t\t\t\t// AssistantMessageComponent with the new hide flag). In non-TUI\n\t\t\t\t// modes ctx.reload() is a no-op; the setting then applies on next\n\t\t\t\t// start. The instant keybinding path (Ctrl+T) also still works.\n\t\t\t\tconst verb = next ? \"hidden\" : \"shown\";\n\t\t\t\tconst note = result.createdFresh\n\t\t\t\t\t? `Thinking blocks ${verb}. (Created ${agentSettingsPath()} with this setting.)`\n\t\t\t\t\t: `Thinking blocks ${verb}.`;\n\t\t\t\tctx.ui.notify(note, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst levels = availableLevelsFor(ctx.model);\n\t\t\tconst currentLevel: ThinkingLevel = pi.getThinkingLevel();\n\t\t\tconst validSet = new Set<ThinkingLevel>(levels);\n\n\t\t\t// No-arg form → open a picker. Skip in non-TUI modes — same pattern\n\t\t\t// as batxj-animations per AGENTS.md §5.\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(`Thinking level: ${currentLevel}`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (levels.length === 0) {\n\t\t\t\t\tctx.ui.notify(\"No thinking levels available for this model.\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst options = levels.map((lvl) =>\n\t\t\t\t\t(lvl === currentLevel ? \"\\u25CF \" : \" \") + formatLevelLabel(lvl),\n\t\t\t\t);\n\t\t\t\tconst chosen = await ctx.ui.select(\n\t\t\t\t\t`Thinking level (current: ${currentLevel})`,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\tif (chosen === undefined) return;\n\t\t\t\t// Chosen string starts with the current-marker (\"● \") or two-space\n\t\t\t\t// gutter; strip the prefix and extract the first token (the level).\n\t\t\t\tconst stripped = chosen.replace(/^[\\u25CF ]\\s?/, \"\").trimStart();\n\t\t\t\tconst level = stripped.split(/\\s+/)[0] as ThinkingLevel;\n\t\t\t\tif (!validSet.has(level)) {\n\t\t\t\t\tctx.ui.notify(`Unsupported thinking level: ${level}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (level === currentLevel) return; // no-op\n\t\t\t\tpi.setThinkingLevel(level);\n\t\t\t\tctx.ui.notify(`Thinking level: ${level}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Direct-set form: `/thinking <level>`\n\t\t\tconst requested = trimmed.toLowerCase() as ThinkingLevel;\n\t\t\tif (!validSet.has(requested)) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Invalid thinking level: \"${trimmed}\". Valid: ${levels.join(\", \")}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (requested === currentLevel) {\n\t\t\t\tctx.ui.notify(`Thinking level: ${requested} (unchanged)`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpi.setThinkingLevel(requested);\n\t\t\tctx.ui.notify(`Thinking level: ${requested}`, \"info\");\n\t\t},\n\t};\n\n\tpi.registerCommand(\"thinking\", thinkingSpec);\n\t// lunr: /effort and /reasoning are full-parity aliases (same handler + completions).\n\tpi.registerCommand(\"effort\", {\n\t\t...thinkingSpec,\n\t\tdescription: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,\n\t});\n\tpi.registerCommand(\"reasoning\", {\n\t\t...thinkingSpec,\n\t\tdescription: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read the current model from the ExtensionAPI runtime. The spec mentions\n * `pi.getModel()`; pi doesn't expose that name on the API object itself, but\n * the bound `ExtensionContextActions` (which the runtime decorates onto\n * events) does include `getModel()`. We duck-type against the loader so this\n * file stays decoupled from non-public internals. When unavailable (e.g.\n * during autocomplete, before any session has bound), returns undefined and\n * the caller falls back to the default 5-level set.\n */\nfunction currentModel(pi: ExtensionAPI): ModelLike | undefined {\n\tconst fn = (pi as unknown as { getModel?: () => unknown }).getModel;\n\tif (typeof fn === \"function\") {\n\t\treturn fn.call(pi) as ModelLike | undefined;\n\t}\n\treturn undefined;\n}\n"]}
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * `/thinking [level]` slash command to view or set the reasoning level,
9
9
  * plus `/thinking show|hide|toggle` for thinking-block visibility.
10
+ * `/effort` and `/reasoning` are full-parity aliases.
10
11
  *
11
12
  * Loaded by pi via jiti — no build step, plain TypeScript.
12
13
  *
@@ -33,11 +34,11 @@ const ALL_LEVELS_5 = ["off", "minimal", "low", "medium", "high"];
33
34
  /** Per-level descriptions. Mirrored from `THINKING_DESCRIPTIONS` in settings-selector.ts. */
34
35
  const THINKING_DESCRIPTIONS = {
35
36
  off: "No reasoning",
36
- minimal: "Very brief reasoning (~1k tokens)",
37
- low: "Light reasoning (~2k tokens)",
38
- medium: "Moderate reasoning (~8k tokens)",
39
- high: "Deep reasoning (~16k tokens)",
40
- xhigh: "Extra-high reasoning (~32k tokens)",
37
+ minimal: "Very brief reasoning",
38
+ low: "Light reasoning",
39
+ medium: "Moderate reasoning",
40
+ high: "Deep reasoning",
41
+ xhigh: "Extra-high reasoning",
41
42
  // lunr: added — mirrors settings-selector.ts.
42
43
  max: "Maximum reasoning",
43
44
  };
@@ -183,9 +184,10 @@ function writeHideThinkingBlock(hide) {
183
184
  // ---------------------------------------------------------------------------
184
185
  // Extension entry point
185
186
  // ---------------------------------------------------------------------------
187
+ const THINKING_COMMAND_DESCRIPTION = "View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle";
186
188
  export default function (pi) {
187
- pi.registerCommand("thinking", {
188
- description: "View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle",
189
+ const thinkingSpec = {
190
+ description: THINKING_COMMAND_DESCRIPTION,
189
191
  getArgumentCompletions(prefix) {
190
192
  const lower = prefix.toLowerCase();
191
193
  // Visibility sub-options first, filtered by prefix, alongside the levels.
@@ -284,6 +286,16 @@ export default function (pi) {
284
286
  pi.setThinkingLevel(requested);
285
287
  ctx.ui.notify(`Thinking level: ${requested}`, "info");
286
288
  },
289
+ };
290
+ pi.registerCommand("thinking", thinkingSpec);
291
+ // lunr: /effort and /reasoning are full-parity aliases (same handler + completions).
292
+ pi.registerCommand("effort", {
293
+ ...thinkingSpec,
294
+ description: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,
295
+ });
296
+ pi.registerCommand("reasoning", {
297
+ ...thinkingSpec,
298
+ description: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,
287
299
  });
288
300
  }
289
301
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"ashxj-thinking.js","sourceRoot":"","sources":["../../src/builtin-extensions/ashxj-thinking.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n// Vendored from https://github.com/ashx-j/ashxj-thinking @ 0330a34\n// (\"Remove custom footer — keep only /thinking command\"), single-file ashxj\n// convention. lunR patches are marked `// lunr:`.\n/**\n * batxj-thinking extension\n *\n * `/thinking [level]` slash command to view or set the reasoning level,\n * plus `/thinking show|hide|toggle` for thinking-block visibility.\n *\n * Loaded by pi via jiti — no build step, plain TypeScript.\n *\n * NOTE on type imports: this file deliberately declares structural types\n * rather than importing from `@ashx-j/lunr` (or\n * `@ashx-j/lunr-tui`). pi-coding-agent's `index.d.ts` re-exports\n * from internal `.d.ts` files that contain subpath imports like\n * `@ashx-j/lunr-ai/compat`, which fail to resolve under the default\n * `tsc` settings when a file argument is passed (the spec'd\n * `tsc --noEmit index.ts` script). Declaring the types we use inline\n * keeps the typecheck self-contained.\n */\n\nimport { readFileSync, writeFileSync, existsSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Minimal structural types (see file header for why these are inline)\n// ---------------------------------------------------------------------------\n\n/** Mirrored from `@ashx-j/lunr-agent` `ThinkingLevel`. */\n// lunr: added \"max\" — lunR's ThinkingLevel union includes it.\ntype ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\n/** Mirrored from `@ashx-j/lunr-tui` `AutocompleteItem`. */\ninterface AutocompleteItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\n/** A trimmed view of a `Model` — only the fields the picker reads. */\ninterface ModelLike {\n\tid?: string;\n\treasoning?: boolean;\n\tthinkingLevelMap?: Record<string, string | null>;\n}\n\n/** A trimmed view of `ExtensionContext` — only the methods/props we use. */\ninterface ExtensionContextLike {\n\tmode: \"tui\" | \"rpc\" | \"json\" | \"print\";\n\tmodel: ModelLike | undefined;\n\tui: {\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void;\n\t\tselect(title: string, options: string[], opts?: { signal?: AbortSignal; timeout?: number }): Promise<string | undefined>;\n\t};\n}\n\n/** A trimmed view of `ExtensionCommandContext` — only the methods/props we use. */\ninterface ExtensionCommandContextLike extends ExtensionContextLike {\n\tgetThinkingLevel(): ThinkingLevel;\n\t/** Reload extensions, skills, prompts, and themes. Re-reads settings.json\n\t * from disk (session.reload() → settingsManager.reload()), so persisted\n\t * runtime settings like `hideThinkingBlock` are live-applied. The captured\n\t * `ctx`/`pi` are stale after this resolves — do not use them afterward. */\n\treload(): Promise<void>;\n}\n\n/** A trimmed view of `ExtensionAPI` — only the methods we use. */\ninterface ExtensionAPI {\n\tregisterCommand(name: string, options: {\n\t\tdescription?: string;\n\t\tgetArgumentCompletions?(prefix: string): AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;\n\t\thandler(args: string, ctx: ExtensionCommandContextLike): Promise<void> | void;\n\t}): void;\n\tgetThinkingLevel(): ThinkingLevel;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\n// ---------------------------------------------------------------------------\n// Thinking level config\n// ---------------------------------------------------------------------------\n\n/** All known levels, in display order. */\n// lunr: \"max\" appended (lunR supports it; filtered per-model below when unsupported).\nconst ALL_LEVELS_7: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n/** Default 5 — used when the active model is undefined (we don't know whether `xhigh`/`max` are supported). */\nconst ALL_LEVELS_5: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\n/** Per-level descriptions. Mirrored from `THINKING_DESCRIPTIONS` in settings-selector.ts. */\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\t// lunr: added — mirrors settings-selector.ts.\n\tmax: \"Maximum reasoning\",\n};\n\n/** Returns the levels valid for the given model, in display order. */\nfunction availableLevelsFor(model: ModelLike | undefined): readonly ThinkingLevel[] {\n\tif (!model) return ALL_LEVELS_5;\n\tif (model.reasoning === false) return [\"off\" as const];\n\t// Filter out levels the model marks as unsupported (null entry).\n\treturn ALL_LEVELS_7.filter((lvl) => {\n\t\tconst m = model.thinkingLevelMap;\n\t\tif (m && Object.prototype.hasOwnProperty.call(m, lvl) && (m as Record<string, string | null>)[lvl] === null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n}\n\n/** Builds the inline `<padded level> — <description>` label for a level. */\nfunction formatLevelLabel(level: ThinkingLevel): string {\n\treturn `${level.padEnd(8)} \\u2014 ${THINKING_DESCRIPTIONS[level]}`;\n}\n\n// ---------------------------------------------------------------------------\n// Persisted thinking-block visibility (hideThinkingBlock)\n//\n// pi has no public extension API to toggle thinking-block visibility live.\n// The live in-session toggle is `app.thinking.toggle` (default Ctrl+T) wired to\n// `toggleThinkingBlockVisibility()` in interactive-mode.js, which calls\n// `SettingsManager.setHideThinkingBlock()` + rebuilds the chat. That method is\n// not exposed on ExtensionContext / ExtensionCommandContext / ExtensionAPI.\n//\n// What extensions CAN do is persist the `hideThinkingBlock` boolean to the\n// global agent settings file (`~/.lunr/agent/settings.json`); the\n// `SettingsManager` reads it on startup. And because `ctx.reload()` drives\n// `session.reload()` → `settingsManager.reload()` (re-reads disk) →\n// `restoreChatBeforeSessionStart()` copies the new value into\n// `this.hideThinkingBlock` and calls `rebuildChatFromMessages()` (which\n// reconstructs every AssistantMessageComponent with the new hide flag), the\n// persisted value IS live-applied within the session by `await ctx.reload()`\n// (verified against pi 0.80.3). In non-TUI modes `ctx.reload()` is a no-op,\n// so the setting simply applies on next start.\n//\n// The instant keybinding path (Ctrl+T) still works and is the lowest-latency\n// toggle; the command is the discoverable, persistable, completions-friendly\n// equivalent.\n// ---------------------------------------------------------------------------\n\n/** Returns the path to lunR's global agent settings file.\n * lunr: lunR's agent dir is `~/.lunr/agent` (upstream hardcoded `~/.pi/agent`).\n * Honors the `PI_CODING_AGENT_DIR` env override like core config.ts does;\n * a custom agent dir is not exposed to extensions, so this is the only\n * location we can write to be picked up by\n * `SettingsManager.tryLoadFromStorage(\"global\")`. */\nfunction agentSettingsPath(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\t// Match core normalizePath(): expand a leading ~ (~/ and, on win32, ~\\).\n\t\tconst home = homedir();\n\t\tconst expanded =\n\t\t\tenvDir === \"~\"\n\t\t\t\t? home\n\t\t\t\t: envDir.startsWith(\"~/\") || (process.platform === \"win32\" && envDir.startsWith(\"~\\\\\"))\n\t\t\t\t\t? join(home, envDir.slice(2))\n\t\t\t\t\t: envDir;\n\t\treturn join(expanded, \"settings.json\");\n\t}\n\treturn join(homedir(), \".lunr\", \"agent\", \"settings.json\");\n}\n\n// ---------------------------------------------------------------------------\n// lunR customize bridge for persisted thinking-block visibility.\n// When the bridge is present (TUI / gateway sessions), reads/writes go through\n// SettingsManager (lockfile + merge). Falls back to direct file I/O when the\n// extension loads in a context without the bridge (e.g. upstream pi).\n// ---------------------------------------------------------------------------\ninterface CustomizeBridge {\n\tgetHideThinkingBlock(): boolean;\n\tsetHideThinkingBlock(hide: boolean): void;\n}\n\nfunction getCustomizeBridge(): CustomizeBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/customize\")] as CustomizeBridge | undefined;\n}\n\n/** Returns the persisted `hideThinkingBlock` value (default false, matching\n * `getHideThinkingBlock()` which returns `this.settings.hideThinkingBlock ?? false`).\n * Never throws — on any error (missing file, bad JSON) returns false. */\nfunction readHideThinkingBlock(): boolean {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) return bridge.getHideThinkingBlock();\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tif (!existsSync(p)) return false;\n\t\tconst cfg = JSON.parse(readFileSync(p, \"utf8\")) as { hideThinkingBlock?: boolean };\n\t\treturn cfg.hideThinkingBlock === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Persist `hideThinkingBlock` to the global agent settings file (safe merge).\n *\n * - Preserves all other keys (merge, never overwrite the whole file).\n * - Matches pi's 2-space indentation + trailing newline.\n * - On invalid JSON in the existing file, backs it up to `settings.json.bak`\n * before writing a fresh file, so a corrupted file is never written back.\n * - Never throws; on any error `ok` is false and the caller notifies the user.\n *\n * `createdFresh` is true when the file didn't exist (or was corrupt and backed\n * up) before this write — the handler uses it to warn that a global settings\n * file was created here.\n */\nfunction writeHideThinkingBlock(hide: boolean): {\n\tok: boolean;\n\tcreatedFresh: boolean;\n} {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) {\n\t\ttry {\n\t\t\tbridge.setHideThinkingBlock(hide);\n\t\t\treturn { ok: true, createdFresh: false };\n\t\t} catch {\n\t\t\treturn { ok: false, createdFresh: false };\n\t\t}\n\t}\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tlet cfg: Record<string, unknown> = {};\n\t\tlet createdFresh = false;\n\t\tif (existsSync(p)) {\n\t\t\ttry {\n\t\t\t\tcfg = JSON.parse(readFileSync(p, \"utf8\")) as Record<string, unknown>;\n\t\t\t} catch {\n\t\t\t\t// Existing file is corrupt — back it up, then start fresh.\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(p, `${p}.bak`);\n\t\t\t\t} catch {\n\t\t\t\t\t// If backup fails, still proceed to overwrite (best effort).\n\t\t\t\t}\n\t\t\t\tcfg = {};\n\t\t\t\tcreatedFresh = true;\n\t\t\t}\n\t\t} else {\n\t\t\tcreatedFresh = true;\n\t\t}\n\t\tcfg.hideThinkingBlock = hide;\n\t\twriteFileSync(p, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf8\");\n\t\treturn { ok: true, createdFresh };\n\t} catch {\n\t\treturn { ok: false, createdFresh: false };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Extension entry point\n// ---------------------------------------------------------------------------\n\nexport default function (pi: ExtensionAPI): void {\n\tpi.registerCommand(\"thinking\", {\n\t\tdescription: \"View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle\",\n\t\tgetArgumentCompletions(prefix: string): AutocompleteItem[] {\n\t\t\tconst lower = prefix.toLowerCase();\n\t\t\t// Visibility sub-options first, filtered by prefix, alongside the levels.\n\t\t\tconst visibility: AutocompleteItem[] = [\n\t\t\t\t{ value: \"show\", label: \"show\", description: \"Show thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"hide\", label: \"hide\", description: \"Hide thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"toggle\", label: \"toggle\", description: \"Toggle thinking blocks (persisted to settings.json)\" },\n\t\t\t];\n\t\t\tconst levels = availableLevelsFor(currentModel(pi));\n\t\t\tconst levelItems = levels\n\t\t\t\t.filter((lvl) => lvl.startsWith(lower))\n\t\t\t\t.map((lvl) => ({ value: lvl, label: lvl, description: THINKING_DESCRIPTIONS[lvl] }));\n\t\t\treturn [\n\t\t\t\t...visibility.filter((v) => v.value.startsWith(lower)),\n\t\t\t\t...levelItems,\n\t\t\t];\n\t\t},\n\t\thandler: async (args: string, ctx: ExtensionCommandContextLike) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst lower = trimmed.toLowerCase();\n\n\t\t\t// ---- Visibility sub-options: /thinking show | hide | toggle ----\n\t\t\t// Caught first so `hide`/`show`/`toggle` are never parsed as levels.\n\t\t\t// Works in every mode: only uses ctx.ui.notify + file I/O + ctx.reload().\n\t\t\tif (lower === \"show\" || lower === \"hide\" || lower === \"toggle\") {\n\t\t\t\tconst current = readHideThinkingBlock();\n\t\t\t\tconst next = lower === \"toggle\" ? !current : lower === \"hide\";\n\t\t\t\tif (next === current) {\n\t\t\t\t\tctx.ui.notify(`Thinking blocks already ${current ? \"hidden\" : \"shown\"}.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = writeHideThinkingBlock(next);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\t// lunr: settings path is lunR's (see agentSettingsPath).\n\t\t\t\t\tctx.ui.notify(`Failed to update ${agentSettingsPath()}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// The notify MUST run before `await ctx.reload()`: after reload the\n\t\t\t\t// loader invalidates this `ctx`/`pi` as stale. ctx.reload() live-\n\t\t\t\t// applies the setting (verified against pi 0.80.3: it drives\n\t\t\t\t// session.reload() → settingsManager.reload() re-reads disk →\n\t\t\t\t// restoreChatBeforeSessionStart copies the new value into\n\t\t\t\t// this.hideThinkingBlock → rebuildChatFromMessages() rebuilds every\n\t\t\t\t// AssistantMessageComponent with the new hide flag). In non-TUI\n\t\t\t\t// modes ctx.reload() is a no-op; the setting then applies on next\n\t\t\t\t// start. The instant keybinding path (Ctrl+T) also still works.\n\t\t\t\tconst verb = next ? \"hidden\" : \"shown\";\n\t\t\t\tconst note = result.createdFresh\n\t\t\t\t\t? `Thinking blocks ${verb}. (Created ${agentSettingsPath()} with this setting.)`\n\t\t\t\t\t: `Thinking blocks ${verb}.`;\n\t\t\t\tctx.ui.notify(note, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst levels = availableLevelsFor(ctx.model);\n\t\t\tconst currentLevel: ThinkingLevel = pi.getThinkingLevel();\n\t\t\tconst validSet = new Set<ThinkingLevel>(levels);\n\n\t\t\t// No-arg form → open a picker. Skip in non-TUI modes — same pattern\n\t\t\t// as batxj-animations per AGENTS.md §5.\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(`Thinking level: ${currentLevel}`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (levels.length === 0) {\n\t\t\t\t\tctx.ui.notify(\"No thinking levels available for this model.\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst options = levels.map((lvl) =>\n\t\t\t\t\t(lvl === currentLevel ? \"\\u25CF \" : \" \") + formatLevelLabel(lvl),\n\t\t\t\t);\n\t\t\t\tconst chosen = await ctx.ui.select(\n\t\t\t\t\t`Thinking level (current: ${currentLevel})`,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\tif (chosen === undefined) return;\n\t\t\t\t// Chosen string starts with the current-marker (\"● \") or two-space\n\t\t\t\t// gutter; strip the prefix and extract the first token (the level).\n\t\t\t\tconst stripped = chosen.replace(/^[\\u25CF ]\\s?/, \"\").trimStart();\n\t\t\t\tconst level = stripped.split(/\\s+/)[0] as ThinkingLevel;\n\t\t\t\tif (!validSet.has(level)) {\n\t\t\t\t\tctx.ui.notify(`Unsupported thinking level: ${level}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (level === currentLevel) return; // no-op\n\t\t\t\tpi.setThinkingLevel(level);\n\t\t\t\tctx.ui.notify(`Thinking level: ${level}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Direct-set form: `/thinking <level>`\n\t\t\tconst requested = trimmed.toLowerCase() as ThinkingLevel;\n\t\t\tif (!validSet.has(requested)) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Invalid thinking level: \"${trimmed}\". Valid: ${levels.join(\", \")}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (requested === currentLevel) {\n\t\t\t\tctx.ui.notify(`Thinking level: ${requested} (unchanged)`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpi.setThinkingLevel(requested);\n\t\t\tctx.ui.notify(`Thinking level: ${requested}`, \"info\");\n\t\t},\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read the current model from the ExtensionAPI runtime. The spec mentions\n * `pi.getModel()`; pi doesn't expose that name on the API object itself, but\n * the bound `ExtensionContextActions` (which the runtime decorates onto\n * events) does include `getModel()`. We duck-type against the loader so this\n * file stays decoupled from non-public internals. When unavailable (e.g.\n * during autocomplete, before any session has bound), returns undefined and\n * the caller falls back to the default 5-level set.\n */\nfunction currentModel(pi: ExtensionAPI): ModelLike | undefined {\n\tconst fn = (pi as unknown as { getModel?: () => unknown }).getModel;\n\tif (typeof fn === \"function\") {\n\t\treturn fn.call(pi) as ModelLike | undefined;\n\t}\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"ashxj-thinking.js","sourceRoot":"","sources":["../../src/builtin-extensions/ashxj-thinking.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n// Vendored from https://github.com/ashx-j/ashxj-thinking @ 0330a34\n// (\"Remove custom footer — keep only /thinking command\"), single-file ashxj\n// convention. lunR patches are marked `// lunr:`.\n/**\n * batxj-thinking extension\n *\n * `/thinking [level]` slash command to view or set the reasoning level,\n * plus `/thinking show|hide|toggle` for thinking-block visibility.\n * `/effort` and `/reasoning` are full-parity aliases.\n *\n * Loaded by pi via jiti — no build step, plain TypeScript.\n *\n * NOTE on type imports: this file deliberately declares structural types\n * rather than importing from `@ashx-j/lunr` (or\n * `@ashx-j/lunr-tui`). pi-coding-agent's `index.d.ts` re-exports\n * from internal `.d.ts` files that contain subpath imports like\n * `@ashx-j/lunr-ai/compat`, which fail to resolve under the default\n * `tsc` settings when a file argument is passed (the spec'd\n * `tsc --noEmit index.ts` script). Declaring the types we use inline\n * keeps the typecheck self-contained.\n */\n\nimport { readFileSync, writeFileSync, existsSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Minimal structural types (see file header for why these are inline)\n// ---------------------------------------------------------------------------\n\n/** Mirrored from `@ashx-j/lunr-agent` `ThinkingLevel`. */\n// lunr: added \"max\" — lunR's ThinkingLevel union includes it.\ntype ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\n/** Mirrored from `@ashx-j/lunr-tui` `AutocompleteItem`. */\ninterface AutocompleteItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\n/** A trimmed view of a `Model` — only the fields the picker reads. */\ninterface ModelLike {\n\tid?: string;\n\treasoning?: boolean;\n\tthinkingLevelMap?: Record<string, string | null>;\n}\n\n/** A trimmed view of `ExtensionContext` — only the methods/props we use. */\ninterface ExtensionContextLike {\n\tmode: \"tui\" | \"rpc\" | \"json\" | \"print\";\n\tmodel: ModelLike | undefined;\n\tui: {\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void;\n\t\tselect(title: string, options: string[], opts?: { signal?: AbortSignal; timeout?: number }): Promise<string | undefined>;\n\t};\n}\n\n/** A trimmed view of `ExtensionCommandContext` — only the methods/props we use. */\ninterface ExtensionCommandContextLike extends ExtensionContextLike {\n\tgetThinkingLevel(): ThinkingLevel;\n\t/** Reload extensions, skills, prompts, and themes. Re-reads settings.json\n\t * from disk (session.reload() → settingsManager.reload()), so persisted\n\t * runtime settings like `hideThinkingBlock` are live-applied. The captured\n\t * `ctx`/`pi` are stale after this resolves — do not use them afterward. */\n\treload(): Promise<void>;\n}\n\n/** A trimmed view of `ExtensionAPI` — only the methods we use. */\ninterface ExtensionAPI {\n\tregisterCommand(name: string, options: {\n\t\tdescription?: string;\n\t\tgetArgumentCompletions?(prefix: string): AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;\n\t\thandler(args: string, ctx: ExtensionCommandContextLike): Promise<void> | void;\n\t}): void;\n\tgetThinkingLevel(): ThinkingLevel;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n}\n\n// ---------------------------------------------------------------------------\n// Thinking level config\n// ---------------------------------------------------------------------------\n\n/** All known levels, in display order. */\n// lunr: \"max\" appended (lunR supports it; filtered per-model below when unsupported).\nconst ALL_LEVELS_7: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n/** Default 5 — used when the active model is undefined (we don't know whether `xhigh`/`max` are supported). */\nconst ALL_LEVELS_5: readonly ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\n/** Per-level descriptions. Mirrored from `THINKING_DESCRIPTIONS` in settings-selector.ts. */\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning\",\n\tlow: \"Light reasoning\",\n\tmedium: \"Moderate reasoning\",\n\thigh: \"Deep reasoning\",\n\txhigh: \"Extra-high reasoning\",\n\t// lunr: added — mirrors settings-selector.ts.\n\tmax: \"Maximum reasoning\",\n};\n\n/** Returns the levels valid for the given model, in display order. */\nfunction availableLevelsFor(model: ModelLike | undefined): readonly ThinkingLevel[] {\n\tif (!model) return ALL_LEVELS_5;\n\tif (model.reasoning === false) return [\"off\" as const];\n\t// Filter out levels the model marks as unsupported (null entry).\n\treturn ALL_LEVELS_7.filter((lvl) => {\n\t\tconst m = model.thinkingLevelMap;\n\t\tif (m && Object.prototype.hasOwnProperty.call(m, lvl) && (m as Record<string, string | null>)[lvl] === null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n}\n\n/** Builds the inline `<padded level> — <description>` label for a level. */\nfunction formatLevelLabel(level: ThinkingLevel): string {\n\treturn `${level.padEnd(8)} \\u2014 ${THINKING_DESCRIPTIONS[level]}`;\n}\n\n// ---------------------------------------------------------------------------\n// Persisted thinking-block visibility (hideThinkingBlock)\n//\n// pi has no public extension API to toggle thinking-block visibility live.\n// The live in-session toggle is `app.thinking.toggle` (default Ctrl+T) wired to\n// `toggleThinkingBlockVisibility()` in interactive-mode.js, which calls\n// `SettingsManager.setHideThinkingBlock()` + rebuilds the chat. That method is\n// not exposed on ExtensionContext / ExtensionCommandContext / ExtensionAPI.\n//\n// What extensions CAN do is persist the `hideThinkingBlock` boolean to the\n// global agent settings file (`~/.lunr/agent/settings.json`); the\n// `SettingsManager` reads it on startup. And because `ctx.reload()` drives\n// `session.reload()` → `settingsManager.reload()` (re-reads disk) →\n// `restoreChatBeforeSessionStart()` copies the new value into\n// `this.hideThinkingBlock` and calls `rebuildChatFromMessages()` (which\n// reconstructs every AssistantMessageComponent with the new hide flag), the\n// persisted value IS live-applied within the session by `await ctx.reload()`\n// (verified against pi 0.80.3). In non-TUI modes `ctx.reload()` is a no-op,\n// so the setting simply applies on next start.\n//\n// The instant keybinding path (Ctrl+T) still works and is the lowest-latency\n// toggle; the command is the discoverable, persistable, completions-friendly\n// equivalent.\n// ---------------------------------------------------------------------------\n\n/** Returns the path to lunR's global agent settings file.\n * lunr: lunR's agent dir is `~/.lunr/agent` (upstream hardcoded `~/.pi/agent`).\n * Honors the `PI_CODING_AGENT_DIR` env override like core config.ts does;\n * a custom agent dir is not exposed to extensions, so this is the only\n * location we can write to be picked up by\n * `SettingsManager.tryLoadFromStorage(\"global\")`. */\nfunction agentSettingsPath(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\t// Match core normalizePath(): expand a leading ~ (~/ and, on win32, ~\\).\n\t\tconst home = homedir();\n\t\tconst expanded =\n\t\t\tenvDir === \"~\"\n\t\t\t\t? home\n\t\t\t\t: envDir.startsWith(\"~/\") || (process.platform === \"win32\" && envDir.startsWith(\"~\\\\\"))\n\t\t\t\t\t? join(home, envDir.slice(2))\n\t\t\t\t\t: envDir;\n\t\treturn join(expanded, \"settings.json\");\n\t}\n\treturn join(homedir(), \".lunr\", \"agent\", \"settings.json\");\n}\n\n// ---------------------------------------------------------------------------\n// lunR customize bridge for persisted thinking-block visibility.\n// When the bridge is present (TUI / gateway sessions), reads/writes go through\n// SettingsManager (lockfile + merge). Falls back to direct file I/O when the\n// extension loads in a context without the bridge (e.g. upstream pi).\n// ---------------------------------------------------------------------------\ninterface CustomizeBridge {\n\tgetHideThinkingBlock(): boolean;\n\tsetHideThinkingBlock(hide: boolean): void;\n}\n\nfunction getCustomizeBridge(): CustomizeBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/customize\")] as CustomizeBridge | undefined;\n}\n\n/** Returns the persisted `hideThinkingBlock` value (default false, matching\n * `getHideThinkingBlock()` which returns `this.settings.hideThinkingBlock ?? false`).\n * Never throws — on any error (missing file, bad JSON) returns false. */\nfunction readHideThinkingBlock(): boolean {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) return bridge.getHideThinkingBlock();\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tif (!existsSync(p)) return false;\n\t\tconst cfg = JSON.parse(readFileSync(p, \"utf8\")) as { hideThinkingBlock?: boolean };\n\t\treturn cfg.hideThinkingBlock === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Persist `hideThinkingBlock` to the global agent settings file (safe merge).\n *\n * - Preserves all other keys (merge, never overwrite the whole file).\n * - Matches pi's 2-space indentation + trailing newline.\n * - On invalid JSON in the existing file, backs it up to `settings.json.bak`\n * before writing a fresh file, so a corrupted file is never written back.\n * - Never throws; on any error `ok` is false and the caller notifies the user.\n *\n * `createdFresh` is true when the file didn't exist (or was corrupt and backed\n * up) before this write — the handler uses it to warn that a global settings\n * file was created here.\n */\nfunction writeHideThinkingBlock(hide: boolean): {\n\tok: boolean;\n\tcreatedFresh: boolean;\n} {\n\tconst bridge = getCustomizeBridge();\n\tif (bridge) {\n\t\ttry {\n\t\t\tbridge.setHideThinkingBlock(hide);\n\t\t\treturn { ok: true, createdFresh: false };\n\t\t} catch {\n\t\t\treturn { ok: false, createdFresh: false };\n\t\t}\n\t}\n\ttry {\n\t\tconst p = agentSettingsPath();\n\t\tlet cfg: Record<string, unknown> = {};\n\t\tlet createdFresh = false;\n\t\tif (existsSync(p)) {\n\t\t\ttry {\n\t\t\t\tcfg = JSON.parse(readFileSync(p, \"utf8\")) as Record<string, unknown>;\n\t\t\t} catch {\n\t\t\t\t// Existing file is corrupt — back it up, then start fresh.\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(p, `${p}.bak`);\n\t\t\t\t} catch {\n\t\t\t\t\t// If backup fails, still proceed to overwrite (best effort).\n\t\t\t\t}\n\t\t\t\tcfg = {};\n\t\t\t\tcreatedFresh = true;\n\t\t\t}\n\t\t} else {\n\t\t\tcreatedFresh = true;\n\t\t}\n\t\tcfg.hideThinkingBlock = hide;\n\t\twriteFileSync(p, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf8\");\n\t\treturn { ok: true, createdFresh };\n\t} catch {\n\t\treturn { ok: false, createdFresh: false };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Extension entry point\n// ---------------------------------------------------------------------------\n\nconst THINKING_COMMAND_DESCRIPTION =\n\t\"View or set the reasoning level for the current model, or toggle thinking-block visibility with show/hide/toggle\";\n\nexport default function (pi: ExtensionAPI): void {\n\tconst thinkingSpec = {\n\t\tdescription: THINKING_COMMAND_DESCRIPTION,\n\t\tgetArgumentCompletions(prefix: string): AutocompleteItem[] {\n\t\t\tconst lower = prefix.toLowerCase();\n\t\t\t// Visibility sub-options first, filtered by prefix, alongside the levels.\n\t\t\tconst visibility: AutocompleteItem[] = [\n\t\t\t\t{ value: \"show\", label: \"show\", description: \"Show thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"hide\", label: \"hide\", description: \"Hide thinking blocks (persisted to settings.json)\" },\n\t\t\t\t{ value: \"toggle\", label: \"toggle\", description: \"Toggle thinking blocks (persisted to settings.json)\" },\n\t\t\t];\n\t\t\tconst levels = availableLevelsFor(currentModel(pi));\n\t\t\tconst levelItems = levels\n\t\t\t\t.filter((lvl) => lvl.startsWith(lower))\n\t\t\t\t.map((lvl) => ({ value: lvl, label: lvl, description: THINKING_DESCRIPTIONS[lvl] }));\n\t\t\treturn [\n\t\t\t\t...visibility.filter((v) => v.value.startsWith(lower)),\n\t\t\t\t...levelItems,\n\t\t\t];\n\t\t},\n\t\thandler: async (args: string, ctx: ExtensionCommandContextLike) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst lower = trimmed.toLowerCase();\n\n\t\t\t// ---- Visibility sub-options: /thinking show | hide | toggle ----\n\t\t\t// Caught first so `hide`/`show`/`toggle` are never parsed as levels.\n\t\t\t// Works in every mode: only uses ctx.ui.notify + file I/O + ctx.reload().\n\t\t\tif (lower === \"show\" || lower === \"hide\" || lower === \"toggle\") {\n\t\t\t\tconst current = readHideThinkingBlock();\n\t\t\t\tconst next = lower === \"toggle\" ? !current : lower === \"hide\";\n\t\t\t\tif (next === current) {\n\t\t\t\t\tctx.ui.notify(`Thinking blocks already ${current ? \"hidden\" : \"shown\"}.`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = writeHideThinkingBlock(next);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\t// lunr: settings path is lunR's (see agentSettingsPath).\n\t\t\t\t\tctx.ui.notify(`Failed to update ${agentSettingsPath()}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// The notify MUST run before `await ctx.reload()`: after reload the\n\t\t\t\t// loader invalidates this `ctx`/`pi` as stale. ctx.reload() live-\n\t\t\t\t// applies the setting (verified against pi 0.80.3: it drives\n\t\t\t\t// session.reload() → settingsManager.reload() re-reads disk →\n\t\t\t\t// restoreChatBeforeSessionStart copies the new value into\n\t\t\t\t// this.hideThinkingBlock → rebuildChatFromMessages() rebuilds every\n\t\t\t\t// AssistantMessageComponent with the new hide flag). In non-TUI\n\t\t\t\t// modes ctx.reload() is a no-op; the setting then applies on next\n\t\t\t\t// start. The instant keybinding path (Ctrl+T) also still works.\n\t\t\t\tconst verb = next ? \"hidden\" : \"shown\";\n\t\t\t\tconst note = result.createdFresh\n\t\t\t\t\t? `Thinking blocks ${verb}. (Created ${agentSettingsPath()} with this setting.)`\n\t\t\t\t\t: `Thinking blocks ${verb}.`;\n\t\t\t\tctx.ui.notify(note, \"info\");\n\t\t\t\tawait ctx.reload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst levels = availableLevelsFor(ctx.model);\n\t\t\tconst currentLevel: ThinkingLevel = pi.getThinkingLevel();\n\t\t\tconst validSet = new Set<ThinkingLevel>(levels);\n\n\t\t\t// No-arg form → open a picker. Skip in non-TUI modes — same pattern\n\t\t\t// as batxj-animations per AGENTS.md §5.\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\t\tctx.ui.notify(`Thinking level: ${currentLevel}`, \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (levels.length === 0) {\n\t\t\t\t\tctx.ui.notify(\"No thinking levels available for this model.\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst options = levels.map((lvl) =>\n\t\t\t\t\t(lvl === currentLevel ? \"\\u25CF \" : \" \") + formatLevelLabel(lvl),\n\t\t\t\t);\n\t\t\t\tconst chosen = await ctx.ui.select(\n\t\t\t\t\t`Thinking level (current: ${currentLevel})`,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\tif (chosen === undefined) return;\n\t\t\t\t// Chosen string starts with the current-marker (\"● \") or two-space\n\t\t\t\t// gutter; strip the prefix and extract the first token (the level).\n\t\t\t\tconst stripped = chosen.replace(/^[\\u25CF ]\\s?/, \"\").trimStart();\n\t\t\t\tconst level = stripped.split(/\\s+/)[0] as ThinkingLevel;\n\t\t\t\tif (!validSet.has(level)) {\n\t\t\t\t\tctx.ui.notify(`Unsupported thinking level: ${level}`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (level === currentLevel) return; // no-op\n\t\t\t\tpi.setThinkingLevel(level);\n\t\t\t\tctx.ui.notify(`Thinking level: ${level}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Direct-set form: `/thinking <level>`\n\t\t\tconst requested = trimmed.toLowerCase() as ThinkingLevel;\n\t\t\tif (!validSet.has(requested)) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Invalid thinking level: \"${trimmed}\". Valid: ${levels.join(\", \")}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (requested === currentLevel) {\n\t\t\t\tctx.ui.notify(`Thinking level: ${requested} (unchanged)`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpi.setThinkingLevel(requested);\n\t\t\tctx.ui.notify(`Thinking level: ${requested}`, \"info\");\n\t\t},\n\t};\n\n\tpi.registerCommand(\"thinking\", thinkingSpec);\n\t// lunr: /effort and /reasoning are full-parity aliases (same handler + completions).\n\tpi.registerCommand(\"effort\", {\n\t\t...thinkingSpec,\n\t\tdescription: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,\n\t});\n\tpi.registerCommand(\"reasoning\", {\n\t\t...thinkingSpec,\n\t\tdescription: `${THINKING_COMMAND_DESCRIPTION} (alias of /thinking)`,\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read the current model from the ExtensionAPI runtime. The spec mentions\n * `pi.getModel()`; pi doesn't expose that name on the API object itself, but\n * the bound `ExtensionContextActions` (which the runtime decorates onto\n * events) does include `getModel()`. We duck-type against the loader so this\n * file stays decoupled from non-public internals. When unavailable (e.g.\n * during autocomplete, before any session has bound), returns undefined and\n * the caller falls back to the default 5-level set.\n */\nfunction currentModel(pi: ExtensionAPI): ModelLike | undefined {\n\tconst fn = (pi as unknown as { getModel?: () => unknown }).getModel;\n\tif (typeof fn === \"function\") {\n\t\treturn fn.call(pi) as ModelLike | undefined;\n\t}\n\treturn undefined;\n}\n"]}