@rol1n/pi-kit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -50
  3. package/README_zh.md +136 -0
  4. package/ask-u-questions/README.md +26 -0
  5. package/{extensions/ask-u-questions → ask-u-questions}/dialog-ui.ts +23 -0
  6. package/{extensions/ask-u-questions → ask-u-questions}/index.ts +9 -5
  7. package/{extensions/ask-u-questions → ask-u-questions}/multi-choice.ts +1 -3
  8. package/{extensions/ask-u-questions → ask-u-questions}/single-choice.ts +1 -3
  9. package/{extensions/ask-u-questions → ask-u-questions}/tool-renderers.ts +1 -3
  10. package/ask-u-questions/types.ts +192 -0
  11. package/continue-signal/README.md +20 -0
  12. package/{extensions/continue-signal → continue-signal}/index.ts +21 -3
  13. package/custom-providers/README.md +20 -0
  14. package/{extensions/custom-providers → custom-providers}/index.ts +16 -2
  15. package/image-generate/README.md +25 -0
  16. package/{extensions/image-generate → image-generate}/index.ts +22 -2
  17. package/{extensions/image-generate → image-generate}/model-selector.ts +1 -1
  18. package/memory-log/README.md +121 -0
  19. package/{extensions/memory-log → memory-log}/config.ts +1 -1
  20. package/{extensions/memory-log → memory-log}/hooks/compaction-hook.ts +58 -15
  21. package/memory-log/hooks/compaction-trigger.ts +87 -0
  22. package/{extensions/memory-log → memory-log}/hooks/observer-trigger.ts +2 -1
  23. package/{extensions/memory-log → memory-log}/index.ts +4 -0
  24. package/{extensions/memory-log → memory-log}/ledger/index.ts +1 -0
  25. package/memory-log/ledger/recall.ts +111 -0
  26. package/{extensions/memory-log → memory-log}/ledger/render.ts +3 -1
  27. package/{extensions/memory-log → memory-log}/ledger/serialize.ts +2 -4
  28. package/{extensions/memory-log → memory-log}/ledger/types.ts +4 -1
  29. package/{extensions/memory-log → memory-log}/runtime.ts +0 -5
  30. package/memory-log/tools/recall.ts +60 -0
  31. package/model-vision/README.md +22 -0
  32. package/{extensions/multi-harness → multi-harness}/README.md +3 -3
  33. package/oai-subquota/README.md +22 -0
  34. package/{extensions/oai-subquota → oai-subquota}/index.ts +16 -6
  35. package/{extensions/oai-subquota → oai-subquota}/snapshot.ts +127 -1
  36. package/{extensions/oai-subquota → oai-subquota}/ui.ts +1 -1
  37. package/package.json +46 -46
  38. package/prompt-snipaste/README.md +27 -0
  39. package/{extensions/prompt-snipaste → prompt-snipaste}/renderers.ts +0 -1
  40. package/{extensions/prompt-snipaste → prompt-snipaste}/snippets.ts +5 -2
  41. package/{extensions/prompt-snipaste → prompt-snipaste}/types.ts +0 -1
  42. package/screenshot/README.md +14 -0
  43. package/srp-theme/README.md +18 -0
  44. package/{extensions/srp-theme → srp-theme}/footer.ts +0 -1
  45. package/{extensions/srp-theme → srp-theme}/tps-meter.ts +0 -1
  46. package/{extensions/subagent → subagent}/README.md +1 -1
  47. package/tui-asr/README.md +21 -0
  48. package/{extensions/tui-asr → tui-asr}/dictation.ts +70 -3
  49. package/{extensions/tui-asr → tui-asr}/index.ts +2 -2
  50. package/{extensions/tui-asr → tui-asr}/input.ts +30 -1
  51. package/{extensions/tui-asr → tui-asr}/model-selector.ts +1 -1
  52. package/web-reach/README.md +19 -0
  53. package/windows-wheel-fix/README.md +12 -0
  54. package/extensions/ask-u-questions/README.md +0 -32
  55. package/extensions/ask-u-questions/answers.ts +0 -53
  56. package/extensions/ask-u-questions/results.ts +0 -83
  57. package/extensions/ask-u-questions/schema.ts +0 -17
  58. package/extensions/ask-u-questions/types.ts +0 -45
  59. package/extensions/ask-u-questions/ui-lock.ts +0 -24
  60. package/extensions/continue-signal/README.md +0 -28
  61. package/extensions/continue-signal/renderer.ts +0 -21
  62. package/extensions/custom-providers/README.md +0 -28
  63. package/extensions/image-generate/README.md +0 -29
  64. package/extensions/image-generate/models.ts +0 -19
  65. package/extensions/memory-log/README.md +0 -54
  66. package/extensions/memory-log/hooks/compaction-trigger.ts +0 -160
  67. package/extensions/model-vision/README.md +0 -28
  68. package/extensions/oai-subquota/README.md +0 -30
  69. package/extensions/oai-subquota/api.ts +0 -27
  70. package/extensions/oai-subquota/auth.ts +0 -34
  71. package/extensions/oai-subquota/cache.ts +0 -30
  72. package/extensions/oai-subquota/types.ts +0 -40
  73. package/extensions/prompt-snipaste/README.md +0 -33
  74. package/extensions/screenshot/README.md +0 -25
  75. package/extensions/srp-theme/README.md +0 -24
  76. package/extensions/subagent/skills/subagent/SKILL.md +0 -70
  77. package/extensions/tui-asr/README.md +0 -28
  78. package/extensions/tui-asr/audio.ts +0 -40
  79. package/extensions/tui-asr/clipboard.ts +0 -30
  80. package/extensions/tui-asr/providers.ts +0 -25
  81. package/extensions/web-reach/README.md +0 -37
  82. package/extensions/web-reach/html.ts +0 -83
  83. package/extensions/web-reach/index.ts +0 -82
  84. package/extensions/web-reach/page.ts +0 -132
  85. package/extensions/web-reach/pdf.ts +0 -42
  86. package/extensions/web-reach/reader.ts +0 -42
  87. package/extensions/web-reach/rsc.ts +0 -161
  88. package/extensions/web-reach/search.ts +0 -85
  89. package/extensions/web-reach/transport.ts +0 -35
  90. package/extensions/windows-wheel-fix/README.md +0 -21
  91. /package/{extensions/custom-providers → custom-providers}/asr/handlers/dashscope.ts +0 -0
  92. /package/{extensions/custom-providers → custom-providers}/asr/index.ts +0 -0
  93. /package/{extensions/custom-providers → custom-providers}/asr/models.ts +0 -0
  94. /package/{extensions/custom-providers → custom-providers}/asr/types.ts +0 -0
  95. /package/{extensions/custom-providers → custom-providers}/chat/index.ts +0 -0
  96. /package/{extensions/custom-providers → custom-providers}/chat/magpie.ts +0 -0
  97. /package/{extensions/custom-providers → custom-providers}/chat/models.ts +0 -0
  98. /package/{extensions/custom-providers → custom-providers}/classifier/index.ts +0 -0
  99. /package/{extensions/custom-providers → custom-providers}/classifier/models.ts +0 -0
  100. /package/{extensions/custom-providers → custom-providers}/image/handlers/dashscope.ts +0 -0
  101. /package/{extensions/custom-providers → custom-providers}/image/handlers/openrouter.ts +0 -0
  102. /package/{extensions/custom-providers → custom-providers}/image/index.ts +0 -0
  103. /package/{extensions/custom-providers → custom-providers}/image/models.ts +0 -0
  104. /package/{extensions/custom-providers → custom-providers}/image/types.ts +0 -0
  105. /package/{extensions/custom-providers → custom-providers}/types.ts +0 -0
  106. /package/{extensions/image-generate → image-generate}/files.ts +0 -0
  107. /package/{extensions/memory-log → memory-log}/agent/consolidator/prompt.ts +0 -0
  108. /package/{extensions/memory-log → memory-log}/agent/consolidator/tools.ts +0 -0
  109. /package/{extensions/memory-log → memory-log}/agent/cost.ts +0 -0
  110. /package/{extensions/memory-log → memory-log}/agent/index.ts +0 -0
  111. /package/{extensions/memory-log → memory-log}/agent/observer/prompt.ts +0 -0
  112. /package/{extensions/memory-log → memory-log}/agent/observer/tool.ts +0 -0
  113. /package/{extensions/memory-log → memory-log}/commands/compact.ts +0 -0
  114. /package/{extensions/memory-log → memory-log}/commands/consolidate.ts +0 -0
  115. /package/{extensions/memory-log → memory-log}/commands/status.ts +0 -0
  116. /package/{extensions/memory-log → memory-log}/debug-log.ts +0 -0
  117. /package/{extensions/memory-log → memory-log}/hooks/consolidator-trigger.ts +0 -0
  118. /package/{extensions/memory-log → memory-log}/ids.ts +0 -0
  119. /package/{extensions/memory-log → memory-log}/ledger/fold.ts +0 -0
  120. /package/{extensions/memory-log → memory-log}/ledger/pool.ts +0 -0
  121. /package/{extensions/memory-log → memory-log}/ledger/progress.ts +0 -0
  122. /package/{extensions/memory-log → memory-log}/ledger/projection.ts +0 -0
  123. /package/{extensions/memory-log → memory-log}/memory/index-render.ts +0 -0
  124. /package/{extensions/memory-log → memory-log}/memory/paths.ts +0 -0
  125. /package/{extensions/memory-log → memory-log}/memory/session.ts +0 -0
  126. /package/{extensions/memory-log → memory-log}/spawn/launch.ts +0 -0
  127. /package/{extensions/memory-log → memory-log}/spawn/runs.ts +0 -0
  128. /package/{extensions/memory-log → memory-log}/tokens.ts +0 -0
  129. /package/{extensions/memory-log → memory-log}/ui/status-controller.ts +0 -0
  130. /package/{extensions/memory-log → memory-log}/ui/timeline.ts +0 -0
  131. /package/{extensions/multi-harness → multi-harness}/adapters.mjs +0 -0
  132. /package/{extensions/multi-harness → multi-harness}/harness-done.mjs +0 -0
  133. /package/{extensions/multi-harness → multi-harness}/index.ts +0 -0
  134. /package/{extensions/multi-harness → multi-harness}/ipc.mjs +0 -0
  135. /package/{extensions/multi-harness → multi-harness}/opencode-plugin.mjs +0 -0
  136. /package/{extensions/multi-harness → multi-harness}/process-runner.mjs +0 -0
  137. /package/{extensions/multi-harness → multi-harness}/turn-hook.mjs +0 -0
  138. /package/{extensions/multi-harness → multi-harness}/worker-bridge.ts +0 -0
  139. /package/{extensions/prompt-snipaste → prompt-snipaste}/index.ts +0 -0
  140. /package/{extensions/prompt-snipaste → prompt-snipaste}/menu.ts +0 -0
  141. /package/{prompts → prompt-snipaste/prompts}/ask-questions.md +0 -0
  142. /package/{prompts → prompt-snipaste/prompts}/delegate-exploration.md +0 -0
  143. /package/{prompts → prompt-snipaste/prompts}/diagnose-report.md +0 -0
  144. /package/{prompts → prompt-snipaste/prompts}/frontend-tech-preset.md +0 -0
  145. /package/{prompts → prompt-snipaste/prompts}/orchestrator-mode.md +0 -0
  146. /package/{prompts → prompt-snipaste/prompts}/session-kickoff.md +0 -0
  147. /package/{prompts → prompt-snipaste/prompts}/verify-not-assume.md +0 -0
  148. /package/{extensions/srp-theme → srp-theme}/index.ts +0 -0
  149. /package/{extensions/subagent → subagent}/bridge.ts +0 -0
  150. /package/{extensions/subagent → subagent}/index.ts +0 -0
  151. /package/{extensions/subagent → subagent}/mux.ts +0 -0
  152. /package/{extensions/subagent → subagent}/session.ts +0 -0
  153. /package/{extensions/subagent → subagent}/ui.ts +0 -0
  154. /package/{extensions/tui-asr → tui-asr}/status.ts +0 -0
  155. /package/{extensions/windows-wheel-fix → windows-wheel-fix}/index.ts +0 -0
