my-pi 0.0.13 → 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.
- package/README.md +2 -0
- package/dist/{api-CWEizv2k.js → api-Dxi4curf.js} +639 -102
- package/dist/api-Dxi4curf.js.map +1 -0
- package/dist/api.js +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/extensions/config.test.ts +9 -0
- package/src/extensions/config.ts +30 -2
- package/src/extensions/confirm-destructive.test.ts +157 -0
- package/src/extensions/confirm-destructive.ts +61 -0
- package/src/extensions/extensions.test.ts +114 -0
- package/src/extensions/extensions.ts +114 -108
- package/src/extensions/handoff.ts +152 -66
- package/src/extensions/hooks-resolution.test.ts +246 -0
- package/src/extensions/hooks-resolution.ts +584 -0
- package/src/extensions/session-name.ts +234 -0
- package/dist/api-CWEizv2k.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InteractiveMode as InteractiveMode$1, SessionManager, SettingsManager, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, defineTool, getAgentDir, parseFrontmatter, runPrintMode as runPrintMode$1 } from "@mariozechner/pi-coding-agent";
|
|
1
|
+
import { BorderedLoader, InteractiveMode as InteractiveMode$1, SessionManager, SettingsManager, convertToLlm, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, defineTool, getAgentDir, parseFrontmatter, runPrintMode as runPrintMode$1, serializeConversation } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { cpSync, existsSync, globSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -6,6 +6,7 @@ import { Type } from "@sinclair/typebox";
|
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { Container, SettingsList, Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
9
|
+
import { complete } from "@mariozechner/pi-ai";
|
|
9
10
|
import { readFile } from "node:fs/promises";
|
|
10
11
|
import { EventEmitter } from "node:events";
|
|
11
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -380,7 +381,7 @@ const BUILTIN_EXTENSIONS = [
|
|
|
380
381
|
{
|
|
381
382
|
key: "handoff",
|
|
382
383
|
label: "Handoff",
|
|
383
|
-
description: "
|
|
384
|
+
description: "AI-generated session handoff with editor review and new-session prefill",
|
|
384
385
|
cli_flag: "--no-handoff",
|
|
385
386
|
aliases: ["handoff"]
|
|
386
387
|
},
|
|
@@ -408,6 +409,31 @@ const BUILTIN_EXTENSIONS = [
|
|
|
408
409
|
description: "Language Server Protocol tools (diagnostics, hover, definition, references)",
|
|
409
410
|
cli_flag: "--no-lsp",
|
|
410
411
|
aliases: ["lsp", "language-server"]
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
key: "session-name",
|
|
415
|
+
label: "Session name",
|
|
416
|
+
description: "AI-powered session auto-naming and /session-name command",
|
|
417
|
+
cli_flag: "--no-session-name",
|
|
418
|
+
aliases: [
|
|
419
|
+
"session-name",
|
|
420
|
+
"session",
|
|
421
|
+
"auto-name"
|
|
422
|
+
]
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
key: "confirm-destructive",
|
|
426
|
+
label: "Confirm destructive",
|
|
427
|
+
description: "Prompt before destructive session actions like clear, switch, and fork",
|
|
428
|
+
cli_flag: "--no-confirm-destructive",
|
|
429
|
+
aliases: ["confirm-destructive", "confirm"]
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
key: "hooks-resolution",
|
|
433
|
+
label: "Hooks resolution",
|
|
434
|
+
description: "Claude Code style PostToolUse hook compatibility from .claude, .rulesync, and .pi configs",
|
|
435
|
+
cli_flag: "--no-hooks",
|
|
436
|
+
aliases: ["hooks-resolution", "hooks"]
|
|
411
437
|
}
|
|
412
438
|
];
|
|
413
439
|
function get_builtin_extensions_config_path() {
|
|
@@ -471,6 +497,33 @@ function find_builtin_extension(query) {
|
|
|
471
497
|
].some((value) => value.toLowerCase() === normalized));
|
|
472
498
|
}
|
|
473
499
|
//#endregion
|
|
500
|
+
//#region src/extensions/confirm-destructive.ts
|
|
501
|
+
async function confirm_destructive(pi) {
|
|
502
|
+
pi.on("session_before_switch", async (event, ctx) => {
|
|
503
|
+
if (!ctx.hasUI) return;
|
|
504
|
+
if (event.reason === "new") {
|
|
505
|
+
if (!await ctx.ui.confirm("Clear session?", "This will delete all messages in the current session.")) {
|
|
506
|
+
ctx.ui.notify("Clear cancelled", "info");
|
|
507
|
+
return { cancel: true };
|
|
508
|
+
}
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (ctx.sessionManager.getEntries().some((e) => e.type === "message" && e.message.role === "user")) {
|
|
512
|
+
if (!await ctx.ui.confirm("Switch session?", "You have messages in the current session. Switch anyway?")) {
|
|
513
|
+
ctx.ui.notify("Switch cancelled", "info");
|
|
514
|
+
return { cancel: true };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
pi.on("session_before_fork", async (event, ctx) => {
|
|
519
|
+
if (!ctx.hasUI) return;
|
|
520
|
+
if (await ctx.ui.select(`Fork from entry ${event.entryId.slice(0, 8)}?`, ["Yes, create fork", "No, stay in current session"]) !== "Yes, create fork") {
|
|
521
|
+
ctx.ui.notify("Fork cancelled", "info");
|
|
522
|
+
return { cancel: true };
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
474
527
|
//#region src/extensions/extensions.ts
|
|
475
528
|
const ENABLED$2 = "[x]";
|
|
476
529
|
const DISABLED$2 = "[ ]";
|
|
@@ -534,6 +587,66 @@ function save_extension_enabled(key, enabled) {
|
|
|
534
587
|
}
|
|
535
588
|
function create_extensions_extension(options = {}) {
|
|
536
589
|
const force_disabled = to_force_disabled_set(options.force_disabled);
|
|
590
|
+
async function show_manager(ctx) {
|
|
591
|
+
if (!ctx.hasUI) return false;
|
|
592
|
+
const states = resolve_builtin_extension_states(force_disabled);
|
|
593
|
+
const initial_enabled = new Set(states.filter((state) => state.saved_enabled).map((state) => state.key));
|
|
594
|
+
const current_enabled = new Set(initial_enabled);
|
|
595
|
+
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
596
|
+
const items = states.map(to_setting_item$1);
|
|
597
|
+
const container = new Container();
|
|
598
|
+
container.addChild({
|
|
599
|
+
render: () => {
|
|
600
|
+
const saved_enabled = current_enabled.size;
|
|
601
|
+
const saved_disabled = states.length - saved_enabled;
|
|
602
|
+
const enabled_now = [...current_enabled].filter((key) => !force_disabled.has(key)).length;
|
|
603
|
+
const disabled_now = states.length - enabled_now;
|
|
604
|
+
return [
|
|
605
|
+
theme.fg("accent", theme.bold("Built-in extensions")),
|
|
606
|
+
theme.fg("muted", `${saved_enabled} saved enabled • ${saved_disabled} saved disabled • ${enabled_now} enabled now • ${disabled_now} disabled now`),
|
|
607
|
+
""
|
|
608
|
+
];
|
|
609
|
+
},
|
|
610
|
+
invalidate: () => {}
|
|
611
|
+
});
|
|
612
|
+
const settings_list = new SettingsList(items, Math.min(Math.max(items.length + 4, 8), 16), {
|
|
613
|
+
cursor: theme.fg("accent", "›"),
|
|
614
|
+
label: (text, selected) => selected ? theme.fg("accent", text) : text,
|
|
615
|
+
value: (text, selected) => {
|
|
616
|
+
const color = text === ENABLED$2 ? "success" : "dim";
|
|
617
|
+
const rendered = theme.fg(color, text);
|
|
618
|
+
return selected ? theme.bold(theme.fg("accent", rendered)) : rendered;
|
|
619
|
+
},
|
|
620
|
+
description: (text) => theme.fg("muted", text),
|
|
621
|
+
hint: (text) => theme.fg("dim", text)
|
|
622
|
+
}, (id, new_value) => {
|
|
623
|
+
const key = id;
|
|
624
|
+
const enabled = new_value === ENABLED$2;
|
|
625
|
+
if (enabled) current_enabled.add(key);
|
|
626
|
+
else current_enabled.delete(key);
|
|
627
|
+
save_extension_enabled(key, enabled);
|
|
628
|
+
}, () => done(void 0), { enableSearch: true });
|
|
629
|
+
container.addChild(settings_list);
|
|
630
|
+
container.addChild(new Text(theme.fg("dim", "esc close • search filters • changes save immediately • CLI --no-* flags still win in this process"), 0, 1));
|
|
631
|
+
return {
|
|
632
|
+
render(width) {
|
|
633
|
+
return container.render(width);
|
|
634
|
+
},
|
|
635
|
+
invalidate() {
|
|
636
|
+
container.invalidate();
|
|
637
|
+
},
|
|
638
|
+
handleInput(data) {
|
|
639
|
+
settings_list.handleInput(data);
|
|
640
|
+
tui.requestRender();
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
});
|
|
644
|
+
if (!sets_equal$2(initial_enabled, current_enabled)) {
|
|
645
|
+
ctx.ui.notify(force_disabled.size > 0 ? "Reloading to apply updated built-in extensions. CLI --no-* flags still force-disable some extensions in this process." : "Reloading to apply updated built-in extensions...", "info");
|
|
646
|
+
await ctx.reload();
|
|
647
|
+
}
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
537
650
|
return async function extensions(pi) {
|
|
538
651
|
const subs = [
|
|
539
652
|
"list",
|
|
@@ -565,65 +678,8 @@ function create_extensions_extension(options = {}) {
|
|
|
565
678
|
},
|
|
566
679
|
handler: async (args, ctx) => {
|
|
567
680
|
const trimmed = args.trim();
|
|
568
|
-
if (!trimmed
|
|
569
|
-
|
|
570
|
-
const initial_enabled = new Set(states.filter((state) => state.saved_enabled).map((state) => state.key));
|
|
571
|
-
const current_enabled = new Set(initial_enabled);
|
|
572
|
-
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
573
|
-
const items = states.map(to_setting_item$1);
|
|
574
|
-
const container = new Container();
|
|
575
|
-
container.addChild({
|
|
576
|
-
render: () => {
|
|
577
|
-
const saved_enabled = current_enabled.size;
|
|
578
|
-
const saved_disabled = states.length - saved_enabled;
|
|
579
|
-
const enabled_now = [...current_enabled].filter((key) => !force_disabled.has(key)).length;
|
|
580
|
-
const disabled_now = states.length - enabled_now;
|
|
581
|
-
return [
|
|
582
|
-
theme.fg("accent", theme.bold("Built-in extensions")),
|
|
583
|
-
theme.fg("muted", `${saved_enabled} saved enabled • ${saved_disabled} saved disabled • ${enabled_now} enabled now • ${disabled_now} disabled now`),
|
|
584
|
-
""
|
|
585
|
-
];
|
|
586
|
-
},
|
|
587
|
-
invalidate: () => {}
|
|
588
|
-
});
|
|
589
|
-
const settings_list = new SettingsList(items, Math.min(Math.max(items.length + 4, 8), 16), {
|
|
590
|
-
cursor: theme.fg("accent", "›"),
|
|
591
|
-
label: (text, selected) => selected ? theme.fg("accent", text) : text,
|
|
592
|
-
value: (text, selected) => {
|
|
593
|
-
const color = text === ENABLED$2 ? "success" : "dim";
|
|
594
|
-
const rendered = theme.fg(color, text);
|
|
595
|
-
return selected ? theme.bold(theme.fg("accent", rendered)) : rendered;
|
|
596
|
-
},
|
|
597
|
-
description: (text) => theme.fg("muted", text),
|
|
598
|
-
hint: (text) => theme.fg("dim", text)
|
|
599
|
-
}, (id, new_value) => {
|
|
600
|
-
const key = id;
|
|
601
|
-
const enabled = new_value === ENABLED$2;
|
|
602
|
-
if (enabled) current_enabled.add(key);
|
|
603
|
-
else current_enabled.delete(key);
|
|
604
|
-
save_extension_enabled(key, enabled);
|
|
605
|
-
}, () => done(void 0), { enableSearch: true });
|
|
606
|
-
container.addChild(settings_list);
|
|
607
|
-
container.addChild(new Text(theme.fg("dim", "esc close • search filters • changes save immediately • CLI --no-* flags still win in this process"), 0, 1));
|
|
608
|
-
return {
|
|
609
|
-
render(width) {
|
|
610
|
-
return container.render(width);
|
|
611
|
-
},
|
|
612
|
-
invalidate() {
|
|
613
|
-
container.invalidate();
|
|
614
|
-
},
|
|
615
|
-
handleInput(data) {
|
|
616
|
-
settings_list.handleInput(data);
|
|
617
|
-
tui.requestRender();
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
});
|
|
621
|
-
if (!sets_equal$2(initial_enabled, current_enabled)) {
|
|
622
|
-
ctx.ui.notify(force_disabled.size > 0 ? "Reloading to apply updated built-in extensions. CLI --no-* flags still force-disable some extensions in this process." : "Reloading to apply updated built-in extensions...", "info");
|
|
623
|
-
await ctx.reload();
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
return;
|
|
681
|
+
if (!trimmed) {
|
|
682
|
+
if (await show_manager(ctx)) return;
|
|
627
683
|
}
|
|
628
684
|
const [sub, ...rest] = (trimmed || "list").split(/\s+/);
|
|
629
685
|
const arg = rest.join(" ");
|
|
@@ -636,6 +692,7 @@ function create_extensions_extension(options = {}) {
|
|
|
636
692
|
case "disable":
|
|
637
693
|
case "toggle": {
|
|
638
694
|
if (!arg) {
|
|
695
|
+
if (await show_manager(ctx)) return;
|
|
639
696
|
ctx.ui.notify(`Usage: /extensions ${sub} <key>`, "warning");
|
|
640
697
|
return;
|
|
641
698
|
}
|
|
@@ -789,52 +846,407 @@ async function filter_output(pi) {
|
|
|
789
846
|
}
|
|
790
847
|
//#endregion
|
|
791
848
|
//#region src/extensions/handoff.ts
|
|
849
|
+
const SYSTEM_PROMPT$1 = `You are a context transfer assistant. Given a conversation history and the user's goal for a new thread, generate a focused prompt that:
|
|
850
|
+
|
|
851
|
+
1. Summarizes relevant context from the conversation (decisions made, approaches taken, key findings)
|
|
852
|
+
2. Lists any relevant files that were discussed or modified
|
|
853
|
+
3. Clearly states the next task based on the user's goal
|
|
854
|
+
4. Is self-contained - the new thread should be able to proceed without the old conversation
|
|
855
|
+
|
|
856
|
+
Format your response as a prompt the user can send to start the new thread. Be concise but include all necessary context. Do not include any preamble like "Here's the prompt" - just output the prompt itself.
|
|
857
|
+
|
|
858
|
+
Example output format:
|
|
859
|
+
## Context
|
|
860
|
+
We've been working on X. Key decisions:
|
|
861
|
+
- Decision 1
|
|
862
|
+
- Decision 2
|
|
863
|
+
|
|
864
|
+
Files involved:
|
|
865
|
+
- path/to/file1.ts
|
|
866
|
+
- path/to/file2.ts
|
|
867
|
+
|
|
868
|
+
## Task
|
|
869
|
+
[Clear description of what to do next based on user's goal]`;
|
|
792
870
|
async function handoff(pi) {
|
|
793
|
-
const history = [];
|
|
794
|
-
pi.on("message_end", async (event) => {
|
|
795
|
-
const msg = event.message;
|
|
796
|
-
if (!msg) return;
|
|
797
|
-
const content = msg.content;
|
|
798
|
-
if (!Array.isArray(content)) return;
|
|
799
|
-
const text = content.filter((c) => c.type === "text").map((c) => c.text || "").join("\n");
|
|
800
|
-
if (!text) return;
|
|
801
|
-
const summary = text.length > 200 ? text.slice(0, 200) + "..." : text;
|
|
802
|
-
history.push({
|
|
803
|
-
role: msg.role || "unknown",
|
|
804
|
-
summary,
|
|
805
|
-
timestamp: Date.now()
|
|
806
|
-
});
|
|
807
|
-
});
|
|
808
871
|
pi.registerCommand("handoff", {
|
|
809
|
-
description: "
|
|
872
|
+
description: "Transfer context to a new focused session with an AI-generated prompt",
|
|
810
873
|
handler: async (args, ctx) => {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
ctx.ui.notify("No conversation history to hand off", "warning");
|
|
874
|
+
if (!ctx.hasUI) {
|
|
875
|
+
ctx.ui.notify("handoff requires interactive mode", "error");
|
|
814
876
|
return;
|
|
815
877
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const
|
|
832
|
-
|
|
833
|
-
ctx.ui.
|
|
878
|
+
if (!ctx.model) {
|
|
879
|
+
ctx.ui.notify("No model selected", "error");
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const goal = args.trim();
|
|
883
|
+
if (!goal) {
|
|
884
|
+
ctx.ui.notify("Usage: /handoff <goal for new thread>", "error");
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const messages = ctx.sessionManager.getBranch().filter((entry) => entry.type === "message").map((entry) => entry.message);
|
|
888
|
+
if (messages.length === 0) {
|
|
889
|
+
ctx.ui.notify("No conversation to hand off", "error");
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const conversation_text = serializeConversation(convertToLlm(messages));
|
|
893
|
+
const current_session_file = ctx.sessionManager.getSessionFile();
|
|
894
|
+
const model = ctx.model;
|
|
895
|
+
const result = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
896
|
+
const loader = new BorderedLoader(tui, theme, "Generating handoff prompt...");
|
|
897
|
+
loader.onAbort = () => done(null);
|
|
898
|
+
const generate = async () => {
|
|
899
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
900
|
+
if (!auth.ok || !auth.apiKey) throw new Error(auth.ok ? `No API key for ${model.provider}` : auth.error);
|
|
901
|
+
const response = await complete(model, {
|
|
902
|
+
systemPrompt: SYSTEM_PROMPT$1,
|
|
903
|
+
messages: [{
|
|
904
|
+
role: "user",
|
|
905
|
+
content: [{
|
|
906
|
+
type: "text",
|
|
907
|
+
text: `## Conversation History\n\n${conversation_text}\n\n## User's Goal for New Thread\n\n${goal}`
|
|
908
|
+
}],
|
|
909
|
+
timestamp: Date.now()
|
|
910
|
+
}]
|
|
911
|
+
}, {
|
|
912
|
+
apiKey: auth.apiKey,
|
|
913
|
+
headers: auth.headers,
|
|
914
|
+
signal: loader.signal
|
|
915
|
+
});
|
|
916
|
+
if (response.stopReason === "aborted") return null;
|
|
917
|
+
return response.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
918
|
+
};
|
|
919
|
+
generate().then(done).catch((err) => {
|
|
920
|
+
console.error("Handoff generation failed:", err);
|
|
921
|
+
done(null);
|
|
922
|
+
});
|
|
923
|
+
return loader;
|
|
924
|
+
});
|
|
925
|
+
if (result === null) {
|
|
926
|
+
ctx.ui.notify("Cancelled", "info");
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const edited_prompt = await ctx.ui.editor("Edit handoff prompt", result);
|
|
930
|
+
if (edited_prompt === void 0) {
|
|
931
|
+
ctx.ui.notify("Cancelled", "info");
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if ((await ctx.newSession({ parentSession: current_session_file })).cancelled) {
|
|
935
|
+
ctx.ui.notify("New session cancelled", "info");
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
ctx.ui.setEditorText(edited_prompt);
|
|
939
|
+
ctx.ui.notify("Handoff ready. Submit when ready.", "info");
|
|
834
940
|
}
|
|
835
941
|
});
|
|
836
942
|
}
|
|
837
943
|
//#endregion
|
|
944
|
+
//#region src/extensions/hooks-resolution.ts
|
|
945
|
+
const HOOK_TIMEOUT_MS = 600 * 1e3;
|
|
946
|
+
function is_file(path) {
|
|
947
|
+
try {
|
|
948
|
+
return statSync(path).isFile();
|
|
949
|
+
} catch {
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function as_record(value) {
|
|
954
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
955
|
+
return value;
|
|
956
|
+
}
|
|
957
|
+
function walk_up_directories(start_dir, stop_dir) {
|
|
958
|
+
const directories = [];
|
|
959
|
+
const has_stop_dir = stop_dir !== void 0;
|
|
960
|
+
let current = resolve(start_dir);
|
|
961
|
+
let parent = dirname(current);
|
|
962
|
+
let reached_stop_dir = has_stop_dir && current === stop_dir;
|
|
963
|
+
let reached_filesystem_root = parent === current;
|
|
964
|
+
directories.push(current);
|
|
965
|
+
while (!reached_stop_dir && !reached_filesystem_root) {
|
|
966
|
+
current = parent;
|
|
967
|
+
parent = dirname(current);
|
|
968
|
+
reached_stop_dir = has_stop_dir && current === stop_dir;
|
|
969
|
+
reached_filesystem_root = parent === current;
|
|
970
|
+
directories.push(current);
|
|
971
|
+
}
|
|
972
|
+
return directories;
|
|
973
|
+
}
|
|
974
|
+
function find_nearest_git_root(start_dir) {
|
|
975
|
+
for (const directory of walk_up_directories(start_dir)) if (existsSync(join(directory, ".git"))) return directory;
|
|
976
|
+
}
|
|
977
|
+
function has_hooks_config(directory) {
|
|
978
|
+
return is_file(join(directory, ".claude", "settings.json")) || is_file(join(directory, ".rulesync", "hooks.json")) || is_file(join(directory, ".pi", "hooks.json"));
|
|
979
|
+
}
|
|
980
|
+
function find_project_dir(cwd) {
|
|
981
|
+
const git_root = find_nearest_git_root(cwd);
|
|
982
|
+
for (const directory of walk_up_directories(cwd, git_root)) if (has_hooks_config(directory)) return directory;
|
|
983
|
+
return git_root ?? resolve(cwd);
|
|
984
|
+
}
|
|
985
|
+
function read_json_file(path) {
|
|
986
|
+
if (!is_file(path)) return void 0;
|
|
987
|
+
try {
|
|
988
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
989
|
+
} catch {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function resolve_hook_command(command, project_dir) {
|
|
994
|
+
return command.replace(/\$CLAUDE_PROJECT_DIR\b/g, project_dir);
|
|
995
|
+
}
|
|
996
|
+
function compile_matcher(matcher_text) {
|
|
997
|
+
if (matcher_text === void 0) return void 0;
|
|
998
|
+
try {
|
|
999
|
+
return new RegExp(matcher_text);
|
|
1000
|
+
} catch {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function create_hook(event_name, matcher_text, command, source, project_dir) {
|
|
1005
|
+
const matcher = compile_matcher(matcher_text);
|
|
1006
|
+
if (matcher_text !== void 0 && matcher === void 0) return void 0;
|
|
1007
|
+
return {
|
|
1008
|
+
event_name,
|
|
1009
|
+
matcher,
|
|
1010
|
+
matcher_text,
|
|
1011
|
+
command: resolve_hook_command(command, project_dir),
|
|
1012
|
+
source
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function get_hook_entries(hooks_record, event_name) {
|
|
1016
|
+
const keys = event_name === "PostToolUse" ? ["PostToolUse", "postToolUse"] : ["PostToolUseFailure", "postToolUseFailure"];
|
|
1017
|
+
for (const key of keys) {
|
|
1018
|
+
const value = hooks_record[key];
|
|
1019
|
+
if (Array.isArray(value)) return value;
|
|
1020
|
+
}
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
function parse_claude_settings_hooks(config, source, project_dir) {
|
|
1024
|
+
const root = as_record(config);
|
|
1025
|
+
const hooks_root = root ? as_record(root.hooks) : void 0;
|
|
1026
|
+
if (!hooks_root) return [];
|
|
1027
|
+
const hooks = [];
|
|
1028
|
+
for (const event_name of ["PostToolUse", "PostToolUseFailure"]) {
|
|
1029
|
+
const entries = get_hook_entries(hooks_root, event_name);
|
|
1030
|
+
for (const entry of entries) {
|
|
1031
|
+
const entry_record = as_record(entry);
|
|
1032
|
+
if (!entry_record || !Array.isArray(entry_record.hooks)) continue;
|
|
1033
|
+
const matcher_text = typeof entry_record.matcher === "string" ? entry_record.matcher : void 0;
|
|
1034
|
+
for (const nested_hook of entry_record.hooks) {
|
|
1035
|
+
const nested_record = as_record(nested_hook);
|
|
1036
|
+
if (!nested_record) continue;
|
|
1037
|
+
if (nested_record.type !== "command") continue;
|
|
1038
|
+
if (typeof nested_record.command !== "string") continue;
|
|
1039
|
+
const hook = create_hook(event_name, matcher_text, nested_record.command, source, project_dir);
|
|
1040
|
+
if (hook) hooks.push(hook);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return hooks;
|
|
1045
|
+
}
|
|
1046
|
+
function parse_simple_hooks_file(config, source, project_dir) {
|
|
1047
|
+
const root = as_record(config);
|
|
1048
|
+
const hooks_root = root ? as_record(root.hooks) : void 0;
|
|
1049
|
+
if (!hooks_root) return [];
|
|
1050
|
+
const hooks = [];
|
|
1051
|
+
for (const event_name of ["PostToolUse", "PostToolUseFailure"]) {
|
|
1052
|
+
const entries = get_hook_entries(hooks_root, event_name);
|
|
1053
|
+
for (const entry of entries) {
|
|
1054
|
+
const entry_record = as_record(entry);
|
|
1055
|
+
if (!entry_record || typeof entry_record.command !== "string") continue;
|
|
1056
|
+
const hook = create_hook(event_name, typeof entry_record.matcher === "string" ? entry_record.matcher : void 0, entry_record.command, source, project_dir);
|
|
1057
|
+
if (hook) hooks.push(hook);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return hooks;
|
|
1061
|
+
}
|
|
1062
|
+
function load_hooks(cwd) {
|
|
1063
|
+
const project_dir = find_project_dir(cwd);
|
|
1064
|
+
const hooks = [];
|
|
1065
|
+
const claude_settings_path = join(project_dir, ".claude", "settings.json");
|
|
1066
|
+
const rulesync_hooks_path = join(project_dir, ".rulesync", "hooks.json");
|
|
1067
|
+
const pi_hooks_path = join(project_dir, ".pi", "hooks.json");
|
|
1068
|
+
const claude_settings = read_json_file(claude_settings_path);
|
|
1069
|
+
if (claude_settings !== void 0) hooks.push(...parse_claude_settings_hooks(claude_settings, claude_settings_path, project_dir));
|
|
1070
|
+
const rulesync_hooks = read_json_file(rulesync_hooks_path);
|
|
1071
|
+
if (rulesync_hooks !== void 0) hooks.push(...parse_simple_hooks_file(rulesync_hooks, rulesync_hooks_path, project_dir));
|
|
1072
|
+
const pi_hooks = read_json_file(pi_hooks_path);
|
|
1073
|
+
if (pi_hooks !== void 0) hooks.push(...parse_simple_hooks_file(pi_hooks, pi_hooks_path, project_dir));
|
|
1074
|
+
return {
|
|
1075
|
+
project_dir,
|
|
1076
|
+
hooks
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function to_claude_tool_name(tool_name) {
|
|
1080
|
+
if (tool_name === "ls") return "LS";
|
|
1081
|
+
if (tool_name.length === 0) return tool_name;
|
|
1082
|
+
return tool_name[0].toUpperCase() + tool_name.slice(1);
|
|
1083
|
+
}
|
|
1084
|
+
function matches_hook(hook, tool_name) {
|
|
1085
|
+
if (!hook.matcher) return true;
|
|
1086
|
+
const claude_tool_name = to_claude_tool_name(tool_name);
|
|
1087
|
+
hook.matcher.lastIndex = 0;
|
|
1088
|
+
if (hook.matcher.test(tool_name)) return true;
|
|
1089
|
+
hook.matcher.lastIndex = 0;
|
|
1090
|
+
return hook.matcher.test(claude_tool_name);
|
|
1091
|
+
}
|
|
1092
|
+
function extract_text_content(content) {
|
|
1093
|
+
if (!Array.isArray(content)) return "";
|
|
1094
|
+
const parts = [];
|
|
1095
|
+
for (const item of content) {
|
|
1096
|
+
if (!item || typeof item !== "object") continue;
|
|
1097
|
+
const item_record = item;
|
|
1098
|
+
if (item_record.type === "text" && typeof item_record.text === "string") parts.push(item_record.text);
|
|
1099
|
+
}
|
|
1100
|
+
return parts.join("\n");
|
|
1101
|
+
}
|
|
1102
|
+
function normalize_tool_input(input) {
|
|
1103
|
+
const normalized = { ...input };
|
|
1104
|
+
const path_value = typeof input.path === "string" ? input.path : void 0;
|
|
1105
|
+
if (path_value !== void 0) {
|
|
1106
|
+
normalized.file_path = path_value;
|
|
1107
|
+
normalized.filePath = path_value;
|
|
1108
|
+
}
|
|
1109
|
+
return normalized;
|
|
1110
|
+
}
|
|
1111
|
+
function build_tool_response(event, normalized_input) {
|
|
1112
|
+
const response = {
|
|
1113
|
+
is_error: event.isError,
|
|
1114
|
+
isError: event.isError,
|
|
1115
|
+
content: event.content,
|
|
1116
|
+
text: extract_text_content(event.content),
|
|
1117
|
+
details: event.details ?? null
|
|
1118
|
+
};
|
|
1119
|
+
const file_path = typeof normalized_input.file_path === "string" ? normalized_input.file_path : void 0;
|
|
1120
|
+
if (file_path !== void 0) {
|
|
1121
|
+
response.file_path = file_path;
|
|
1122
|
+
response.filePath = file_path;
|
|
1123
|
+
}
|
|
1124
|
+
return response;
|
|
1125
|
+
}
|
|
1126
|
+
function build_hook_payload(event, event_name, ctx, project_dir) {
|
|
1127
|
+
const normalized_input = normalize_tool_input(event.input);
|
|
1128
|
+
return {
|
|
1129
|
+
session_id: ctx.sessionManager.getSessionFile() ?? "ephemeral",
|
|
1130
|
+
cwd: ctx.cwd,
|
|
1131
|
+
claude_project_dir: project_dir,
|
|
1132
|
+
hook_event_name: event_name,
|
|
1133
|
+
tool_name: to_claude_tool_name(event.toolName),
|
|
1134
|
+
tool_call_id: event.toolCallId,
|
|
1135
|
+
tool_input: normalized_input,
|
|
1136
|
+
tool_response: build_tool_response(event, normalized_input)
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
async function run_command_hook(command, cwd, payload) {
|
|
1140
|
+
return await new Promise((resolve) => {
|
|
1141
|
+
const started_at = Date.now();
|
|
1142
|
+
const child = spawn("bash", ["-lc", command], {
|
|
1143
|
+
cwd,
|
|
1144
|
+
env: {
|
|
1145
|
+
...process.env,
|
|
1146
|
+
CLAUDE_PROJECT_DIR: cwd
|
|
1147
|
+
},
|
|
1148
|
+
stdio: [
|
|
1149
|
+
"pipe",
|
|
1150
|
+
"pipe",
|
|
1151
|
+
"pipe"
|
|
1152
|
+
]
|
|
1153
|
+
});
|
|
1154
|
+
let stdout = "";
|
|
1155
|
+
let stderr = "";
|
|
1156
|
+
let timed_out = false;
|
|
1157
|
+
let resolved = false;
|
|
1158
|
+
const finish = (code) => {
|
|
1159
|
+
if (resolved) return;
|
|
1160
|
+
resolved = true;
|
|
1161
|
+
resolve({
|
|
1162
|
+
code,
|
|
1163
|
+
stdout,
|
|
1164
|
+
stderr,
|
|
1165
|
+
elapsed_ms: Date.now() - started_at,
|
|
1166
|
+
timed_out
|
|
1167
|
+
});
|
|
1168
|
+
};
|
|
1169
|
+
const timeout = setTimeout(() => {
|
|
1170
|
+
timed_out = true;
|
|
1171
|
+
child.kill("SIGTERM");
|
|
1172
|
+
setTimeout(() => {
|
|
1173
|
+
child.kill("SIGKILL");
|
|
1174
|
+
}, 1e3).unref?.();
|
|
1175
|
+
}, HOOK_TIMEOUT_MS);
|
|
1176
|
+
timeout.unref?.();
|
|
1177
|
+
child.stdout.on("data", (chunk) => {
|
|
1178
|
+
stdout += chunk.toString("utf8");
|
|
1179
|
+
});
|
|
1180
|
+
child.stderr.on("data", (chunk) => {
|
|
1181
|
+
stderr += chunk.toString("utf8");
|
|
1182
|
+
});
|
|
1183
|
+
child.on("error", (error) => {
|
|
1184
|
+
clearTimeout(timeout);
|
|
1185
|
+
stderr += `${error.message}\n`;
|
|
1186
|
+
finish(-1);
|
|
1187
|
+
});
|
|
1188
|
+
child.on("close", (code) => {
|
|
1189
|
+
clearTimeout(timeout);
|
|
1190
|
+
finish(code ?? -1);
|
|
1191
|
+
});
|
|
1192
|
+
try {
|
|
1193
|
+
child.stdin.write(JSON.stringify(payload));
|
|
1194
|
+
child.stdin.end();
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
stderr += `${error instanceof Error ? error.message : String(error)}\n`;
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
function hook_event_name_for_result(event) {
|
|
1201
|
+
return event.isError ? "PostToolUseFailure" : "PostToolUse";
|
|
1202
|
+
}
|
|
1203
|
+
function format_duration$1(elapsed_ms) {
|
|
1204
|
+
if (elapsed_ms < 1e3) return `${elapsed_ms}ms`;
|
|
1205
|
+
return `${(elapsed_ms / 1e3).toFixed(1)}s`;
|
|
1206
|
+
}
|
|
1207
|
+
function hook_name(command) {
|
|
1208
|
+
const sh_path_match = command.match(/[^\s|;&]+\.sh\b/);
|
|
1209
|
+
if (sh_path_match) return basename(sh_path_match[0]);
|
|
1210
|
+
return basename(command.trim().split(/\s+/)[0] ?? "hook");
|
|
1211
|
+
}
|
|
1212
|
+
function create_hooks_resolution_extension(options = {}) {
|
|
1213
|
+
const load_hooks_impl = options.load_hooks ?? load_hooks;
|
|
1214
|
+
const run_command_hook_impl = options.run_command_hook ?? run_command_hook;
|
|
1215
|
+
return async function hooks_resolution(pi) {
|
|
1216
|
+
let state = {
|
|
1217
|
+
project_dir: process.cwd(),
|
|
1218
|
+
hooks: []
|
|
1219
|
+
};
|
|
1220
|
+
const refresh_hooks = (cwd) => {
|
|
1221
|
+
state = load_hooks_impl(cwd);
|
|
1222
|
+
};
|
|
1223
|
+
pi.on("session_start", (_event, ctx) => {
|
|
1224
|
+
refresh_hooks(ctx.cwd);
|
|
1225
|
+
});
|
|
1226
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
1227
|
+
if (state.hooks.length === 0) return;
|
|
1228
|
+
const event_name = hook_event_name_for_result(event);
|
|
1229
|
+
const matching_hooks = state.hooks.filter((hook) => hook.event_name === event_name && matches_hook(hook, event.toolName));
|
|
1230
|
+
if (matching_hooks.length === 0) return;
|
|
1231
|
+
const payload = build_hook_payload(event, event_name, ctx, state.project_dir);
|
|
1232
|
+
const executed_commands = /* @__PURE__ */ new Set();
|
|
1233
|
+
for (const hook of matching_hooks) {
|
|
1234
|
+
if (executed_commands.has(hook.command)) continue;
|
|
1235
|
+
executed_commands.add(hook.command);
|
|
1236
|
+
const result = await run_command_hook_impl(hook.command, state.project_dir, payload);
|
|
1237
|
+
const name = hook_name(hook.command);
|
|
1238
|
+
const duration = format_duration$1(result.elapsed_ms);
|
|
1239
|
+
if (ctx.hasUI) if (result.code === 0) ctx.ui.notify(`Hook \`${name}\` ran (${duration})`, "info");
|
|
1240
|
+
else {
|
|
1241
|
+
const error_line = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
|
|
1242
|
+
ctx.ui.notify(`Hook \`${name}\` failed (${duration}): ${error_line}`, "warning");
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
var hooks_resolution_default = create_hooks_resolution_extension();
|
|
1249
|
+
//#endregion
|
|
838
1250
|
//#region src/lsp/client.ts
|
|
839
1251
|
var LspClientStartError = class extends Error {
|
|
840
1252
|
command;
|
|
@@ -3132,6 +3544,122 @@ Always pass \`--json\` for structured output.` };
|
|
|
3132
3544
|
});
|
|
3133
3545
|
}
|
|
3134
3546
|
//#endregion
|
|
3547
|
+
//#region src/extensions/session-name.ts
|
|
3548
|
+
const SYSTEM_PROMPT = `You are a session naming assistant. Given a conversation history, generate a short, descriptive session name (2-5 words) that captures the main topic or task.
|
|
3549
|
+
|
|
3550
|
+
Guidelines:
|
|
3551
|
+
- Be concise but specific
|
|
3552
|
+
- Use kebab-case or natural language
|
|
3553
|
+
- Focus on the core task/question
|
|
3554
|
+
- Avoid generic names like "discussion" or "conversation"
|
|
3555
|
+
- No quotes, no punctuation at the end
|
|
3556
|
+
|
|
3557
|
+
Examples:
|
|
3558
|
+
- "fix auth bug" -> "fix-auth-bug" or "authentication fix"
|
|
3559
|
+
- "how do I deploy to vercel" -> "vercel deployment"
|
|
3560
|
+
- "explain react hooks" -> "react hooks explanation"
|
|
3561
|
+
- "optimize database queries" -> "db query optimization"
|
|
3562
|
+
|
|
3563
|
+
Output ONLY the session name, nothing else.`;
|
|
3564
|
+
const AUTO_NAME_THRESHOLD = 1;
|
|
3565
|
+
const MAX_CHARS = 4e3;
|
|
3566
|
+
const MAX_NAME_LEN = 50;
|
|
3567
|
+
function clean_name(value) {
|
|
3568
|
+
return value.replace(/^["']|["']$/g, "").replace(/\n/g, " ").replace(/\s+/g, " ").trim().slice(0, MAX_NAME_LEN);
|
|
3569
|
+
}
|
|
3570
|
+
function truncate_conversation(value) {
|
|
3571
|
+
return value.length > MAX_CHARS ? value.slice(0, MAX_CHARS) + "\n..." : value;
|
|
3572
|
+
}
|
|
3573
|
+
async function generate_session_name(ctx, model, conversation_text, signal) {
|
|
3574
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
3575
|
+
if (!auth.ok || !auth.apiKey) throw new Error(auth.ok ? `No API key for ${model.provider}` : auth.error);
|
|
3576
|
+
const response = await complete(model, {
|
|
3577
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
3578
|
+
messages: [{
|
|
3579
|
+
role: "user",
|
|
3580
|
+
content: [{
|
|
3581
|
+
type: "text",
|
|
3582
|
+
text: `## Conversation History\n\n${truncate_conversation(conversation_text)}\n\nGenerate a concise session name for this conversation.`
|
|
3583
|
+
}],
|
|
3584
|
+
timestamp: Date.now()
|
|
3585
|
+
}]
|
|
3586
|
+
}, {
|
|
3587
|
+
apiKey: auth.apiKey,
|
|
3588
|
+
headers: auth.headers,
|
|
3589
|
+
signal
|
|
3590
|
+
});
|
|
3591
|
+
if (response.stopReason === "aborted") return null;
|
|
3592
|
+
return clean_name(response.content.filter((c) => c.type === "text").map((c) => c.text.trim()).join(" "));
|
|
3593
|
+
}
|
|
3594
|
+
async function session_name(pi) {
|
|
3595
|
+
let auto_named_attempted = false;
|
|
3596
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
3597
|
+
if (!ctx.hasUI || !ctx.model) return;
|
|
3598
|
+
if (pi.getSessionName() || auto_named_attempted) return;
|
|
3599
|
+
const branch = ctx.sessionManager.getBranch();
|
|
3600
|
+
if (branch.filter((entry) => entry.type === "message" && entry.message.role === "user").length < AUTO_NAME_THRESHOLD) return;
|
|
3601
|
+
auto_named_attempted = true;
|
|
3602
|
+
const messages = branch.filter((entry) => entry.type === "message").map((entry) => entry.message);
|
|
3603
|
+
if (messages.length === 0) return;
|
|
3604
|
+
const conversation_text = serializeConversation(convertToLlm(messages));
|
|
3605
|
+
generate_session_name(ctx, ctx.model, conversation_text).then((name) => {
|
|
3606
|
+
if (!name) return;
|
|
3607
|
+
pi.setSessionName(name);
|
|
3608
|
+
ctx.ui.notify(`Auto-named: ${name}`, "info");
|
|
3609
|
+
}).catch((err) => {
|
|
3610
|
+
console.error("Auto-naming failed:", err);
|
|
3611
|
+
});
|
|
3612
|
+
});
|
|
3613
|
+
pi.on("session_start", async () => {
|
|
3614
|
+
auto_named_attempted = false;
|
|
3615
|
+
});
|
|
3616
|
+
pi.registerCommand("session-name", {
|
|
3617
|
+
description: "Set, show, or auto-generate the current session name",
|
|
3618
|
+
handler: async (args, ctx) => {
|
|
3619
|
+
const trimmed = args.trim();
|
|
3620
|
+
if (!trimmed) {
|
|
3621
|
+
const current = pi.getSessionName();
|
|
3622
|
+
ctx.ui.notify(current ? `Session: ${current}` : "No session name set", "info");
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
if (trimmed === "--auto" || trimmed === "-a") {
|
|
3626
|
+
if (!ctx.hasUI || !ctx.model) {
|
|
3627
|
+
ctx.ui.notify("Auto-naming requires interactive mode and a selected model", "error");
|
|
3628
|
+
return;
|
|
3629
|
+
}
|
|
3630
|
+
const messages = ctx.sessionManager.getBranch().filter((entry) => entry.type === "message").map((entry) => entry.message);
|
|
3631
|
+
if (messages.length === 0) {
|
|
3632
|
+
ctx.ui.notify("No conversation to analyze", "error");
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
const conversation_text = serializeConversation(convertToLlm(messages));
|
|
3636
|
+
const result = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
3637
|
+
const loader = new BorderedLoader(tui, theme, "Generating session name...");
|
|
3638
|
+
loader.onAbort = () => done(null);
|
|
3639
|
+
generate_session_name(ctx, ctx.model, conversation_text, loader.signal).then(done).catch((err) => {
|
|
3640
|
+
console.error("Auto-naming failed:", err);
|
|
3641
|
+
done(null);
|
|
3642
|
+
});
|
|
3643
|
+
return loader;
|
|
3644
|
+
});
|
|
3645
|
+
if (result === null) {
|
|
3646
|
+
ctx.ui.notify("Auto-naming cancelled", "info");
|
|
3647
|
+
return;
|
|
3648
|
+
}
|
|
3649
|
+
if (!result) {
|
|
3650
|
+
ctx.ui.notify("Failed to generate name", "error");
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
pi.setSessionName(result);
|
|
3654
|
+
ctx.ui.notify(`Session named: ${result}`, "info");
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
pi.setSessionName(clean_name(trimmed));
|
|
3658
|
+
ctx.ui.notify(`Session named: ${clean_name(trimmed)}`, "info");
|
|
3659
|
+
}
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
//#endregion
|
|
3135
3663
|
//#region src/skills/config.ts
|
|
3136
3664
|
const DEFAULT_CONFIG$1 = {
|
|
3137
3665
|
version: 1,
|
|
@@ -4480,7 +5008,10 @@ const BUILTIN_EXTENSION_FACTORIES = {
|
|
|
4480
5008
|
handoff,
|
|
4481
5009
|
recall,
|
|
4482
5010
|
"prompt-presets": prompt_presets,
|
|
4483
|
-
lsp: lsp_default
|
|
5011
|
+
lsp: lsp_default,
|
|
5012
|
+
"session-name": session_name,
|
|
5013
|
+
"confirm-destructive": confirm_destructive,
|
|
5014
|
+
"hooks-resolution": hooks_resolution_default
|
|
4484
5015
|
};
|
|
4485
5016
|
const PACKAGE_THEME_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "themes");
|
|
4486
5017
|
const PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
@@ -4497,6 +5028,9 @@ function get_force_disabled_builtins(options) {
|
|
|
4497
5028
|
if (!options.recall) force_disabled.add("recall");
|
|
4498
5029
|
if (!options.prompt_presets) force_disabled.add("prompt-presets");
|
|
4499
5030
|
if (!options.lsp) force_disabled.add("lsp");
|
|
5031
|
+
if (!options.session_name) force_disabled.add("session-name");
|
|
5032
|
+
if (!options.confirm_destructive) force_disabled.add("confirm-destructive");
|
|
5033
|
+
if (!options.hooks_resolution) force_disabled.add("hooks-resolution");
|
|
4500
5034
|
return force_disabled;
|
|
4501
5035
|
}
|
|
4502
5036
|
function create_builtin_extension_factory(key, extension, force_disabled) {
|
|
@@ -4518,7 +5052,7 @@ function create_extensions_override(managed_inline_paths) {
|
|
|
4518
5052
|
};
|
|
4519
5053
|
}
|
|
4520
5054
|
async function create_my_pi(options = {}) {
|
|
4521
|
-
const { cwd = process.cwd(), agent_dir, extensions = [], extensionFactories: user_factories = [], mcp = true, skills = true, chain = true, filter_output = true, handoff = true, recall = true, prompt_presets = true, lsp = true, telemetry, telemetry_db_path, model, system_prompt, append_system_prompt } = options;
|
|
5055
|
+
const { cwd = process.cwd(), agent_dir, extensions = [], extensionFactories: user_factories = [], mcp = true, skills = true, chain = true, filter_output = true, handoff = true, recall = true, prompt_presets = true, lsp = true, session_name = true, confirm_destructive = true, hooks_resolution = true, telemetry, telemetry_db_path, model, system_prompt, append_system_prompt } = options;
|
|
4522
5056
|
const effective_agent_dir = resolve_agent_dir(cwd, agent_dir);
|
|
4523
5057
|
if (agent_dir) process.env[PI_AGENT_DIR_ENV] = effective_agent_dir;
|
|
4524
5058
|
const resolved_extensions = extensions.map((p) => resolve(cwd, p));
|
|
@@ -4530,7 +5064,10 @@ async function create_my_pi(options = {}) {
|
|
|
4530
5064
|
handoff,
|
|
4531
5065
|
recall,
|
|
4532
5066
|
prompt_presets,
|
|
4533
|
-
lsp
|
|
5067
|
+
lsp,
|
|
5068
|
+
session_name,
|
|
5069
|
+
confirm_destructive,
|
|
5070
|
+
hooks_resolution
|
|
4534
5071
|
});
|
|
4535
5072
|
const managed_extension_factories = [
|
|
4536
5073
|
create_telemetry_extension({
|
|
@@ -4588,4 +5125,4 @@ async function create_my_pi(options = {}) {
|
|
|
4588
5125
|
//#endregion
|
|
4589
5126
|
export { create_my_pi as n, runPrintMode$1 as r, InteractiveMode$1 as t };
|
|
4590
5127
|
|
|
4591
|
-
//# sourceMappingURL=api-
|
|
5128
|
+
//# sourceMappingURL=api-Dxi4curf.js.map
|