@rol1n/pi-kit 0.1.0 → 0.1.1

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 (149) hide show
  1. package/README.md +33 -58
  2. package/ask-u-questions/README.md +26 -0
  3. package/{extensions/ask-u-questions → ask-u-questions}/dialog-ui.ts +23 -0
  4. package/{extensions/ask-u-questions → ask-u-questions}/index.ts +9 -5
  5. package/{extensions/ask-u-questions → ask-u-questions}/multi-choice.ts +1 -3
  6. package/{extensions/ask-u-questions → ask-u-questions}/single-choice.ts +1 -3
  7. package/{extensions/ask-u-questions → ask-u-questions}/tool-renderers.ts +1 -3
  8. package/ask-u-questions/types.ts +192 -0
  9. package/continue-signal/README.md +20 -0
  10. package/{extensions/continue-signal → continue-signal}/index.ts +21 -3
  11. package/custom-providers/README.md +20 -0
  12. package/{extensions/custom-providers → custom-providers}/index.ts +16 -2
  13. package/image-generate/README.md +25 -0
  14. package/{extensions/image-generate → image-generate}/index.ts +22 -2
  15. package/{extensions/image-generate → image-generate}/model-selector.ts +1 -1
  16. package/model-vision/README.md +22 -0
  17. package/{extensions/multi-harness → multi-harness}/README.md +3 -3
  18. package/oai-subquota/README.md +22 -0
  19. package/{extensions/oai-subquota → oai-subquota}/index.ts +16 -6
  20. package/{extensions/oai-subquota → oai-subquota}/snapshot.ts +127 -1
  21. package/{extensions/oai-subquota → oai-subquota}/ui.ts +1 -1
  22. package/package.json +45 -46
  23. package/prompt-snipaste/README.md +27 -0
  24. package/{extensions/prompt-snipaste → prompt-snipaste}/renderers.ts +0 -1
  25. package/{extensions/prompt-snipaste → prompt-snipaste}/snippets.ts +5 -2
  26. package/{extensions/prompt-snipaste → prompt-snipaste}/types.ts +0 -1
  27. package/screenshot/README.md +14 -0
  28. package/srp-theme/README.md +18 -0
  29. package/{extensions/srp-theme → srp-theme}/footer.ts +0 -1
  30. package/{extensions/srp-theme → srp-theme}/tps-meter.ts +0 -1
  31. package/{extensions/subagent → subagent}/README.md +1 -1
  32. package/tui-asr/README.md +21 -0
  33. package/{extensions/tui-asr → tui-asr}/dictation.ts +70 -3
  34. package/{extensions/tui-asr → tui-asr}/index.ts +2 -2
  35. package/{extensions/tui-asr → tui-asr}/input.ts +30 -1
  36. package/{extensions/tui-asr → tui-asr}/model-selector.ts +1 -1
  37. package/web-reach/README.md +19 -0
  38. package/{extensions/web-reach → web-reach}/page.ts +84 -2
  39. package/windows-wheel-fix/README.md +12 -0
  40. package/extensions/ask-u-questions/README.md +0 -32
  41. package/extensions/ask-u-questions/answers.ts +0 -53
  42. package/extensions/ask-u-questions/results.ts +0 -83
  43. package/extensions/ask-u-questions/schema.ts +0 -17
  44. package/extensions/ask-u-questions/types.ts +0 -45
  45. package/extensions/ask-u-questions/ui-lock.ts +0 -24
  46. package/extensions/continue-signal/README.md +0 -28
  47. package/extensions/continue-signal/renderer.ts +0 -21
  48. package/extensions/custom-providers/README.md +0 -28
  49. package/extensions/image-generate/README.md +0 -29
  50. package/extensions/image-generate/models.ts +0 -19
  51. package/extensions/model-vision/README.md +0 -28
  52. package/extensions/oai-subquota/README.md +0 -30
  53. package/extensions/oai-subquota/api.ts +0 -27
  54. package/extensions/oai-subquota/auth.ts +0 -34
  55. package/extensions/oai-subquota/cache.ts +0 -30
  56. package/extensions/oai-subquota/types.ts +0 -40
  57. package/extensions/prompt-snipaste/README.md +0 -33
  58. package/extensions/screenshot/README.md +0 -25
  59. package/extensions/srp-theme/README.md +0 -24
  60. package/extensions/subagent/skills/subagent/SKILL.md +0 -70
  61. package/extensions/tui-asr/README.md +0 -28
  62. package/extensions/tui-asr/audio.ts +0 -40
  63. package/extensions/tui-asr/clipboard.ts +0 -30
  64. package/extensions/tui-asr/providers.ts +0 -25
  65. package/extensions/web-reach/README.md +0 -37
  66. package/extensions/web-reach/pdf.ts +0 -42
  67. package/extensions/web-reach/reader.ts +0 -42
  68. package/extensions/windows-wheel-fix/README.md +0 -21
  69. /package/{extensions/custom-providers → custom-providers}/asr/handlers/dashscope.ts +0 -0
  70. /package/{extensions/custom-providers → custom-providers}/asr/index.ts +0 -0
  71. /package/{extensions/custom-providers → custom-providers}/asr/models.ts +0 -0
  72. /package/{extensions/custom-providers → custom-providers}/asr/types.ts +0 -0
  73. /package/{extensions/custom-providers → custom-providers}/chat/index.ts +0 -0
  74. /package/{extensions/custom-providers → custom-providers}/chat/magpie.ts +0 -0
  75. /package/{extensions/custom-providers → custom-providers}/chat/models.ts +0 -0
  76. /package/{extensions/custom-providers → custom-providers}/classifier/index.ts +0 -0
  77. /package/{extensions/custom-providers → custom-providers}/classifier/models.ts +0 -0
  78. /package/{extensions/custom-providers → custom-providers}/image/handlers/dashscope.ts +0 -0
  79. /package/{extensions/custom-providers → custom-providers}/image/handlers/openrouter.ts +0 -0
  80. /package/{extensions/custom-providers → custom-providers}/image/index.ts +0 -0
  81. /package/{extensions/custom-providers → custom-providers}/image/models.ts +0 -0
  82. /package/{extensions/custom-providers → custom-providers}/image/types.ts +0 -0
  83. /package/{extensions/custom-providers → custom-providers}/types.ts +0 -0
  84. /package/{extensions/image-generate → image-generate}/files.ts +0 -0
  85. /package/{extensions/memory-log → memory-log}/README.md +0 -0
  86. /package/{extensions/memory-log → memory-log}/agent/consolidator/prompt.ts +0 -0
  87. /package/{extensions/memory-log → memory-log}/agent/consolidator/tools.ts +0 -0
  88. /package/{extensions/memory-log → memory-log}/agent/cost.ts +0 -0
  89. /package/{extensions/memory-log → memory-log}/agent/index.ts +0 -0
  90. /package/{extensions/memory-log → memory-log}/agent/observer/prompt.ts +0 -0
  91. /package/{extensions/memory-log → memory-log}/agent/observer/tool.ts +0 -0
  92. /package/{extensions/memory-log → memory-log}/commands/compact.ts +0 -0
  93. /package/{extensions/memory-log → memory-log}/commands/consolidate.ts +0 -0
  94. /package/{extensions/memory-log → memory-log}/commands/status.ts +0 -0
  95. /package/{extensions/memory-log → memory-log}/config.ts +0 -0
  96. /package/{extensions/memory-log → memory-log}/debug-log.ts +0 -0
  97. /package/{extensions/memory-log → memory-log}/hooks/compaction-hook.ts +0 -0
  98. /package/{extensions/memory-log → memory-log}/hooks/compaction-trigger.ts +0 -0
  99. /package/{extensions/memory-log → memory-log}/hooks/consolidator-trigger.ts +0 -0
  100. /package/{extensions/memory-log → memory-log}/hooks/observer-trigger.ts +0 -0
  101. /package/{extensions/memory-log → memory-log}/ids.ts +0 -0
  102. /package/{extensions/memory-log → memory-log}/index.ts +0 -0
  103. /package/{extensions/memory-log → memory-log}/ledger/fold.ts +0 -0
  104. /package/{extensions/memory-log → memory-log}/ledger/index.ts +0 -0
  105. /package/{extensions/memory-log → memory-log}/ledger/pool.ts +0 -0
  106. /package/{extensions/memory-log → memory-log}/ledger/progress.ts +0 -0
  107. /package/{extensions/memory-log → memory-log}/ledger/projection.ts +0 -0
  108. /package/{extensions/memory-log → memory-log}/ledger/render.ts +0 -0
  109. /package/{extensions/memory-log → memory-log}/ledger/serialize.ts +0 -0
  110. /package/{extensions/memory-log → memory-log}/ledger/types.ts +0 -0
  111. /package/{extensions/memory-log → memory-log}/memory/index-render.ts +0 -0
  112. /package/{extensions/memory-log → memory-log}/memory/paths.ts +0 -0
  113. /package/{extensions/memory-log → memory-log}/memory/session.ts +0 -0
  114. /package/{extensions/memory-log → memory-log}/runtime.ts +0 -0
  115. /package/{extensions/memory-log → memory-log}/spawn/launch.ts +0 -0
  116. /package/{extensions/memory-log → memory-log}/spawn/runs.ts +0 -0
  117. /package/{extensions/memory-log → memory-log}/tokens.ts +0 -0
  118. /package/{extensions/memory-log → memory-log}/ui/status-controller.ts +0 -0
  119. /package/{extensions/memory-log → memory-log}/ui/timeline.ts +0 -0
  120. /package/{extensions/multi-harness → multi-harness}/adapters.mjs +0 -0
  121. /package/{extensions/multi-harness → multi-harness}/harness-done.mjs +0 -0
  122. /package/{extensions/multi-harness → multi-harness}/index.ts +0 -0
  123. /package/{extensions/multi-harness → multi-harness}/ipc.mjs +0 -0
  124. /package/{extensions/multi-harness → multi-harness}/opencode-plugin.mjs +0 -0
  125. /package/{extensions/multi-harness → multi-harness}/process-runner.mjs +0 -0
  126. /package/{extensions/multi-harness → multi-harness}/turn-hook.mjs +0 -0
  127. /package/{extensions/multi-harness → multi-harness}/worker-bridge.ts +0 -0
  128. /package/{extensions/prompt-snipaste → prompt-snipaste}/index.ts +0 -0
  129. /package/{extensions/prompt-snipaste → prompt-snipaste}/menu.ts +0 -0
  130. /package/{prompts → prompt-snipaste/prompts}/ask-questions.md +0 -0
  131. /package/{prompts → prompt-snipaste/prompts}/delegate-exploration.md +0 -0
  132. /package/{prompts → prompt-snipaste/prompts}/diagnose-report.md +0 -0
  133. /package/{prompts → prompt-snipaste/prompts}/frontend-tech-preset.md +0 -0
  134. /package/{prompts → prompt-snipaste/prompts}/orchestrator-mode.md +0 -0
  135. /package/{prompts → prompt-snipaste/prompts}/session-kickoff.md +0 -0
  136. /package/{prompts → prompt-snipaste/prompts}/verify-not-assume.md +0 -0
  137. /package/{extensions/srp-theme → srp-theme}/index.ts +0 -0
  138. /package/{extensions/subagent → subagent}/bridge.ts +0 -0
  139. /package/{extensions/subagent → subagent}/index.ts +0 -0
  140. /package/{extensions/subagent → subagent}/mux.ts +0 -0
  141. /package/{extensions/subagent → subagent}/session.ts +0 -0
  142. /package/{extensions/subagent → subagent}/ui.ts +0 -0
  143. /package/{extensions/tui-asr → tui-asr}/status.ts +0 -0
  144. /package/{extensions/web-reach → web-reach}/html.ts +0 -0
  145. /package/{extensions/web-reach → web-reach}/index.ts +0 -0
  146. /package/{extensions/web-reach → web-reach}/rsc.ts +0 -0
  147. /package/{extensions/web-reach → web-reach}/search.ts +0 -0
  148. /package/{extensions/web-reach → web-reach}/transport.ts +0 -0
  149. /package/{extensions/windows-wheel-fix → windows-wheel-fix}/index.ts +0 -0