@@ -16,4 +16,3 @@ export interface PromptCardData {
16
16
  body: string;
17
17
  }[];
18
18
  }
19
-
@@ -0,0 +1,14 @@
1
+ # screenshot
2
+
3
+ Insert the file path of the most recently modified Windows/WSL system screenshot into Pi's editor via `/screenshot`.
4
+
5
+ Searches standard and localized `Pictures/Screenshots` and OneDrive screenshot directories under `USERPROFILE` on Windows or `/mnt/c/Users` on WSL.
6
+
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ pi install npm:@rol1n/screenshot
12
+ ```
13
+
14
+ Available as a standalone package (not enabled in `@rol1n/pi-kit` by default).
@@ -0,0 +1,18 @@
1
+ # srp-theme
2
+
3
+ Incremental Pi terminal presentation enhancement: adds a `belowEditor` last-prompt preview widget and a real-time streaming TPS (tokens per second) status meter while keeping Pi's native header and footer intact.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pi install npm:@rol1n/srp-theme
9
+ ```
10
+
11
+ Also included in `@rol1n/pi-kit`.
12
+
13
+ ## Commands
14
+
15
+ ```text
16
+ /srp-theme on
17
+ /srp-theme off
18
+ ```
@@ -191,4 +191,3 @@ export function renderTimeAndFinishedLine(
191
191
  const line = truncateToWidth(content, width, "…");
192
192
  return [theme.fg("dim", line)];
193
193
  }
