mitsupi 1.4.0 → 1.6.0

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 (43) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/README.md +28 -18
  4. package/analyze-edits.py +232 -0
  5. package/distributions/README.md +8 -0
  6. package/distributions/mitsupi-common/README.md +11 -0
  7. package/distributions/mitsupi-common/package.json +95 -0
  8. package/distributions/mitsupi-loaded/README.md +13 -0
  9. package/distributions/mitsupi-loaded/package.json +38 -0
  10. package/{pi-extensions → extensions}/answer.ts +11 -11
  11. package/extensions/btw.ts +963 -0
  12. package/{pi-extensions → extensions}/context.ts +2 -2
  13. package/{pi-extensions → extensions}/control.ts +20 -13
  14. package/{pi-extensions → extensions}/files.ts +6 -8
  15. package/{pi-extensions → extensions}/go-to-bed.ts +2 -2
  16. package/{pi-extensions → extensions}/loop.ts +18 -11
  17. package/extensions/multi-edit.ts +871 -0
  18. package/{pi-extensions → extensions}/review.ts +254 -82
  19. package/{pi-extensions → extensions}/session-breakdown.ts +551 -74
  20. package/extensions/split-fork.ts +130 -0
  21. package/{pi-extensions → extensions}/todos.ts +41 -34
  22. package/extensions/uv.ts +123 -0
  23. package/intercepted-commands/poetry +8 -1
  24. package/intercepted-commands/python +42 -2
  25. package/intercepted-commands/python3 +42 -2
  26. package/package.json +6 -3
  27. package/skills/google-workspace/SKILL.md +53 -8
  28. package/skills/google-workspace/scripts/auth.js +106 -28
  29. package/skills/google-workspace/scripts/common.js +201 -32
  30. package/skills/google-workspace/scripts/workspace.js +64 -9
  31. package/skills/native-web-search/SKILL.md +2 -0
  32. package/skills/native-web-search/search.mjs +102 -15
  33. package/skills/pi-share/SKILL.md +5 -2
  34. package/skills/pi-share/fetch-session.mjs +46 -15
  35. package/skills/summarize/SKILL.md +4 -3
  36. package/skills/summarize/to-markdown.mjs +3 -1
  37. package/skills/web-browser/SKILL.md +6 -0
  38. package/skills/web-browser/scripts/start.js +75 -27
  39. package/pi-extensions/uv.ts +0 -33
  40. /package/{pi-extensions → extensions}/notify.ts +0 -0
  41. /package/{pi-extensions → extensions}/prompt-editor.ts +0 -0
  42. /package/{pi-extensions → extensions}/whimsical.ts +0 -0
  43. /package/{pi-themes → themes}/nightowl.json +0 -0
