@phi-code-admin/phi-code 0.86.0 → 0.87.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/CHANGELOG.md +43 -0
- package/docs/fork-policy.md +18 -0
- package/extensions/phi/agents.ts +7 -4
- package/extensions/phi/benchmark.ts +129 -49
- package/extensions/phi/browser.ts +10 -37
- package/extensions/phi/btw/btw.ts +1 -7
- package/extensions/phi/chrome/index.ts +1283 -741
- package/extensions/phi/commit.ts +9 -14
- package/extensions/phi/goal/index.ts +10 -33
- package/extensions/phi/init.ts +37 -47
- package/extensions/phi/keys.ts +2 -7
- package/extensions/phi/mcp/callback-server.ts +162 -165
- package/extensions/phi/mcp/config.ts +122 -136
- package/extensions/phi/mcp/errors.ts +18 -23
- package/extensions/phi/mcp/index.ts +322 -355
- package/extensions/phi/mcp/oauth-provider.ts +289 -289
- package/extensions/phi/mcp/server-manager.ts +390 -413
- package/extensions/phi/mcp/tool-bridge.ts +381 -415
- package/extensions/phi/memory.ts +2 -2
- package/extensions/phi/models.ts +27 -26
- package/extensions/phi/orchestrator.ts +338 -229
- package/extensions/phi/productivity.ts +4 -2
- package/extensions/phi/providers/agent-def.ts +1 -1
- package/extensions/phi/providers/alibaba.ts +56 -7
- package/extensions/phi/providers/context-window.ts +4 -1
- package/extensions/phi/providers/live-models.ts +5 -20
- package/extensions/phi/providers/orchestrator-helpers.ts +19 -5
- package/extensions/phi/providers/phase-machine.ts +220 -0
- package/extensions/phi/setup.ts +196 -169
- package/extensions/phi/skill-loader.ts +14 -17
- package/extensions/phi/smart-router.ts +6 -6
- package/extensions/phi/web-search.ts +90 -50
- package/package.json +1 -1
package/extensions/phi/setup.ts
CHANGED
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
* - Storage chmod 0600 garantit la sécurité au repos
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
import { writeFile, mkdir } from "node:fs/promises";
|
|
24
|
-
import { join } from "node:path";
|
|
22
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
25
23
|
import { homedir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { ApiKeyStore, type ExtensionAPI, type ExtensionUIContext, getApiKeyStore, getConfigWatcher } from "phi-code";
|
|
26
26
|
import {
|
|
27
27
|
ALIBABA_ENV_VAR,
|
|
28
28
|
ALIBABA_MODELS,
|
|
@@ -32,16 +32,16 @@ import {
|
|
|
32
32
|
pingAlibaba,
|
|
33
33
|
validateAlibabaApiKey,
|
|
34
34
|
} from "./providers/alibaba.js";
|
|
35
|
+
import { fetchLiveModels, pingProvider, toPersistedModel } from "./providers/live-models.js";
|
|
35
36
|
import {
|
|
36
|
-
OPENCODE_GO_AUTH_URL,
|
|
37
|
-
OPENCODE_GO_ENV_VAR,
|
|
38
37
|
buildOpenCodeGoAnthropicProviderConfig,
|
|
39
38
|
buildOpenCodeGoProviderConfig,
|
|
40
39
|
getOpenCodeGoModels,
|
|
40
|
+
OPENCODE_GO_AUTH_URL,
|
|
41
|
+
OPENCODE_GO_ENV_VAR,
|
|
41
42
|
pingOpenCodeGo,
|
|
42
43
|
validateOpenCodeGoApiKey,
|
|
43
44
|
} from "./providers/opencode-go.js";
|
|
44
|
-
import { fetchLiveModels, pingProvider, toPersistedModel } from "./providers/live-models.js";
|
|
45
45
|
|
|
46
46
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
47
47
|
|
|
@@ -78,11 +78,41 @@ interface RoutingConfigOut {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const ORCHESTRATION_ROLES = [
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
{
|
|
82
|
+
key: "explore",
|
|
83
|
+
label: "Explore",
|
|
84
|
+
desc: "Read-only analysis",
|
|
85
|
+
agent: "explore",
|
|
86
|
+
keywords: ["read", "analyze", "explain", "understand", "find", "search", "look", "show", "what", "how"],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
key: "plan",
|
|
90
|
+
label: "Plan",
|
|
91
|
+
desc: "Architecture and design",
|
|
92
|
+
agent: "plan",
|
|
93
|
+
keywords: ["plan", "design", "architect", "spec", "structure", "organize", "strategy", "approach"],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: "code",
|
|
97
|
+
label: "Code",
|
|
98
|
+
desc: "Implementation",
|
|
99
|
+
agent: "code",
|
|
100
|
+
keywords: ["implement", "create", "build", "refactor", "write", "add", "modify", "update", "generate"],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: "test",
|
|
104
|
+
label: "Test",
|
|
105
|
+
desc: "Validation and tests",
|
|
106
|
+
agent: "test",
|
|
107
|
+
keywords: ["test", "verify", "validate", "check", "assert", "coverage"],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: "review",
|
|
111
|
+
label: "Review",
|
|
112
|
+
desc: "Quality and security review",
|
|
113
|
+
agent: "review",
|
|
114
|
+
keywords: ["review", "audit", "quality", "security", "improve", "optimize"],
|
|
115
|
+
},
|
|
86
116
|
] as const;
|
|
87
117
|
|
|
88
118
|
const DEBUG_KEYWORDS = ["fix", "bug", "error", "debug", "crash", "broken", "failing", "issue", "troubleshoot"];
|
|
@@ -226,10 +256,7 @@ function maskKeyForDisplay(key: string | undefined): string {
|
|
|
226
256
|
return ApiKeyStore.maskKey(key);
|
|
227
257
|
}
|
|
228
258
|
|
|
229
|
-
function buildRoutingConfig(
|
|
230
|
-
defaultModel: string,
|
|
231
|
-
orchestration: Record<string, RouteAssignment>,
|
|
232
|
-
): RoutingConfigOut {
|
|
259
|
+
function buildRoutingConfig(defaultModel: string, orchestration: Record<string, RouteAssignment>): RoutingConfigOut {
|
|
233
260
|
const routes: RoutingConfigOut["routes"] = {};
|
|
234
261
|
for (const role of ORCHESTRATION_ROLES) {
|
|
235
262
|
const a = orchestration[role.key] ?? { preferred: defaultModel, fallback: defaultModel };
|
|
@@ -258,7 +285,11 @@ async function writeRoutingConfig(routing: RoutingConfigOut): Promise<string> {
|
|
|
258
285
|
const dir = join(homedir(), ".phi", "agent");
|
|
259
286
|
await mkdir(dir, { recursive: true });
|
|
260
287
|
const path = join(dir, "routing.json");
|
|
261
|
-
await writeFile(
|
|
288
|
+
await writeFile(
|
|
289
|
+
path,
|
|
290
|
+
`${JSON.stringify({ ...routing, $schema: "./routing.schema.json", version: 1 }, null, 2)}\n`,
|
|
291
|
+
"utf-8",
|
|
292
|
+
);
|
|
262
293
|
return path;
|
|
263
294
|
}
|
|
264
295
|
|
|
@@ -427,10 +458,7 @@ async function configureGenericCloud(
|
|
|
427
458
|
]);
|
|
428
459
|
if (!authChoice || authChoice === "Cancel") return undefined;
|
|
429
460
|
if (authChoice.startsWith("OAuth")) {
|
|
430
|
-
ui.notify(
|
|
431
|
-
`Run \`/login ${provider.id}\` after setup to authenticate via OAuth.`,
|
|
432
|
-
"info",
|
|
433
|
-
);
|
|
461
|
+
ui.notify(`Run \`/login ${provider.id}\` after setup to authenticate via OAuth.`, "info");
|
|
434
462
|
store.setKey(provider.id, "$OAUTH", {
|
|
435
463
|
baseUrl: provider.baseUrl,
|
|
436
464
|
api: provider.api,
|
|
@@ -489,9 +517,8 @@ async function configureGenericCloud(
|
|
|
489
517
|
});
|
|
490
518
|
ui.setStatus("setup-fetch", undefined);
|
|
491
519
|
|
|
492
|
-
const models = (
|
|
493
|
-
? live.models
|
|
494
|
-
: provider.staticModels.map((id) => ({ id, name: id, reasoning: true }))
|
|
520
|
+
const models = (
|
|
521
|
+
live.models.length > 0 ? live.models : provider.staticModels.map((id) => ({ id, name: id, reasoning: true }))
|
|
495
522
|
).map(toPersistedModel);
|
|
496
523
|
|
|
497
524
|
watcher.muteForWrite("models_json_changed");
|
|
@@ -582,12 +609,12 @@ async function configureAssignments(
|
|
|
582
609
|
const orchestration: Record<string, RouteAssignment> = {};
|
|
583
610
|
for (const role of ORCHESTRATION_ROLES) {
|
|
584
611
|
const preferred =
|
|
585
|
-
(await pickModelFromCatalog(ui, `${role.label} - preferred model (${role.desc})`, models)) ??
|
|
586
|
-
models[0].ref;
|
|
612
|
+
(await pickModelFromCatalog(ui, `${role.label} - preferred model (${role.desc})`, models)) ?? models[0].ref;
|
|
587
613
|
const fallbackModels = models.filter((m) => m.ref !== preferred);
|
|
588
|
-
const fallback =
|
|
589
|
-
|
|
590
|
-
|
|
614
|
+
const fallback =
|
|
615
|
+
fallbackModels.length > 0
|
|
616
|
+
? ((await pickModelFromCatalog(ui, `${role.label} - fallback model`, fallbackModels)) ?? preferred)
|
|
617
|
+
: preferred;
|
|
591
618
|
orchestration[role.key] = { preferred, fallback };
|
|
592
619
|
ui.notify(` ${role.label}: ${preferred} / ${fallback}`, "info");
|
|
593
620
|
}
|
|
@@ -626,165 +653,165 @@ export default function setupExtension(pi: ExtensionAPI) {
|
|
|
626
653
|
}
|
|
627
654
|
|
|
628
655
|
try {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
656
|
+
ui.notify(
|
|
657
|
+
"**φ Phi Code Setup Wizard**\n\n" +
|
|
658
|
+
"This wizard configures providers and assigns models to **orchestration roles** " +
|
|
659
|
+
"(used by `/plan`).\n" +
|
|
660
|
+
"The **chat default model is controlled via `/model`** and stays sticky across " +
|
|
661
|
+
"prompts — this wizard will never change it.\n\n" +
|
|
662
|
+
"Keys are stored in `~/.phi/agent/models.json` (chmod 0600 on Unix). " +
|
|
663
|
+
"Edit that file directly later to hot-reload (no restart needed).",
|
|
664
|
+
"info",
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
const available = new Map<string, { source: "key" | "env" | "local"; modelCount: number }>();
|
|
668
|
+
const assignments: { default?: string; orchestration: Record<string, RouteAssignment> } = {
|
|
669
|
+
orchestration: {},
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const refreshAvailable = (): void => {
|
|
673
|
+
const catalog = getProviderCatalog();
|
|
674
|
+
available.clear();
|
|
675
|
+
for (const p of catalog) {
|
|
676
|
+
const stored = store.getProvider(p.id);
|
|
677
|
+
if (stored?.apiKey) {
|
|
678
|
+
const source = stored.apiKey === "$OAUTH" ? "key" : stored.apiKey === "local" ? "local" : "key";
|
|
679
|
+
const modelCount = Array.isArray(stored.models) ? stored.models.length : p.staticModels.length;
|
|
680
|
+
available.set(p.id, { source, modelCount });
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
if (!p.local && process.env[p.envVar]) {
|
|
684
|
+
available.set(p.id, { source: "env", modelCount: p.staticModels.length });
|
|
685
|
+
}
|
|
658
686
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
refreshAvailable();
|
|
664
|
-
|
|
665
|
-
let done = false;
|
|
666
|
-
while (!done) {
|
|
667
|
-
const catalog = getProviderCatalog();
|
|
668
|
-
const choices: string[] = [];
|
|
669
|
-
for (const p of catalog) {
|
|
670
|
-
const state = available.get(p.id);
|
|
671
|
-
const tag = state ? "[ok]" : "[--]";
|
|
672
|
-
const modelTag = state ? ` (${state.modelCount} models)` : "";
|
|
673
|
-
choices.push(`${tag} ${p.displayName}${modelTag}`);
|
|
674
|
-
}
|
|
675
|
-
choices.push("---");
|
|
676
|
-
choices.push("Assign models to agent roles");
|
|
677
|
-
choices.push("Finish and save");
|
|
678
|
-
choices.push("Quit without saving");
|
|
679
|
-
|
|
680
|
-
const action = await ui.select("Phi Code Setup - choose an action", choices);
|
|
681
|
-
if (!action) {
|
|
682
|
-
done = true;
|
|
683
|
-
break;
|
|
684
|
-
}
|
|
687
|
+
ui.setWidget("setup-status", buildStatusWidget(store, available, assignments));
|
|
688
|
+
};
|
|
685
689
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
690
|
+
refreshAvailable();
|
|
691
|
+
|
|
692
|
+
let done = false;
|
|
693
|
+
while (!done) {
|
|
694
|
+
const catalog = getProviderCatalog();
|
|
695
|
+
const choices: string[] = [];
|
|
696
|
+
for (const p of catalog) {
|
|
697
|
+
const state = available.get(p.id);
|
|
698
|
+
const tag = state ? "[ok]" : "[--]";
|
|
699
|
+
const modelTag = state ? ` (${state.modelCount} models)` : "";
|
|
700
|
+
choices.push(`${tag} ${p.displayName}${modelTag}`);
|
|
701
|
+
}
|
|
702
|
+
choices.push("---");
|
|
703
|
+
choices.push("Assign models to agent roles");
|
|
704
|
+
choices.push("Finish and save");
|
|
705
|
+
choices.push("Quit without saving");
|
|
706
|
+
|
|
707
|
+
const action = await ui.select("Phi Code Setup - choose an action", choices);
|
|
708
|
+
if (!action) {
|
|
709
|
+
done = true;
|
|
710
|
+
break;
|
|
692
711
|
}
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
712
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
"
|
|
700
|
-
"You have not assigned all roles. Save current state anyway?",
|
|
713
|
+
if (action === "Quit without saving") {
|
|
714
|
+
const confirm = await ui.confirm(
|
|
715
|
+
"Quit",
|
|
716
|
+
"Discard all in-memory assignments? (provider keys already saved are kept)",
|
|
701
717
|
);
|
|
702
|
-
if (
|
|
718
|
+
if (confirm) {
|
|
719
|
+
ui.setWidget("setup-status", undefined);
|
|
720
|
+
ui.notify("Setup cancelled (saved provider keys are unchanged).", "warning");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
continue;
|
|
703
724
|
}
|
|
704
|
-
done = true;
|
|
705
|
-
break;
|
|
706
|
-
}
|
|
707
725
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
for (const m of models) {
|
|
716
|
-
const id = (m as { id?: string }).id;
|
|
717
|
-
if (typeof id !== "string") continue;
|
|
718
|
-
// Provider-qualified ref so the same id from different providers stays distinct.
|
|
719
|
-
const ref = `${p.id}/${id}`;
|
|
720
|
-
if (seenRefs.has(ref)) continue;
|
|
721
|
-
seenRefs.add(ref);
|
|
722
|
-
allModels.push({ ref, display: `${id} [${p.id}]` });
|
|
726
|
+
if (action === "Finish and save") {
|
|
727
|
+
if (!assignments.default || Object.keys(assignments.orchestration).length === 0) {
|
|
728
|
+
const proceed = await ui.confirm(
|
|
729
|
+
"Assignments incomplete",
|
|
730
|
+
"You have not assigned all roles. Save current state anyway?",
|
|
731
|
+
);
|
|
732
|
+
if (!proceed) continue;
|
|
723
733
|
}
|
|
734
|
+
done = true;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (action === "Assign models to agent roles") {
|
|
739
|
+
const allModels: Array<{ ref: string; display: string }> = [];
|
|
740
|
+
const seenRefs = new Set<string>();
|
|
741
|
+
for (const p of catalog) {
|
|
742
|
+
const stored = store.getProvider(p.id);
|
|
743
|
+
if (!stored) continue;
|
|
744
|
+
const models = Array.isArray(stored.models) ? stored.models : [];
|
|
745
|
+
for (const m of models) {
|
|
746
|
+
const id = (m as { id?: string }).id;
|
|
747
|
+
if (typeof id !== "string") continue;
|
|
748
|
+
// Provider-qualified ref so the same id from different providers stays distinct.
|
|
749
|
+
const ref = `${p.id}/${id}`;
|
|
750
|
+
if (seenRefs.has(ref)) continue;
|
|
751
|
+
seenRefs.add(ref);
|
|
752
|
+
allModels.push({ ref, display: `${id} [${p.id}]` });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const { defaultModel, orchestration } = await configureAssignments(ui, allModels);
|
|
756
|
+
assignments.default = defaultModel;
|
|
757
|
+
assignments.orchestration = orchestration;
|
|
758
|
+
refreshAvailable();
|
|
759
|
+
continue;
|
|
724
760
|
}
|
|
725
|
-
const { defaultModel, orchestration } = await configureAssignments(ui, allModels);
|
|
726
|
-
assignments.default = defaultModel;
|
|
727
|
-
assignments.orchestration = orchestration;
|
|
728
|
-
refreshAvailable();
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
761
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
762
|
+
if (action === "---") continue;
|
|
763
|
+
|
|
764
|
+
const providerIndex = choices.indexOf(action);
|
|
765
|
+
if (providerIndex < 0 || providerIndex >= catalog.length) continue;
|
|
766
|
+
const provider = catalog[providerIndex];
|
|
767
|
+
|
|
768
|
+
let result: { providerId: string; modelCount: number } | undefined;
|
|
769
|
+
try {
|
|
770
|
+
if (provider.id === "alibaba-codingplan") {
|
|
771
|
+
result = await configureAlibaba(ui, store);
|
|
772
|
+
} else if (provider.id === "opencode-go") {
|
|
773
|
+
result = await configureOpenCodeGo(ui, store);
|
|
774
|
+
} else if (provider.local) {
|
|
775
|
+
result = await configureLocal(ui, store, provider);
|
|
776
|
+
} else {
|
|
777
|
+
result = await configureGenericCloud(ui, store, provider);
|
|
778
|
+
}
|
|
779
|
+
} catch (err) {
|
|
780
|
+
ui.notify(
|
|
781
|
+
`Provider configuration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
782
|
+
"error",
|
|
783
|
+
);
|
|
748
784
|
}
|
|
749
|
-
} catch (err) {
|
|
750
|
-
ui.notify(
|
|
751
|
-
`Provider configuration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
752
|
-
"error",
|
|
753
|
-
);
|
|
754
|
-
}
|
|
755
785
|
|
|
756
|
-
|
|
757
|
-
|
|
786
|
+
if (result) refreshAvailable();
|
|
787
|
+
}
|
|
758
788
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
789
|
+
// Save routing if assignments were made
|
|
790
|
+
if (assignments.default) {
|
|
791
|
+
try {
|
|
792
|
+
const routing = buildRoutingConfig(assignments.default, assignments.orchestration);
|
|
793
|
+
const routingPath = await writeRoutingConfig(routing);
|
|
794
|
+
ui.notify(`Routing config written to \`${routingPath}\`.`, "info");
|
|
795
|
+
} catch (err) {
|
|
796
|
+
ui.notify(`Failed to write routing.json: ${err}`, "error");
|
|
797
|
+
}
|
|
767
798
|
}
|
|
768
|
-
}
|
|
769
799
|
|
|
770
|
-
ui.setWidget("setup-status", undefined);
|
|
771
|
-
ui.notify(
|
|
772
|
-
"**Setup complete.**\n\n" +
|
|
773
|
-
"Next steps:\n" +
|
|
774
|
-
" - `/keys` to list/manage saved keys\n" +
|
|
775
|
-
" - `/models refresh` to re-fetch the catalog from each provider's API\n" +
|
|
776
|
-
" - `/routing` to inspect routing\n" +
|
|
777
|
-
" - `/agents` to list sub-agents\n" +
|
|
778
|
-
" - `/skills` to list skills\n" +
|
|
779
|
-
" - Edit `~/.phi/agent/models.json` or `routing.json` directly: hot-reload kicks in",
|
|
780
|
-
"info",
|
|
781
|
-
);
|
|
782
|
-
} catch (err) {
|
|
783
800
|
ui.setWidget("setup-status", undefined);
|
|
784
801
|
ui.notify(
|
|
785
|
-
|
|
786
|
-
|
|
802
|
+
"**Setup complete.**\n\n" +
|
|
803
|
+
"Next steps:\n" +
|
|
804
|
+
" - `/keys` to list/manage saved keys\n" +
|
|
805
|
+
" - `/models refresh` to re-fetch the catalog from each provider's API\n" +
|
|
806
|
+
" - `/routing` to inspect routing\n" +
|
|
807
|
+
" - `/agents` to list sub-agents\n" +
|
|
808
|
+
" - `/skills` to list skills\n" +
|
|
809
|
+
" - Edit `~/.phi/agent/models.json` or `routing.json` directly: hot-reload kicks in",
|
|
810
|
+
"info",
|
|
787
811
|
);
|
|
812
|
+
} catch (err) {
|
|
813
|
+
ui.setWidget("setup-status", undefined);
|
|
814
|
+
ui.notify(`Setup wizard error: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
788
815
|
}
|
|
789
816
|
},
|
|
790
817
|
});
|
|
@@ -20,22 +20,19 @@
|
|
|
20
20
|
* - /skills command to list available skills
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import type { ExtensionAPI } from "phi-code";
|
|
24
|
-
import { SkillScanner, SkillLoader } from "sigma-skills";
|
|
25
|
-
import type { SkillsConfig } from "sigma-skills";
|
|
26
23
|
import { existsSync } from "node:fs";
|
|
27
|
-
import { join } from "node:path";
|
|
28
24
|
import { homedir } from "node:os";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import type { ExtensionAPI } from "phi-code";
|
|
27
|
+
import type { SkillsConfig } from "sigma-skills";
|
|
28
|
+
import { SkillLoader, SkillScanner } from "sigma-skills";
|
|
29
29
|
|
|
30
30
|
export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
31
31
|
// Bundled skills live at <package>/skills in the repo layout (two hops up
|
|
32
32
|
// from extensions/phi/); postinstall copies them to ~/.phi/agent/skills
|
|
33
33
|
// (== globalDir) in the installed layout. Probe both (the scanner dedupes
|
|
34
34
|
// by skill name).
|
|
35
|
-
const bundledCandidates = [
|
|
36
|
-
join(__dirname, "..", "..", "skills"),
|
|
37
|
-
join(homedir(), ".phi", "agent", "skills"),
|
|
38
|
-
];
|
|
35
|
+
const bundledCandidates = [join(__dirname, "..", "..", "skills"), join(homedir(), ".phi", "agent", "skills")];
|
|
39
36
|
const config: SkillsConfig = {
|
|
40
37
|
globalDir: join(homedir(), ".phi", "agent", "skills"),
|
|
41
38
|
projectDir: join(process.cwd(), ".phi", "skills"),
|
|
@@ -103,10 +100,10 @@ export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
|
103
100
|
if (skills.length === 0) {
|
|
104
101
|
ctx.ui.notify(
|
|
105
102
|
"No skills found. Create skill directories with SKILL.md files in:\n" +
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"info"
|
|
103
|
+
`- \`${config.projectDir}\` (project-local)\n` +
|
|
104
|
+
`- \`${config.globalDir}\` (global)\n` +
|
|
105
|
+
"Or install bundled skills via `/phi-init`.",
|
|
106
|
+
"info",
|
|
110
107
|
);
|
|
111
108
|
return;
|
|
112
109
|
}
|
|
@@ -122,13 +119,13 @@ export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
|
122
119
|
// Show specific skill
|
|
123
120
|
const content = loader.getSkillContext(query);
|
|
124
121
|
if (content) {
|
|
125
|
-
const skill = loader.listSkills().find(s => s.name === query);
|
|
122
|
+
const skill = loader.listSkills().find((s) => s.name === query);
|
|
126
123
|
ctx.ui.notify(
|
|
127
124
|
`**📚 Skill: ${query}**\n\n` +
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"info"
|
|
125
|
+
`Path: \`${skill?.path || "unknown"}\`\n` +
|
|
126
|
+
`Keywords: ${skill?.keywords.slice(0, 10).join(", ") || "none"}\n\n` +
|
|
127
|
+
`---\n\n${content.slice(0, 2000)}${content.length > 2000 ? "\n\n... (truncated, use `read` for full content)" : ""}`,
|
|
128
|
+
"info",
|
|
132
129
|
);
|
|
133
130
|
} else {
|
|
134
131
|
ctx.ui.notify(`Skill "${query}" not found. Use \`/skills\` to list available skills.`, "warning");
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
* Command: /routing — show config, enable/disable, test, reload
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
11
14
|
import type { ExtensionAPI } from "phi-code";
|
|
15
|
+
import type { RoutingConfig } from "sigma-agents";
|
|
12
16
|
import { SmartRouter } from "sigma-agents";
|
|
13
|
-
import type { RoutingConfig, TaskCategory } from "sigma-agents";
|
|
14
|
-
import { readFile, mkdir, writeFile, access } from "node:fs/promises";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
import { homedir } from "node:os";
|
|
17
17
|
|
|
18
18
|
// ─── Extension Config ────────────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -40,7 +40,7 @@ export default function smartRouterExtension(pi: ExtensionAPI) {
|
|
|
40
40
|
const configPath = join(configDir, "routing.json");
|
|
41
41
|
|
|
42
42
|
let router = new SmartRouter(SmartRouter.defaultConfig());
|
|
43
|
-
|
|
43
|
+
const extConfig: ExtensionConfig = {
|
|
44
44
|
enabled: true,
|
|
45
45
|
autoSwitch: false,
|
|
46
46
|
notifyOnRecommendation: false,
|
|
@@ -70,7 +70,7 @@ export default function smartRouterExtension(pi: ExtensionAPI) {
|
|
|
70
70
|
* If the preferred model exists in the registry, use it.
|
|
71
71
|
* Otherwise, fall back to the current model.
|
|
72
72
|
*/
|
|
73
|
-
function
|
|
73
|
+
function _resolveModel(preferredModel: string, ctx: any): string {
|
|
74
74
|
try {
|
|
75
75
|
const available = ctx.modelRegistry?.getAvailable?.() || [];
|
|
76
76
|
if (available.some((m: any) => m.id === preferredModel)) {
|