194
-
@@ -439,4 +439,3 @@ export const safeFallbackTheme: Theme = {
439
439
  bg: (_color: string, text: string) => text,
440
440
  bold: (text: string) => text,
441
441
  } as unknown as Theme;
442
-
@@ -32,4 +32,4 @@ Without a usable multiplexer, the worker backend falls back to a background proc
32
32
 
33
33
  ## Other delegation packages
34
34
 
35
- Use [multi-harness](https://github.com/RolinShmily/pi-kit/tree/main/extensions/multi-harness) for Codex, Claude, or OpenCode peers. That package shares multiplexer helpers but provides a separate tool and does not automatically enable this extension's entrypoint. The bundled subagent skill file is reference material and is not registered by the package manifest.
35
+ Use [multi-harness](https://github.com/RolinShmily/pi-kit/tree/main/multi-harness) for Codex, Claude, or OpenCode peers. That package shares multiplexer helpers (`mux.ts`) while exposing a separate tool.
@@ -0,0 +1,21 @@
1
+ # tui-asr
2
+
3
+ Streaming voice dictation for Pi's terminal editor using Aliyun DashScope Paraformer.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pi install npm:@rol1n/tui-asr
9
+ ```
10
+
11
+ Also included in `@rol1n/pi-kit`. Installing this package automatically installs `@rol1n/custom-providers`.
12
+
13
+ ## Commands and shortcuts
14
+
15
+ | Action | Trigger |
16
+ |---|---|
17
+ | Start / stop dictation | `/asr` or `Alt+M` |
18
+ | Cancel dictation | `Alt+N` |
19
+ | Switch ASR model | `/asr model [name]` |
20
+
21
+ Requires `sox` (`rec` on POSIX, `sox -t waveaudio` on Windows) on `PATH` and DashScope credentials via `/login dashscope`, `DASHSCOPE_API_KEY`, or `ALIYUN_API_KEY`.
@@ -1,11 +1,78 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import type { ChildProcessByStdio } from "node:child_process";
2
+ import { spawn, type ChildProcessByStdio } from "node:child_process";
3
3
  import type { Readable } from "node:stream";
4
- import { rmsFromPcm16, spawnRecorder } from "./audio.ts";
4
+ import {
5
+ ALL_ASR_MODELS,
6
+ createAsrProvider,
7
+ ensureCustomProviders,
8
+ type AsrModelDefinition,
9
+ type StreamingAsrProvider,
10
+ } from "../custom-providers/index.ts";
5
11
  import { insertTranscript } from "./input.ts";
6
- import { ALL_ASR_MODELS, createAsrProvider, resolveProviderApiKey, type StreamingAsrProvider } from "./providers.ts";
7
12
  import { createStatusDisplay } from "./status.ts";
8
13
 
14
+ export { ALL_ASR_MODELS, createAsrProvider, ensureCustomProviders, type AsrModelDefinition, type StreamingAsrProvider };
15
+
16
+ export async function resolveProviderApiKey(provider: string, ctx?: ExtensionContext | null): Promise<string> {
17
+ if (ctx?.modelRegistry) {
18
+ const candidates = provider === "dashscope" ? ["dashscope", "aliyun"] : [provider];
19
+ for (const id of candidates) {
20
+ try {
21
+ const key = await ctx.modelRegistry.getApiKeyForProvider(id);
22
+ if (key?.trim()) return key.trim();
23
+ } catch {}
24
+ try {
25
+ const auth = await ctx.modelRegistry.getProviderAuth(id);
26
+ if (auth?.auth?.apiKey?.trim()) return auth.auth.apiKey.trim();
27
+ } catch {}
28
+ }
29
+ }
30
+ const envKey = process.env[`${provider.toUpperCase()}_API_KEY`];
31
+ if (envKey?.trim()) return envKey.trim();
32
+ if (provider === "dashscope") {
33
+ return process.env.ALIYUN_API_KEY?.trim() || "";
34
+ }
35
+ return "";
36
+ }
37
+
38
+ export function rmsFromPcm16(buf: Buffer): number {
39
+ if (buf.length < 2) return 0;
40
+ const sampleCount = Math.floor(buf.length / 2);
41
+ let sumSquares = 0;
42
+ for (let i = 0; i < sampleCount; i++) {
43
+ const sample = buf.readInt16LE(i * 2);
44
+ sumSquares += sample * sample;
45
+ }
46
+ const meanSquare = sumSquares / sampleCount;
47
+ return Math.sqrt(meanSquare);
48
+ }
49
+
50
+ /** Spawn the existing 16 kHz mono PCM recorder for the current platform. */
51
+ export function spawnRecorder(): ChildProcessByStdio<null, Readable, Readable> {
52
+ const commonAudioArgs = [
53
+ "-q",
54
+ "--buffer", "512",
55
+ "-r", "16000",
56
+ "-c", "1",
57
+ "-b", "16",
58
+ "-e", "signed-integer",
59
+ "-t", "raw",
60
+ "-",
61
+ ];
62
+
63
+ let proc: ChildProcessByStdio<null, Readable, Readable>;
64
+ if (process.platform === "win32") {
65
+ proc = spawn("sox", ["-t", "waveaudio", "default", ...commonAudioArgs], { stdio: ["ignore", "pipe", "pipe"] });
66
+ } else {
67
+ try {
68
+ proc = spawn("rec", commonAudioArgs, { stdio: ["ignore", "pipe", "pipe"] });
69
+ } catch {
70
+ proc = spawn("sox", ["-d", ...commonAudioArgs], { stdio: ["ignore", "pipe", "pipe"] });
71
+ }
72
+ }
73
+ return proc;
74
+ }
75
+
9
76
  type State = "idle" | "recording" | "stopping";
10
77
 
11
78
  /** Keep recording state, cancellation guards, and cleanup in one session-local controller. */
@@ -19,8 +19,7 @@
19
19
 
20
20
  import { type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
21
21
  import type { AutocompleteItem } from "@earendil-works/pi-tui";
22
- import { ALL_ASR_MODELS, resolveProviderApiKey, type AsrModelDefinition } from "./providers.ts";
23
- import { createDictationController } from "./dictation.ts";
22
+ import { ALL_ASR_MODELS, createDictationController, ensureCustomProviders, resolveProviderApiKey, type AsrModelDefinition } from "./dictation.ts";
24
23
  import { SearchableAsrModelSelectorComponent } from "./model-selector.ts";
25
24
 
26
25
  /** Default startup ASR model ID */
@@ -31,6 +30,7 @@ const TOGGLE_SHORTCUTS = ["alt+m"];
31
30
  const CANCEL_SHORTCUTS = ["alt+n"];
32
31
 
33
32
  export default function (pi: ExtensionAPI) {
33
+ ensureCustomProviders(pi);
34
34
  let activeModel = DEFAULT_ASR_MODEL;
35
35
 
36
36
  // TUI state
@@ -1,5 +1,34 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { copyToClipboard } from "./clipboard.ts";
2
+ import { spawn, type ChildProcessByStdio } from "node:child_process";
3
+
4
+ export function copyToClipboard(text: string): boolean {
5
+ try {
6
+ const isWsl =
7
+ process.platform === "linux" &&
8
+ typeof process.env.WSL_DISTRO_NAME === "string";
9
+
10
+ let proc: ChildProcessByStdio<any, any, any>;
11
+ if (isWsl) {
12
+ proc = spawn("clip.exe", [], { stdio: ["pipe", "ignore", "ignore"] });
13
+ } else if (process.platform === "win32") {
14
+ proc = spawn("clip", [], { stdio: ["pipe", "ignore", "ignore"] });
15
+ } else if (process.platform === "darwin") {
16
+ proc = spawn("pbcopy", [], { stdio: ["pipe", "ignore", "ignore"] });
17
+ } else {
18
+ try {
19
+ proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] });
20
+ } catch {
21
+ proc = spawn("xclip", ["-selection", "clipboard"], { stdio: ["pipe", "ignore", "ignore"] });
22
+ }
23
+ }
24
+
25
+ proc.stdin.write(text);
26
+ proc.stdin.end();
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
3
32
 
4
33
  interface EditorLike {
5
34
  getText(): string;
@@ -1,5 +1,5 @@
1
1
  import { Input, fuzzyFilter, getKeybindings } from "@earendil-works/pi-tui";
2
- import type { AsrModelDefinition } from "./providers.ts";
2
+ import type { AsrModelDefinition } from "./dictation.ts";
3
3
 
4
4
  export class SearchableAsrModelSelectorComponent {
5
5
  private searchInput = new Input();
@@ -0,0 +1,19 @@
1
+ # web-reach
2
+
3
+ Keyless web search (`web_search` via Exa MCP) and multi-tier page extraction (`web_fetch` supporting Next.js RSC, semantic HTML-to-Markdown, optional `unpdf` PDF extraction, and Jina Reader fallback).
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pi install npm:@rol1n/web-reach
9
+ ```
10
+
11
+ Available as a standalone package (not enabled in `@rol1n/pi-kit` by default).
12
+
13
+ ## Configuration
14
+
15
+ | Environment variable | Default |
16
+ |---|---|
17
+ | `SRP_WEB_SEARCH_TIMEOUT_MS` | `60000` |
18
+ | `SRP_WEB_FETCH_TIMEOUT_MS` | `20000` |
19
+ | `SRP_WEB_FETCH_MAX_BYTES` | `4194304` (4 MiB) |
@@ -0,0 +1,12 @@
1
+ # windows-wheel-fix
2
+
3
+ Restores mouse-wheel reporting for Pi under Windows ConPTY / Zellij ([earendil-works/pi#9656](https://github.com/earendil-works/pi/issues/9656)) by re-emitting mouse DECSET sequences once raw mode is active. No-op on non-Windows platforms.
4
+
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ pi install npm:@rol1n/windows-wheel-fix
10
+ ```
11
+
12
+ Also included in `@rol1n/pi-kit`.
@@ -1,32 +0,0 @@
1
- # ask-u-questions
2
-
3
- Interactive single-choice, multiple-choice, and free-text questions for Pi. Registers the `ask_user_question` tool and serializes dialogs through a shared UI lock.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/ask-u-questions
9
- ```
10
-
11
- Also included in `@rol1n/pi-kit`. Enable one copy of the extension.
12
-
13
- ## Use
14
-
15
- The agent calls the tool with a question and optional choices:
16
-
17
- ```json
18
- {
19
- "question": "Which output format should I use?",
20
- "options": [
21
- { "label": "Markdown (Recommended)", "value": "markdown" },
22
- { "label": "JSON", "value": "json" }
23
- ],
24
- "multiSelect": false
25
- }
26
- ```
27
-
28
- Omit `options` or supply an empty array for free-text input. Set `multiSelect` only when several choices are expected. `details` adds supporting context; an Other choice is supplied automatically for choice questions.
29
-
30
- ## Requirements and behavior
31
-
32
- Use an interactive Pi terminal session for dialogs. Results distinguish answered, cancelled, and unavailable states, so a cancelled dialog is not an approval. No API credentials or package-specific settings are required.
@@ -1,53 +0,0 @@
1
- import type { AskAnswer, AskOption } from "./types.ts";
2
-
3
- export function normalizeOptions(
4
- options: Array<{ label: string; value?: string; description?: string }> | undefined,
5
- ): AskOption[] {
6
- return (options || [])
7
- .map((option) => ({
8
- label: option.label.trim(),
9
- value: option.value?.trim() || option.label.trim(),
10
- description: option.description?.trim() || undefined,
11
- }))
12
- .filter((option) => option.label.length > 0);
13
- }
14
-
15
- export function isChineseContext(
16
- question: string,
17
- options?: Array<{ label?: string; description?: string }>,
18
- context?: string,
19
- ): boolean {
20
- if (/[\u4e00-\u9fa5]/.test(question)) return true;
21
- if (context && /[\u4e00-\u9fa5]/.test(context)) return true;
22
- if (options && options.some((o) => (o.label && /[\u4e00-\u9fa5]/.test(o.label)) || (o.description && /[\u4e00-\u9fa5]/.test(o.description)))) {
23
- return true;
24
- }
25
- return false;
26
- }
27
-
28
- export function getOtherLabel(options: AskOption[], isZh = false): string {
29
- if (isZh) {
30
- return options.some((option) => option.label === "其他(自定义输入)")
31
- ? "自定义输入"
32
- : "其他(自定义输入)";
33
- }
34
- return options.some((option) => option.label === "Other (custom input)")
35
- ? "Custom input"
36
- : "Other (custom input)";
37
- }
38
-
39
- function answerSortRank(answer: AskAnswer): number {
40
- switch (answer.type) {
41
- case "option":
42
- return answer.index;
43
- case "other":
44
- return Number.MAX_SAFE_INTEGER - 1;
45
- case "text":
46
- return Number.MAX_SAFE_INTEGER;
47
- }
48
- }
49
-
50
- export function sortAnswers(answers: AskAnswer[]): AskAnswer[] {
51
- return [...answers].sort((a, b) => answerSortRank(a) - answerSortRank(b));
52
- }
53
-
@@ -1,83 +0,0 @@
1
- import type { AskAnswer, AskUserQuestionMode, AskUserQuestionResultDetails, AskUserQuestionStatus } from "./types.ts";
2
- import { isChineseContext } from "./answers.ts";
3
-
4
- function formatAnswerForModel(answer: AskAnswer): string {
5
- switch (answer.type) {
6
- case "text":
7
- return answer.label;
8
- case "other":
9
- return `Other: ${answer.label}`;
10
- case "option":
11
- return `${answer.index}. ${answer.label}`;
12
- }
13
- }
14
-
15
- function buildStructuredResult(
16
- status: AskUserQuestionStatus,
17
- question: string,
18
- mode: AskUserQuestionMode,
19
- answers: AskAnswer[],
20
- context?: string,
21
- message?: string,
22
- ): AskUserQuestionResultDetails {
23
- return {
24
- status,
25
- question,
26
- context,
27
- mode,
28
- answers,
29
- message,
30
- };
31
- }
32
-
33
- export function cancelledResult(question: string, mode: AskUserQuestionMode, context?: string) {
34
- const isZh = isChineseContext(question, undefined, context);
35
- const message = isZh ? "用户取消了本次提问" : "User cancelled the question.";
36
- return {
37
- content: [{ type: "text" as const, text: message }],
38
- details: buildStructuredResult("cancelled", question, mode, [], context, message),
39
- };
40
- }
41
-
42
- export function unavailableResult(question: string, mode: AskUserQuestionMode, message: string, context?: string) {
43
- return {
44
- content: [{ type: "text" as const, text: message }],
45
- details: buildStructuredResult("unavailable", question, mode, [], context, message),
46
- };
47
- }
48
-
49
- export function buildResult(
50
- question: string,
51
- context: string | undefined,
52
- mode: AskUserQuestionMode,
53
- answers: AskAnswer[],
54
- ) {
55
- const isZh = isChineseContext(question, answers, context);
56
- let text: string;
57
- if (mode === "text") {
58
- const answer = answers[0];
59
- if (isZh) {
60
- text = answer.label.trim().length > 0
61
- ? `用户输入回答: ${answer.label}`
62
- : "用户提交了空回答";
63
- } else {
64
- text = answer.label.trim().length > 0
65
- ? `User answered: ${answer.label}`
66
- : "User submitted an empty answer";
67
- }
68
- } else if (mode === "single-select") {
69
- text = isZh
70
- ? `用户选择项: ${formatAnswerForModel(answers[0])}`
71
- : `User selected: ${formatAnswerForModel(answers[0])}`;
72
- } else {
73
- text = isZh
74
- ? `用户选择多项:\n${answers.map((answer) => `- ${formatAnswerForModel(answer)}`).join("\n")}`
75
- : `User selected multiple:\n${answers.map((answer) => `- ${formatAnswerForModel(answer)}`).join("\n")}`;
76
- }
77
-
78
- return {
79
- content: [{ type: "text" as const, text }],
80
- details: buildStructuredResult("answered", question, mode, answers, context),
81
- };
82
- }
83
-
@@ -1,17 +0,0 @@
1
- import { Type } from "typebox";
2
-
3
- const OptionSchema = Type.Object({
4
- label: Type.String({ description: 'Option label. Put a recommended option first and append "(Recommended)".' }),
5
- value: Type.Optional(Type.String({ description: "Machine-readable value; defaults to the label." })),
6
- description: Type.Optional(Type.String({ description: "Additional context shown below the option." })),
7
- });
8
-
9
- export const AskUserQuestionParams = Type.Object({
10
- question: Type.String({ description: "The question to ask. Ask only one question per call." }),
11
- details: Type.Optional(Type.String({ description: "Background or instructions shown below the question." })),
12
- options: Type.Optional(Type.Array(OptionSchema, {
13
- description: "Choices for selection. Omit or pass an empty array for free-text input. An Other choice is always available.",
14
- })),
15
- multiSelect: Type.Optional(Type.Boolean({ description: "Allow selecting multiple options when true." })),
16
- });
17
-
@@ -1,45 +0,0 @@
1
- export interface AskOption {
2
- label: string;
3
- value: string;
4
- description?: string;
5
- }
6
-
7
- export interface DisplayOption extends AskOption {
8
- id: string;
9
- index?: number;
10
- isOther?: boolean;
11
- isSubmit?: boolean;
12
- }
13
-
14
- export interface TextAnswer {
15
- type: "text";
16
- label: string;
17
- value: string;
18
- }
19
-
20
- export interface OptionAnswer {
21
- type: "option";
22
- label: string;
23
- value: string;
24
- index: number;
25
- }
26
-
27
- export interface OtherAnswer {
28
- type: "other";
29
- label: string;
30
- value: string;
31
- }
32
-
33
- export type AskAnswer = TextAnswer | OptionAnswer | OtherAnswer;
34
- export type AskUserQuestionStatus = "answered" | "cancelled" | "unavailable";
35
- export type AskUserQuestionMode = "text" | "single-select" | "multi-select";
36
-
37
- export interface AskUserQuestionResultDetails {
38
- status: AskUserQuestionStatus;
39
- question: string;
40
- context?: string;
41
- mode: AskUserQuestionMode;
42
- answers: AskAnswer[];
43
- message?: string;
44
- }
45
-
@@ -1,24 +0,0 @@
1
- const SHARED_UI_LOCK_KEY = "__piSharedUiLock";
2
- function getSharedUiLock(): { withLock<T>(fn: () => T | Promise<T>): Promise<T> } {
3
- const g = globalThis as any;
4
- if (!g[SHARED_UI_LOCK_KEY]) {
5
- let chain: Promise<void> = Promise.resolve();
6
- g[SHARED_UI_LOCK_KEY] = {
7
- withLock<T>(fn: () => T | Promise<T>): Promise<T> {
8
- const prev = chain;
9
- let release: () => void;
10
- chain = new Promise<void>((r) => {
11
- release = r;
12
- });
13
- return prev.then(fn).finally(() => release!());
14
- },
15
- };
16
- }
17
- return g[SHARED_UI_LOCK_KEY];
18
- }
19
- const sharedUiLock = getSharedUiLock();
20
-
21
- export function withUILock<T>(fn: () => Promise<T>): Promise<T> {
22
- return sharedUiLock.withLock(fn);
23
- }
24
-
@@ -1,28 +0,0 @@
1
- # continue-signal
2
-
3
- Resume an idle Pi agent with a visible continuation signal.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/continue-signal
9
- ```
10
-
11
- Also included in `@rol1n/pi-kit`.
12
-
13
- ## Commands
14
-
15
- | Action | Command or shortcut |
16
- |---|---|
17
- | Resume unfinished work | `/continue` or Alt+C |
18
- | Resume with a specific instruction | `/continue Review the remaining test failures` |
19
-
20
- An explicit command argument takes priority. Without one, the extension uses and clears the current editor draft. If the draft is empty, it asks the agent to review the conversation and resume unfinished work. It leaves an already-running agent alone.
21
-
22
- ## Configuration
23
-
24
- No settings or credentials are needed. The shortcut requires a terminal that forwards Alt+C. This extension starts a new agent turn; it does not reconstruct a lost session or restart a crashed process.
25
-
26
- ## Source layout
27
-
28
- `index.ts` registers the command and shortcut and sends continuation messages. `renderer.ts` owns the visible signal card.
@@ -1,21 +0,0 @@
1
- import type { MessageRenderer } from "@earendil-works/pi-coding-agent";
2
- import { Box, Text } from "@earendil-works/pi-tui";
3
-
4
- export const renderContinueSignal: MessageRenderer<{ timestamp?: number }> = (
5
- message,
6
- { expanded, outputPad },
7
- theme,
8
- ) => {
9
- const time = new Date(message.details?.timestamp ?? Date.now()).toLocaleTimeString();
10
- const title = `${theme.bold(theme.fg("accent", "⚡ LOOP RESUME SIGNAL"))} ${theme.fg("muted", `[${time}]`)}`;
11
-
12
- const box = new Box(outputPad, 1, (text) => theme.bg("customMessageBg", text));
13
- box.addChild(new Text(title, 0, 0));
14
- box.addChild(new Text(theme.fg("text", "Re-linking conversation context & resuming agent loop..."), 0, 0));
15
-
16
- if (expanded && message.content) {
17
- box.addChild(new Text(theme.fg("dim", `Prompt: ${message.content}`), 0, 0));
18
- }
19
-
20
- return box;
21
- };
@@ -1,28 +0,0 @@
1
- # custom-providers
2
-
3
- Register additional model providers through Pi's native model registry and login interface. Chat, image, classifier, and ASR definitions are organized by model type.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/custom-providers
9
- ```
10
-
11
- Also included in `@rol1n/pi-kit`. Open Pi's model selector after provider registration and authenticate only the providers you use.
12
-
13
- ## Provider configuration
14
-
15
- | Provider | Configuration |
16
- |---|---|
17
- | Magpie | `MAGPIE_BASE_URL` selects the gateway; `MAGPIE_GATEWAY_KEY` or `/login magpie` supplies its gateway key |
18
- | SHUAI API | `/login shuaiapi`; chat catalog and endpoint are declared in `chat/` |
19
- | BigModel | `/login bigmodel`; chat catalog and endpoint are declared in `chat/` |
20
- | DashScope | `/login dashscope`; custom chat/image definitions and ASR model helpers |
21
-
22
- Magpie defaults to `http://127.0.0.1:3425/v1`. Set `MAGPIE_BASE_URL` before starting Pi when your gateway runs elsewhere. Model discovery preserves supported native protocols rather than treating every discovered model as OpenAI chat completions. System One classification delegates to the host Pi implementation.
23
-
24
- `DASHSCOPE_IMAGE_URL` can override the DashScope image API base URL; its default is `https://dashscope.aliyuncs.com/api/v1`. OpenRouter's image catalog belongs to Pi's built-in provider and is not registered again here. The dedicated `classifier/` catalog is currently reserved; Magpie discovers classifier models dynamically.
25
-
26
- ## Related packages
27
-
28
- [image-generate](https://github.com/RolinShmily/pi-kit/tree/main/extensions/image-generate) provides the image-generation tool. [tui-asr](https://github.com/RolinShmily/pi-kit/tree/main/extensions/tui-asr) provides dictation. Their shared helper files do not independently register this provider extension. Store credentials through Pi or environment variables rather than editing source files.
@@ -1,29 +0,0 @@
1
- # image-generate
2
-
3
- Generate images through OpenRouter or DashScope and save them to the current project. Registers `image_generate` and the `/image-generate` model selector.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/image-generate
9
- ```
10
-
11
- Also included in `@rol1n/pi-kit`. The extension entrypoint is `extensions/image-generate/index.ts`; model lookup, the selector UI, and image persistence live in `models.ts`, `model-selector.ts`, and `files.ts` beside it.
12
-
13
- ## Use
14
-
15
- `/image-generate model` opens the searchable selector; `/image-generate model qwen-image-3.0-pro` selects a named model for the current session.
16
-
17
- ```json
18
- {
19
- "prompt": "A clear technical illustration of a terminal workflow",
20
- "aspectRatio": "16:9",
21
- "model": "google/gemini-3.1-flash-image"
22
- }
23
- ```
24
-
25
- The `image_generate` tool also accepts `image` as a local path or remote reference URL where the selected provider supports reference images. The default model is `google/gemini-3.1-flash-image`. Outputs are saved under `.pi/generated-images/` relative to the working directory and the tool returns the saved path.
26
-
27
- ## Authentication and providers
28
-
29
- Use `/login openrouter` or `/login dashscope`, or the corresponding `OPENROUTER_API_KEY` / `DASHSCOPE_API_KEY` environment variable. DashScope registry entries are supplied by [custom-providers](https://github.com/RolinShmily/pi-kit/tree/main/extensions/custom-providers). Helper adapters ship with this package, but copying helpers does not register that extension. Generation requests use the selected provider's account and pricing.
@@ -1,19 +0,0 @@
1
- /** Image catalog lookup and provider selection. */
2
-
3
- export const DEFAULT_MODEL = "google/gemini-3.1-flash-image";
4
-
5
- /** Image models known to Pi: OpenRouter's built-in catalog plus our DashScope registration. */
6
- export function registryImageModels(ctx: { modelRegistry: { getModelsOfType(type: string): readonly any[] } }) {
7
- return ctx.modelRegistry.getModelsOfType("image") as readonly { id: string; name?: string; provider: string }[];
8
- }
9
-
10
- /** Resolve which provider owns a model id, preferring the Pi registry over heuristics. */
11
- export function resolveProviderId(
12
- ctx: { modelRegistry: { getModelsOfType(type: string): readonly any[] } },
13
- modelId: string,
14
- ): string {
15
- const lower = modelId.toLowerCase();
16
- const known = registryImageModels(ctx).find((m) => m.id.toLowerCase() === lower);
17
- if (known) return known.provider;
18
- return lower.startsWith("qwen-image") ? "dashscope" : "openrouter";
19
- }