@@ -1,8 +1,90 @@
1
1
  import { DEFAULT_USER_AGENT, envNum, truncate, withTimeout } from "./transport.ts";
2
2
  import { extractRSCContent } from "./rsc.ts";
3
3
  import { extractTitleFromHtml, htmlToMarkdown, isLikelyJSRendered } from "./html.ts";
4
- import { fetchViaJinaReader } from "./reader.ts";
5
- import { extractPdfBuffer, isPdfUrl } from "./pdf.ts";
4
+
5
+ const JINA_READER_BASE = "https://r.jina.ai/";
6
+
7
+ export async function fetchViaJinaReader(
8
+ url: string,
9
+ signal?: AbortSignal,
10
+ ): Promise<{ text: string; title: string } | null> {
11
+ const { ac, done } = withTimeout(30_000, "Jina Reader timed out", signal);
12
+ try {
13
+ const res = await fetch(`${JINA_READER_BASE}${url}`, {
14
+ headers: {
15
+ Accept: "text/markdown",
16
+ "X-No-Cache": "true",
17
+ "User-Agent": DEFAULT_USER_AGENT,
18
+ },
19
+ signal: ac.signal,
20
+ });
21
+ if (!res.ok) return null;
22
+
23
+ const raw = await res.text();
24
+ const contentIndex = raw.indexOf("Markdown Content:");
25
+ const markdown = contentIndex >= 0 ? raw.slice(contentIndex + 17).trim() : raw.trim();
26
+
27
+ if (
28
+ markdown.length < 80 ||
29
+ markdown.startsWith("Loading...") ||
30
+ markdown.startsWith("Please enable JavaScript")
31
+ ) {
32
+ return null;
33
+ }
34
+
35
+ const titleMatch = markdown.match(/^#+\s+(.+)$/m);
36
+ const title = titleMatch?.[1]?.trim() || "";
37
+
38
+ return { text: markdown, title };
39
+ } catch {
40
+ return null;
41
+ } finally {
42
+ done();
43
+ }
44
+ }
45
+
46
+ export function isPdfUrl(url: string, contentType?: string): boolean {
47
+ if (contentType?.includes("application/pdf")) return true;
48
+ try {
49
+ return new URL(url).pathname.toLowerCase().endsWith(".pdf");
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ export async function extractPdfBuffer(
56
+ buffer: ArrayBuffer,
57
+ url: string,
58
+ ): Promise<{ text: string; title: string }> {
59
+ try {
60
+ const { getDocumentProxy } = await import("unpdf");
61
+ const pdf = await getDocumentProxy(new Uint8Array(buffer));
62
+ const maxPages = Math.min(pdf.numPages, 80);
63
+ const pages: string[] = [];
64
+
65
+ for (let i = 1; i <= maxPages; i++) {
66
+ const page = await pdf.getPage(i);
67
+ const textContent = await page.getTextContent();
68
+ const pageText = textContent.items
69
+ .map((item: unknown) => (item as { str?: string }).str || "")
70
+ .join(" ")
71
+ .replace(/\s+/g, " ")
72
+ .trim();
73
+ if (pageText) pages.push(`--- Page ${i} ---\n${pageText}`);
74
+ }
75
+
76
+ const title = new URL(url).pathname.split("/").pop() || "PDF Document";
77
+ const text = `# ${title}\n\nPages: ${pdf.numPages}\n\n${pages.join("\n\n")}`;
78
+ return { text, title };
79
+ } catch {
80
+ // Fallback plain text extraction
81
+ const text = Buffer.from(buffer).toString("latin1").replace(/[^\x20-\x7E\n\r\t]/g, " ");
82
+ return {
83
+ text: `# PDF Document\n\n${text.slice(0, 8000)}`,
84
+ title: "PDF Document",
85
+ };
86
+ }
87
+ }
6
88
 
7
89
  const MIN_USEFUL_CONTENT_LENGTH = 300;
8
90
 
@@ -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
- }
@@ -1,28 +0,0 @@
1
- # model-vision
2
-
3
- Analyze a local image or public image URL with a vision model. Registers the `model_vision` tool for OCR, UI inspection, and image questions.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/model-vision
9
- ```
10
-
11
- Available independently; it is not enabled in the aggregate package by default. The extension entrypoint is `extensions/model-vision/index.ts`; `analysis.ts` coordinates image loading (`image.ts`), network validation (`network.ts`), and model requests (`requests.ts`).
12
-
13
- ## Use
14
-
15
- ```json
16
- {
17
- "image": "./screenshots/error.png",
18
- "prompt": "Read the error message and describe the relevant UI."
19
- }
20
- ```
21
-
22
- Supported image formats are PNG, JPEG, WebP, GIF, and BMP. Local paths resolve from the working directory. Remote images must use public HTTP(S) addresses; private-network targets are rejected. Images are limited to 10 MiB and requests have a 90-second timeout.
23
-
24
- ## Model configuration
25
-
26
- The current fallback chain is declared in `DEFAULT_VISION_MODELS` in `extensions/model-vision/models.ts`: Antigravity `gemini-3.8-flash` through Pi's native API, followed by SHUAI API `gpt-6-luna` through OpenAI completions. Change that source list to choose a different chain; there is currently no settings key or tool argument for choosing the vision model.
27
-
28
- Install and authenticate the provider integrations you select. Antigravity is an external integration; SHUAI registration is available in [custom-providers](https://github.com/RolinShmily/pi-kit/tree/main/extensions/custom-providers). The tool returns extracted text and the provider/model used.
@@ -1,30 +0,0 @@
1
- # oai-subquota
2
-
3
- Display OpenAI Codex subscription quota, reset windows, and a compact status line using the authenticated `openai-codex` provider.
4
-
5
- ## Install
6
-
7
- ```sh
8
- pi install npm:@rol1n/oai-subquota
9
- ```
10
-
11
- Also included in `@rol1n/pi-kit`.
12
-
13
- ## Commands
14
-
15
- | Command | Action |
16
- |---|---|
17
- | `/oai-subquota codex` | Show the Codex quota summary |
18
- | `/oai-subquota refresh` | Force a new quota query |
19
- | `/oai-subquota statusline` | Print compact quota text |
20
- | `/oai-subquota chatgpt` | Report the parked ChatGPT OAuth integration's status |
21
-
22
- ## Authentication and cache
23
-
24
- Authenticate through `/login openai-codex`. The extension uses Pi's provider authentication rather than a separate API-key setting. Cached snapshots are stored at `$XDG_CACHE_HOME/oai-subquota/usage.json`, falling back to `~/.cache/oai-subquota/usage.json`. The normal cache lifetime is 60 seconds.
25
-
26
- The current implementation supports the Codex usage endpoint. General ChatGPT subscription quota lookup is explicitly parked and does not provide a working second quota source. Backend availability and subscription eligibility determine whether fresh quota data can be retrieved.
27
-
28
- ## Source layout
29
-
30
- `index.ts` registers the command and lifecycle hooks and coordinates refreshes. `auth.ts` reads account claims from the provider token; `api.ts` handles usage requests; `cache.ts` handles snapshot persistence; `snapshot.ts` normalizes API responses and captured headers and merges snapshots. `types.ts` defines the shared data model, and `ui.ts` formats and renders quota output. The entrypoint re-exports the existing public types and formatting helpers.
@@ -1,27 +0,0 @@
1
- import { isRecord } from "./snapshot.ts";
2
-
3
- const USAGE_ENDPOINT = "https://chatgpt.com/backend-api/wham/usage";
4
- const REQUEST_TIMEOUT_MS = 15_000;
5
-
6
- export async function queryUsage(accessToken: string, accountId?: string): Promise<Record<string, unknown>> {
7
- const headers: Record<string, string> = {
8
- Authorization: `Bearer ${accessToken}`,
9
- "User-Agent": "codex-cli",
10
- Accept: "application/json",
11
- };
12
- if (accountId) headers["ChatGPT-Account-Id"] = accountId;
13
-
14
- const res = await fetch(USAGE_ENDPOINT, {
15
- method: "GET",
16
- headers,
17
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
18
- });
19
-
20
- if (res.status === 401 || res.status === 403) throw new Error(`Authentication rejected (HTTP ${res.status}). Please run /login openai-codex.`);
21
- if (res.status === 429) throw new Error("Rate limit exceeded for usage endpoint (HTTP 429). Please try again later.");
22
- if (!res.ok) throw new Error(`Usage request failed (HTTP ${res.status}).`);
23
-
24
- const body = await res.json();
25
- if (!isRecord(body)) throw new Error("Invalid JSON received from usage endpoint.");
26
- return body;
27
- }
@@ -1,34 +0,0 @@
1
- import { Buffer } from "node:buffer";
2
- import { isRecord } from "./snapshot.ts";
3
- import type { AccountSnapshot } from "./types.ts";
4
-
5
- const JWT_AUTH_CLAIM = "https://api.openai.com/auth";
6
- const JWT_PROFILE_CLAIM = "https://api.openai.com/profile";
7
-
8
- function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
9
- const part = token.split(".")[1];
10
- if (!part) return undefined;
11
- try {
12
- const json = JSON.parse(Buffer.from(part, "base64url").toString("utf8"));
13
- return isRecord(json) ? json : undefined;
14
- } catch {
15
- return undefined;
16
- }
17
- }
18
-
19
- export function extractAccountFromToken(token: string): AccountSnapshot {
20
- const payload = decodeJwtPayload(token);
21
- if (!payload) return {};
22
-
23
- const auth = isRecord(payload[JWT_AUTH_CLAIM]) ? payload[JWT_AUTH_CLAIM] : undefined;
24
- const profile = isRecord(payload[JWT_PROFILE_CLAIM]) ? payload[JWT_PROFILE_CLAIM] : undefined;
25
-
26
- const accountId = typeof auth?.chatgpt_account_id === "string" ? auth.chatgpt_account_id : undefined;
27
- const plan = typeof auth?.chatgpt_plan_type === "string" ? auth.chatgpt_plan_type : undefined;
28
- const email =
29
- (typeof profile?.email === "string" && profile.email) ||
30
- (typeof payload.email === "string" && payload.email) ||
31
- undefined;
32
-
33
- return { accountId, plan, email };
34
- }
@@ -1,30 +0,0 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { dirname, join } from "node:path";
4
- import { isRecord } from "./snapshot.ts";
5
- import type { CodexUsageSnapshot } from "./types.ts";
6
-
7
- function getCachePath(): string {
8
- const base = process.env.XDG_CACHE_HOME || join(homedir() || ".", ".cache");
9
- return join(base, "oai-subquota", "usage.json");
10
- }
11
-
12
- export async function readCachedSnapshot(): Promise<CodexUsageSnapshot | undefined> {
13
- try {
14
- const raw = await readFile(getCachePath(), "utf8");
15
- const parsed = JSON.parse(raw);
16
- return isRecord(parsed) ? (parsed as unknown as CodexUsageSnapshot) : undefined;
17
- } catch {
18
- return undefined;
19
- }
20
- }
21
-
22
- export async function writeCachedSnapshot(snapshot: CodexUsageSnapshot): Promise<void> {
23
- try {
24
- const file = getCachePath();
25
- await mkdir(dirname(file), { recursive: true });
26
- await writeFile(file, `${JSON.stringify(snapshot, null, 2)}\n`, { mode: 0o600 });
27
- } catch {
28
- // Non-fatal if filesystem is read-only
29
- }
30
- }