pi-codemcp 1.1.2 → 1.2.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.
- package/extensions/index.ts +19 -1
- package/package.json +1 -1
- package/sidecar/executor.py +29 -7
- package/sidecar/gateway.py +26 -4
- package/src/modal.ts +78 -27
- package/src/prompts.ts +2 -1
package/extensions/index.ts
CHANGED
|
@@ -45,7 +45,7 @@ export function createCodeMcpExtension(options: SidecarClientOptions = {}) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
await showServerManagerModal(ctx, {
|
|
48
|
+
const managerResult = await showServerManagerModal(ctx, {
|
|
49
49
|
servers,
|
|
50
50
|
chains: chainStatesFromViews(savedChains),
|
|
51
51
|
settings,
|
|
@@ -74,6 +74,7 @@ export function createCodeMcpExtension(options: SidecarClientOptions = {}) {
|
|
|
74
74
|
onDeleteChain: async (chain) =>
|
|
75
75
|
chainStatesFromViews(await chains.delete(chain.name, chain.scope)),
|
|
76
76
|
});
|
|
77
|
+
if (managerResult === "report-problem") await promptForProblemReport(pi, ctx);
|
|
77
78
|
} catch (error) {
|
|
78
79
|
ctx.ui.notify(summarizeError(error), "error");
|
|
79
80
|
}
|
|
@@ -163,6 +164,23 @@ export async function saveManagerChanges(
|
|
|
163
164
|
|
|
164
165
|
export default createCodeMcpExtension();
|
|
165
166
|
|
|
167
|
+
export async function promptForProblemReport(
|
|
168
|
+
pi: Pick<ExtensionAPI, "sendUserMessage">,
|
|
169
|
+
ctx: Pick<ExtensionCommandContext, "ui">,
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
const description = await ctx.ui.editor("What went wrong?", "");
|
|
172
|
+
if (description?.trim()) pi.sendUserMessage(formatProblemReportPrompt(description.trim()));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function formatProblemReportPrompt(description: string): string {
|
|
176
|
+
return `Something went wrong with pi-codemcp.
|
|
177
|
+
|
|
178
|
+
User's description:
|
|
179
|
+
${description}
|
|
180
|
+
|
|
181
|
+
Investigate the problem in current pi setup. Inspect the available pi-codemcp configuration, environment, and installed package as needed. Determine the likely cause, then prepare a GitHub issue for https://github.com/yolonir/pi-codemcp. Do not include any personal or sensitive information in the issue. Do not autosumbit issue without clear approval.`;
|
|
182
|
+
}
|
|
183
|
+
|
|
166
184
|
function requireServerStatus(
|
|
167
185
|
status: Record<string, unknown>,
|
|
168
186
|
serverName: string,
|
package/package.json
CHANGED
package/sidecar/executor.py
CHANGED
|
@@ -298,6 +298,7 @@ class MontyExecutor:
|
|
|
298
298
|
type_stubs = catalog.type_stubs_for(referenced, include=spec.name)
|
|
299
299
|
async with self._execution_lock:
|
|
300
300
|
await self._type_check(wrapped, catalog, type_stubs)
|
|
301
|
+
await self._compile_runtime(code, catalog, has_input=True)
|
|
301
302
|
|
|
302
303
|
def _new_context(
|
|
303
304
|
self,
|
|
@@ -351,6 +352,9 @@ class MontyExecutor:
|
|
|
351
352
|
except pydantic_monty.MontySyntaxError as error:
|
|
352
353
|
context.metrics.typecheck_ms += _elapsed_ms(typecheck_started)
|
|
353
354
|
return self._failure(context, "preflight", error.display("type-msg").strip())
|
|
355
|
+
except pydantic_monty.MontyRuntimeError as error:
|
|
356
|
+
context.metrics.typecheck_ms += _elapsed_ms(typecheck_started)
|
|
357
|
+
return self._failure(context, "preflight", error.display("type-msg").strip())
|
|
354
358
|
except RuntimeError as error:
|
|
355
359
|
context.metrics.typecheck_ms += _elapsed_ms(typecheck_started)
|
|
356
360
|
return self._failure(
|
|
@@ -360,18 +364,22 @@ class MontyExecutor:
|
|
|
360
364
|
)
|
|
361
365
|
context.metrics.typecheck_ms += _elapsed_ms(typecheck_started)
|
|
362
366
|
|
|
363
|
-
runtime_wrapped = _wrap_code(code, typed=False, has_input=has_input)
|
|
364
|
-
runtime_code = _rewrite_sdk_calls(runtime_wrapped, context.catalog)
|
|
365
367
|
try:
|
|
366
|
-
monty = await
|
|
367
|
-
|
|
368
|
-
|
|
368
|
+
monty = await self._compile_runtime(
|
|
369
|
+
code,
|
|
370
|
+
context.catalog,
|
|
371
|
+
has_input=has_input,
|
|
369
372
|
)
|
|
370
|
-
except (
|
|
373
|
+
except (
|
|
374
|
+
pydantic_monty.MontySyntaxError,
|
|
375
|
+
pydantic_monty.MontyRuntimeError,
|
|
376
|
+
RuntimeError,
|
|
377
|
+
NotImplementedError,
|
|
378
|
+
) as error:
|
|
371
379
|
return self._failure(
|
|
372
380
|
context,
|
|
373
381
|
"preflight",
|
|
374
|
-
f"
|
|
382
|
+
f"Sandbox compilation failed: {error}",
|
|
375
383
|
)
|
|
376
384
|
|
|
377
385
|
async def dispatch_wrapper(name: str, arguments: JsonObject) -> JsonValue:
|
|
@@ -530,6 +538,20 @@ class MontyExecutor:
|
|
|
530
538
|
response.metrics = context.metrics.model_copy()
|
|
531
539
|
return response
|
|
532
540
|
|
|
541
|
+
@staticmethod
|
|
542
|
+
async def _compile_runtime(
|
|
543
|
+
code: str,
|
|
544
|
+
catalog: ToolCatalog,
|
|
545
|
+
*,
|
|
546
|
+
has_input: bool,
|
|
547
|
+
) -> pydantic_monty.Monty:
|
|
548
|
+
runtime_wrapped = _wrap_code(code, typed=False, has_input=has_input)
|
|
549
|
+
runtime_code = _rewrite_sdk_calls(runtime_wrapped, catalog)
|
|
550
|
+
return await pydantic_monty.Monty.acreate(
|
|
551
|
+
runtime_code,
|
|
552
|
+
script_name="codemcp_execute.py",
|
|
553
|
+
)
|
|
554
|
+
|
|
533
555
|
async def _type_check(
|
|
534
556
|
self,
|
|
535
557
|
wrapped_code: str,
|
package/sidecar/gateway.py
CHANGED
|
@@ -727,11 +727,22 @@ class GatewayRuntime:
|
|
|
727
727
|
spec = candidate_catalog.tools[candidate.public_name]
|
|
728
728
|
try:
|
|
729
729
|
await self.executor.validate_saved_chain(code, candidate_catalog, spec)
|
|
730
|
-
except (
|
|
730
|
+
except (
|
|
731
|
+
pydantic_monty.MontyTypingError,
|
|
732
|
+
pydantic_monty.MontySyntaxError,
|
|
733
|
+
pydantic_monty.MontyRuntimeError,
|
|
734
|
+
NotImplementedError,
|
|
735
|
+
RuntimeError,
|
|
736
|
+
) as error:
|
|
731
737
|
if isinstance(error, pydantic_monty.MontyTypingError):
|
|
732
738
|
message = error.display("concise", color=False).strip()
|
|
733
|
-
|
|
739
|
+
elif isinstance(
|
|
740
|
+
error,
|
|
741
|
+
(pydantic_monty.MontySyntaxError, pydantic_monty.MontyRuntimeError),
|
|
742
|
+
):
|
|
734
743
|
message = error.display("type-msg").strip()
|
|
744
|
+
else:
|
|
745
|
+
message = f"Sandbox compilation failed: {error}"
|
|
735
746
|
raise ValueError(_saved_chain_preflight_error(message, output_schema)) from error
|
|
736
747
|
dependencies = self._chain_dependencies(code, candidate_catalog)
|
|
737
748
|
saved = ChainStore.build(
|
|
@@ -790,11 +801,22 @@ class GatewayRuntime:
|
|
|
790
801
|
spec = candidate_catalog.tools[current.public_name]
|
|
791
802
|
try:
|
|
792
803
|
await self.executor.validate_saved_chain(current.code, candidate_catalog, spec)
|
|
793
|
-
except (
|
|
804
|
+
except (
|
|
805
|
+
pydantic_monty.MontyTypingError,
|
|
806
|
+
pydantic_monty.MontySyntaxError,
|
|
807
|
+
pydantic_monty.MontyRuntimeError,
|
|
808
|
+
NotImplementedError,
|
|
809
|
+
RuntimeError,
|
|
810
|
+
) as error:
|
|
794
811
|
if isinstance(error, pydantic_monty.MontyTypingError):
|
|
795
812
|
message = error.display("concise", color=False).strip()
|
|
796
|
-
|
|
813
|
+
elif isinstance(
|
|
814
|
+
error,
|
|
815
|
+
(pydantic_monty.MontySyntaxError, pydantic_monty.MontyRuntimeError),
|
|
816
|
+
):
|
|
797
817
|
message = error.display("type-msg").strip()
|
|
818
|
+
else:
|
|
819
|
+
message = f"Sandbox compilation failed: {error}"
|
|
798
820
|
raise ValueError(
|
|
799
821
|
_saved_chain_preflight_error(message, current.output_schema)
|
|
800
822
|
) from error
|
package/src/modal.ts
CHANGED
|
@@ -124,6 +124,7 @@ interface ManagerSaveResult {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
type UnsavedAction = "save" | "discard" | "cancel";
|
|
127
|
+
export type ServerManagerResult = "report-problem" | undefined;
|
|
127
128
|
|
|
128
129
|
interface ServerManagerOptions {
|
|
129
130
|
servers: ServerModalState[];
|
|
@@ -159,6 +160,11 @@ const OVERLAY_OPTIONS = {
|
|
|
159
160
|
maxHeight: "85%",
|
|
160
161
|
} as const;
|
|
161
162
|
|
|
163
|
+
const PROBLEM_REPORT_LABEL = "Extension is broken!";
|
|
164
|
+
const PROBLEM_REPORT_DESCRIPTION =
|
|
165
|
+
"Well, that sucks. With this button you can ask the agent to describe the problem and prepare a GitHub issue for review. The goal is to make pi-codemcp usable for everyone, don't be lazy - submit an issue. Don't worry, you will see all prompts, this is a transparent process.";
|
|
166
|
+
const PROBLEM_REPORT_SHORTCUT = "Report issue: R";
|
|
167
|
+
|
|
162
168
|
const SETTING_DEFINITIONS: SettingDefinition[] = [
|
|
163
169
|
{
|
|
164
170
|
key: "backgroundWarmup",
|
|
@@ -214,18 +220,18 @@ const SETTING_DEFINITIONS: SettingDefinition[] = [
|
|
|
214
220
|
export async function showServerManagerModal(
|
|
215
221
|
ctx: ExtensionCommandContext,
|
|
216
222
|
options: ServerManagerOptions,
|
|
217
|
-
): Promise<
|
|
223
|
+
): Promise<ServerManagerResult> {
|
|
218
224
|
if (ctx.mode !== "tui") {
|
|
219
225
|
throw new Error("CodeMCP server manager requires interactive mode");
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
|
|
228
|
+
return ctx.ui.custom<ServerManagerResult>(
|
|
223
229
|
(tui, theme, keybindings, done) =>
|
|
224
230
|
new ServerManagerModal(
|
|
225
231
|
options,
|
|
226
232
|
theme,
|
|
227
233
|
keybindings,
|
|
228
|
-
() => done(
|
|
234
|
+
(result) => done(result),
|
|
229
235
|
() => tui.requestRender(),
|
|
230
236
|
),
|
|
231
237
|
{ overlay: true, overlayOptions: OVERLAY_OPTIONS },
|
|
@@ -352,7 +358,7 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
352
358
|
private readonly options: ServerManagerOptions,
|
|
353
359
|
private readonly theme: Theme,
|
|
354
360
|
private readonly keybindings: Keybindings,
|
|
355
|
-
private readonly close: () => void,
|
|
361
|
+
private readonly close: (result?: ServerManagerResult) => void,
|
|
356
362
|
private readonly requestRender: () => void,
|
|
357
363
|
) {
|
|
358
364
|
this.savedSettings = cloneSettings(options.settings);
|
|
@@ -400,6 +406,11 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
400
406
|
return;
|
|
401
407
|
}
|
|
402
408
|
if (this.settingsBusy || this.closePromptBusy) return;
|
|
409
|
+
if (data === "R") {
|
|
410
|
+
this.focusProblemReport();
|
|
411
|
+
this.requestRender();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
403
414
|
if (this.keybindings.matches(data, "tui.select.cancel") || matchesKey(data, Key.escape)) {
|
|
404
415
|
if (this.activeTab !== "settings" && this.search.getValue()) {
|
|
405
416
|
this.search.setValue("");
|
|
@@ -504,7 +515,7 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
504
515
|
this.selectedSettingIndex = cycleIndex(
|
|
505
516
|
this.selectedSettingIndex,
|
|
506
517
|
-1,
|
|
507
|
-
SETTING_DEFINITIONS.length,
|
|
518
|
+
SETTING_DEFINITIONS.length + 1,
|
|
508
519
|
);
|
|
509
520
|
return;
|
|
510
521
|
}
|
|
@@ -512,10 +523,14 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
512
523
|
this.selectedSettingIndex = cycleIndex(
|
|
513
524
|
this.selectedSettingIndex,
|
|
514
525
|
1,
|
|
515
|
-
SETTING_DEFINITIONS.length,
|
|
526
|
+
SETTING_DEFINITIONS.length + 1,
|
|
516
527
|
);
|
|
517
528
|
return;
|
|
518
529
|
}
|
|
530
|
+
if (this.selectedSettingIndex === SETTING_DEFINITIONS.length) {
|
|
531
|
+
if (matchesKey(data, Key.enter) || data === " ") this.openProblemReport();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
519
534
|
if (matchesKey(data, Key.left)) this.cycleSelectedSetting(-1);
|
|
520
535
|
else if (matchesKey(data, Key.right) || matchesKey(data, Key.enter) || data === " ") {
|
|
521
536
|
this.cycleSelectedSetting(1);
|
|
@@ -820,27 +835,50 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
820
835
|
),
|
|
821
836
|
);
|
|
822
837
|
}
|
|
838
|
+
const problemReportSelected = this.selectedSettingIndex === SETTING_DEFINITIONS.length;
|
|
839
|
+
const problemReportPrefix = problemReportSelected ? this.theme.fg("accent", "→") : " ";
|
|
840
|
+
left.push(
|
|
841
|
+
"",
|
|
842
|
+
truncateToWidth(
|
|
843
|
+
`${problemReportPrefix} ${
|
|
844
|
+
problemReportSelected
|
|
845
|
+
? this.theme.fg("accent", PROBLEM_REPORT_LABEL)
|
|
846
|
+
: PROBLEM_REPORT_LABEL
|
|
847
|
+
}`,
|
|
848
|
+
leftWidth,
|
|
849
|
+
),
|
|
850
|
+
);
|
|
823
851
|
const definition = SETTING_DEFINITIONS[this.selectedSettingIndex];
|
|
824
|
-
const right =
|
|
852
|
+
const right = problemReportSelected
|
|
825
853
|
? [
|
|
826
|
-
this.theme.fg("accent", this.theme.bold(
|
|
827
|
-
this.theme.fg("muted", settingLabel(definition, this.draftSettings[definition.key])),
|
|
854
|
+
this.theme.fg("accent", this.theme.bold(PROBLEM_REPORT_LABEL)),
|
|
828
855
|
"",
|
|
829
|
-
...wrapPlainText(
|
|
856
|
+
...wrapPlainText(PROBLEM_REPORT_DESCRIPTION, rightWidth).map((line) =>
|
|
830
857
|
this.theme.fg("muted", line),
|
|
831
858
|
),
|
|
832
859
|
"",
|
|
833
|
-
this.theme.fg("dim", "
|
|
834
|
-
...(this.settingsBusy
|
|
835
|
-
? ["", this.theme.fg("warning", "Saving staged changes…")]
|
|
836
|
-
: this.hasUnsavedChanges()
|
|
837
|
-
? ["", this.theme.fg("warning", "Unsaved changes")]
|
|
838
|
-
: []),
|
|
839
|
-
...(this.settingsError
|
|
840
|
-
? ["", this.theme.fg("warning", `Error: ${this.settingsError}`)]
|
|
841
|
-
: []),
|
|
860
|
+
this.theme.fg("dim", "enter describe the problem"),
|
|
842
861
|
]
|
|
843
|
-
:
|
|
862
|
+
: definition
|
|
863
|
+
? [
|
|
864
|
+
this.theme.fg("accent", this.theme.bold(definition.label)),
|
|
865
|
+
this.theme.fg("muted", settingLabel(definition, this.draftSettings[definition.key])),
|
|
866
|
+
"",
|
|
867
|
+
...wrapPlainText(definition.description, rightWidth).map((line) =>
|
|
868
|
+
this.theme.fg("muted", line),
|
|
869
|
+
),
|
|
870
|
+
"",
|
|
871
|
+
this.theme.fg("dim", "←/→ change · enter next · ctrl+s save"),
|
|
872
|
+
...(this.settingsBusy
|
|
873
|
+
? ["", this.theme.fg("warning", "Saving staged changes…")]
|
|
874
|
+
: this.hasUnsavedChanges()
|
|
875
|
+
? ["", this.theme.fg("warning", "Unsaved changes")]
|
|
876
|
+
: []),
|
|
877
|
+
...(this.settingsError
|
|
878
|
+
? ["", this.theme.fg("warning", `Error: ${this.settingsError}`)]
|
|
879
|
+
: []),
|
|
880
|
+
]
|
|
881
|
+
: [];
|
|
844
882
|
const lines: string[] = [];
|
|
845
883
|
for (let index = 0; index < splitHeight; index += 1) {
|
|
846
884
|
lines.push(
|
|
@@ -852,16 +890,17 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
852
890
|
|
|
853
891
|
private footer(): string {
|
|
854
892
|
const pending = this.hasUnsavedChanges() ? " · * unsaved · ctrl+s save" : "";
|
|
893
|
+
const report = ` · ${PROBLEM_REPORT_SHORTCUT}`;
|
|
855
894
|
if (this.activeTab === "settings") {
|
|
856
|
-
return `tab servers · ↑/↓ navigate ·
|
|
895
|
+
return `tab servers · ↑/↓ navigate · ←/→ change · enter select · ctrl+s save · esc close${pending}${report}`;
|
|
857
896
|
}
|
|
858
897
|
if (this.activeTab === "chains") {
|
|
859
|
-
return `tab stats · ↑/↓ navigate · space toggle · r revalidate · del delete · esc close${pending}`;
|
|
898
|
+
return `tab stats · ↑/↓ navigate · space toggle · r revalidate · del delete · esc close${pending}${report}`;
|
|
860
899
|
}
|
|
861
900
|
if (this.activeTab === "stats") {
|
|
862
|
-
return `tab settings · bounded local rollups · esc close${pending}`;
|
|
901
|
+
return `tab settings · bounded local rollups · esc close${pending}${report}`;
|
|
863
902
|
}
|
|
864
|
-
return `tab chains · ←/→ pane · ↑/↓ navigate · space toggle · d discover · esc close${pending}`;
|
|
903
|
+
return `tab chains · ←/→ pane · ↑/↓ navigate · space toggle · d discover · esc close${pending}${report}`;
|
|
865
904
|
}
|
|
866
905
|
|
|
867
906
|
private moveSelection(direction: -1 | 1): void {
|
|
@@ -1053,17 +1092,29 @@ class ServerManagerModal implements Component, Focusable {
|
|
|
1053
1092
|
}
|
|
1054
1093
|
}
|
|
1055
1094
|
|
|
1056
|
-
private
|
|
1095
|
+
private focusProblemReport(): void {
|
|
1096
|
+
this.activeTab = "settings";
|
|
1097
|
+
this.selectedSettingIndex = SETTING_DEFINITIONS.length;
|
|
1098
|
+
this.search.setValue("");
|
|
1099
|
+
this.search.focused = false;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
private openProblemReport(): void {
|
|
1103
|
+
if (this.hasUnsavedChanges()) this.resolveUnsavedClose("report-problem");
|
|
1104
|
+
else this.close("report-problem");
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
private resolveUnsavedClose(result?: ServerManagerResult): void {
|
|
1057
1108
|
if (this.closePromptBusy || this.settingsBusy) return;
|
|
1058
1109
|
this.closePromptBusy = true;
|
|
1059
1110
|
void this.options
|
|
1060
1111
|
.onResolveUnsaved()
|
|
1061
1112
|
.then(async (action) => {
|
|
1062
1113
|
if (action === "discard") {
|
|
1063
|
-
this.close();
|
|
1114
|
+
this.close(result);
|
|
1064
1115
|
return;
|
|
1065
1116
|
}
|
|
1066
|
-
if (action === "save" && (await this.persistDraft())) this.close();
|
|
1117
|
+
if (action === "save" && (await this.persistDraft())) this.close(result);
|
|
1067
1118
|
})
|
|
1068
1119
|
.catch((error: unknown) => {
|
|
1069
1120
|
this.settingsError = summarizeError(error);
|
package/src/prompts.ts
CHANGED
|
@@ -11,11 +11,12 @@ export const EXECUTE_PROMPT_GUIDELINES = [
|
|
|
11
11
|
"Use programmatic execution for a bounded workflow when code can deterministically filter, join, aggregate, deduplicate, validate, or reduce intermediate results.",
|
|
12
12
|
"Keep a model turn between calls when an intermediate result changes the semantic decision or user approval is required.",
|
|
13
13
|
"Return the smallest result that answers the request; oversized results fail explicitly with bounded structural inspection data.",
|
|
14
|
-
"SDK facades
|
|
14
|
+
"SDK facades are prebound globals and must not be imported. Import supported stdlib normally (for example, `import asyncio`); class declarations and `__import__` are unsupported.",
|
|
15
15
|
] as const;
|
|
16
16
|
|
|
17
17
|
export const SAVE_CHAIN_PROMPT_GUIDELINES = [
|
|
18
18
|
"Save only after the user explicitly asks or accepts, and only after the same code has executed successfully.",
|
|
19
|
+
"Use generated result item types for nested schema collections; do not declare TypedDict classes.",
|
|
19
20
|
"Use project scope when available unless the user explicitly requests global scope; make schemas describe the exact parameterized contract.",
|
|
20
21
|
] as const;
|
|
21
22
|
|