@@ -124,7 +124,7 @@ function buildSkillIndex(pi: ExtensionAPI, cwd: string): SkillIndexEntry[] {
124
124
  .getCommands()
125
125
  .filter((c) => c.source === "skill")
126
126
  .map((c) => {
127
- const p = c.path ? normalizeReadPath(c.path, cwd) : "";
127
+ const p = c.sourceInfo?.path ? normalizeReadPath(c.sourceInfo.path, cwd) : "";
128
128
  return {
129
129
  name: normalizeSkillName(c.name),
130
130
  skillFilePath: p,
@@ -479,7 +479,7 @@ export default function contextExtension(pi: ExtensionAPI) {
479
479
 
480
480
  const extensionsByPath = new Map<string, string[]>();
481
481
  for (const c of extensionCmds) {
482
- const p = c.path ?? "<unknown>";
482
+ const p = c.sourceInfo?.path ?? "<unknown>";
483
483
  const arr = extensionsByPath.get(p) ?? [];
484
484
  arr.push(c.name);
485
485
  extensionsByPath.set(p, arr);
@@ -42,7 +42,13 @@
42
42
  * Events are JSON objects with { type: "event", event, data?, subscriptionId? }
43
43
  */
44
44
 
45
- import type { ExtensionAPI, ExtensionContext, TurnEndEvent, MessageRenderer } from "@mariozechner/pi-coding-agent";
45
+ import type {
46
+ ExtensionAPI,
47
+ ExtensionContext,
48
+ TurnEndEvent,
49
+ MessageRenderer,
50
+ ModelRegistry,
51
+ } from "@mariozechner/pi-coding-agent";
46
52
  import { getMarkdownTheme } from "@mariozechner/pi-coding-agent";
47
53
  import { complete, type Model, type Api, type UserMessage, type TextContent } from "@mariozechner/pi-ai";
48
54
  import { StringEnum } from "@mariozechner/pi-ai";
@@ -165,21 +171,18 @@ Be concise but comprehensive. Preserve exact file paths, function names, and err
165
171
 
166
172
  async function selectSummarizationModel(
167
173
  currentModel: Model<Api> | undefined,
168
- modelRegistry: {
169
- find: (provider: string, modelId: string) => Model<Api> | undefined;
170
- getApiKey: (model: Model<Api>) => Promise<string | undefined>;
171
- },
174
+ modelRegistry: ModelRegistry,
172
175
  ): Promise<Model<Api> | undefined> {
173
176
  const codexModel = modelRegistry.find("openai-codex", CODEX_MODEL_ID);
174
177
  if (codexModel) {
175
- const apiKey = await modelRegistry.getApiKey(codexModel);
176
- if (apiKey) return codexModel;
178
+ const auth = await modelRegistry.getApiKeyAndHeaders(codexModel);
179
+ if (auth.ok) return codexModel;
177
180
  }
178
181
 
179
182
  const haikuModel = modelRegistry.find("anthropic", HAIKU_MODEL_ID);
180
183
  if (haikuModel) {
181
- const apiKey = await modelRegistry.getApiKey(haikuModel);
182
- if (apiKey) return haikuModel;
184
+ const auth = await modelRegistry.getApiKeyAndHeaders(haikuModel);
185
+ if (auth.ok) return haikuModel;
183
186
  }
184
187
 
185
188
  return currentModel;
@@ -657,9 +660,9 @@ async function handleCommand(
657
660
  return;
658
661
  }
659
662
 
660
- const apiKey = await ctx.modelRegistry.getApiKey(model);
661
- if (!apiKey) {
662
- respond(false, "get_summary", undefined, "No API key available for summarization model");
663
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
664
+ if (!auth.ok) {
665
+ respond(false, "get_summary", undefined, auth.error);
663
666
  return;
664
667
  }
665
668
 
@@ -677,7 +680,7 @@ async function handleCommand(
677
680
  const response = await complete(
678
681
  model,
679
682
  { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: [userMessage] },
680
- { apiKey },
683
+ { apiKey: auth.apiKey, headers: auth.headers },
681
684
  );
682
685
 
683
686
  if (response.stopReason === "aborted" || response.stopReason === "error") {
@@ -1060,6 +1063,10 @@ export default function (pi: ExtensionAPI) {
1060
1063
  await refreshServer(ctx);
1061
1064
  });
1062
1065
 
1066
+ pi.on("session_fork", async (_event, ctx) => {
1067
+ await refreshServer(ctx);
1068
+ });
1069
+
1063
1070
  pi.on("session_shutdown", async () => {
1064
1071
  if (state.aliasTimer) {
1065
1072
  clearInterval(state.aliasTimer);
@@ -24,7 +24,6 @@ import { DynamicBorder } from "@mariozechner/pi-coding-agent";
24
24
  import {
25
25
  Container,
26
26
  fuzzyFilter,
27
- getEditorKeybindings,
28
27
  Input,
29
28
  matchesKey,
30
29
  type SelectItem,
@@ -856,7 +855,7 @@ const showFileSelector = async (
856
855
  });
857
856
 
858
857
  let quickAction: "diff" | null = null;
859
- const selection = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
858
+ const selection = await ctx.ui.custom<string | null>((tui, theme, keybindings, done) => {
860
859
  const container = new Container();
861
860
  container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
862
861
  container.addChild(new Text(theme.fg("accent", theme.bold(" Select file")), 0, 0));
@@ -937,16 +936,15 @@ const showFileSelector = async (
937
936
  }
938
937
  }
939
938
 
940
- const kb = getEditorKeybindings();
941
939
  if (
942
- kb.matches(data, "selectUp") ||
943
- kb.matches(data, "selectDown") ||
944
- kb.matches(data, "selectConfirm") ||
945
- kb.matches(data, "selectCancel")
940
+ keybindings.matches(data, "tui.select.up") ||
941
+ keybindings.matches(data, "tui.select.down") ||
942
+ keybindings.matches(data, "tui.select.confirm") ||
943
+ keybindings.matches(data, "tui.select.cancel")
946
944
  ) {
947
945
  if (selectList) {
948
946
  selectList.handleInput(data);
949
- } else if (kb.matches(data, "selectCancel")) {
947
+ } else if (keybindings.matches(data, "tui.select.cancel")) {
950
948
  done(null);
951
949
  }
952
950
  tui.requestRender();
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI, ToolCallEventResult } from "@mariozechner/pi-coding-agent";
2
2
 
3
3
  // "After midnight" usually means late-night usage. Default window: 00:00-05:59 local time.
4
4
  const QUIET_HOURS_START = 0;
@@ -134,7 +134,7 @@ export default function goToBedExtension(pi: ExtensionAPI) {
134
134
  }
135
135
  });
136
136
 
137
- pi.on("tool_call", async (event) => {
137
+ pi.on("tool_call", async (event): Promise<ToolCallEventResult | void> => {
138
138
  const now = new Date();
139
139
  if (!isQuietHours(now)) {
140
140
  confirmedNightKey = null;
@@ -87,22 +87,22 @@ function getConditionText(mode: LoopMode, condition?: string): string {
87
87
 
88
88
  async function selectSummaryModel(
89
89
  ctx: ExtensionContext,
90
- ): Promise<{ model: Model<Api>; apiKey: string } | null> {
90
+ ): Promise<{ model: Model<Api>; apiKey?: string; headers?: Record<string, string> } | null> {
91
91
  if (!ctx.model) return null;
92
92
 
93
93
  if (ctx.model.provider === "anthropic") {
94
94
  const haikuModel = ctx.modelRegistry.find("anthropic", HAIKU_MODEL_ID);
95
95
  if (haikuModel) {
96
- const apiKey = await ctx.modelRegistry.getApiKey(haikuModel);
97
- if (apiKey) {
98
- return { model: haikuModel, apiKey };
96
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(haikuModel);
97
+ if (auth.ok) {
98
+ return { model: haikuModel, apiKey: auth.apiKey, headers: auth.headers };
99
99
  }
100
100
  }
101
101
  }
102
102
 
103
- const apiKey = await ctx.modelRegistry.getApiKey(ctx.model);
104
- if (!apiKey) return null;
105
- return { model: ctx.model, apiKey };
103
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
104
+ if (!auth.ok) return null;
105
+ return { model: ctx.model, apiKey: auth.apiKey, headers: auth.headers };
106
106
  }
107
107
 
108
108
  async function summarizeBreakoutCondition(
@@ -124,7 +124,7 @@ async function summarizeBreakoutCondition(
124
124
  const response = await complete(
125
125
  selection.model,
126
126
  { systemPrompt: SUMMARY_SYSTEM_PROMPT, messages: [userMessage] },
127
- { apiKey: selection.apiKey },
127
+ { apiKey: selection.apiKey, headers: selection.headers },
128
128
  );
129
129
 
130
130
  if (response.stopReason === "aborted" || response.stopReason === "error") {
@@ -400,15 +400,22 @@ export default function loopExtension(pi: ExtensionAPI): void {
400
400
 
401
401
  pi.on("session_before_compact", async (event, ctx) => {
402
402
  if (!loopState.active || !loopState.mode || !ctx.model) return;
403
- const apiKey = await ctx.modelRegistry.getApiKey(ctx.model);
404
- if (!apiKey) return;
403
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
404
+ if (!auth.ok) return;
405
405
 
406
406
  const instructionParts = [event.customInstructions, getCompactionInstructions(loopState.mode, loopState.condition)]
407
407
  .filter(Boolean)
408
408
  .join("\n\n");
409
409
 
410
410
  try {
411
- const compaction = await compact(event.preparation, ctx.model, apiKey, instructionParts, event.signal);
411
+ const compaction = await compact(
412
+ event.preparation,
413
+ ctx.model,
414
+ auth.apiKey ?? "",
415
+ auth.headers,
416
+ instructionParts,
417
+ event.signal,
418
+ );
412
419
  return { compaction };
413
420
  } catch (error) {
414
421
  if (ctx.hasUI) {