@phi-code-admin/phi-code 0.59.9 → 0.60.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.
@@ -608,104 +608,127 @@ _Edit this file to customize Phi Code's behavior for your project._
608
608
  ctx.ui.notify("║ Φ Phi Code Setup Wizard ║", "info");
609
609
  ctx.ui.notify("╚══════════════════════════════════════╝\n", "info");
610
610
 
611
- // 1. Detect API keys and local providers
612
- ctx.ui.notify("🔍 Detecting providers...", "info");
611
+ // 1. Detect providers
612
+ ctx.ui.notify("🔍 Detecting providers...\n", "info");
613
613
  const providers = detectProviders();
614
614
 
615
+ // Also check models.json for previously configured providers
616
+ const modelsJsonPath = join(agentDir, "models.json");
617
+ try {
618
+ const mjContent = await readFile(modelsJsonPath, "utf-8");
619
+ const mjConfig = JSON.parse(mjContent);
620
+ if (mjConfig.providers) {
621
+ for (const [id, config] of Object.entries<any>(mjConfig.providers)) {
622
+ // Mark provider as available if it has an API key in models.json
623
+ if (config.apiKey) {
624
+ const match = providers.find(p =>
625
+ id.includes(p.name.toLowerCase().split(" ")[0]) ||
626
+ p.name.toLowerCase().replace(/\s+/g, "-") === id
627
+ );
628
+ if (match) {
629
+ match.available = true;
630
+ if (config.models?.length > 0) {
631
+ match.models = config.models.map((m: any) => m.id || m);
632
+ }
633
+ }
634
+ }
635
+ }
636
+ }
637
+ } catch { /* no models.json yet */ }
638
+
615
639
  // Probe local providers (Ollama, LM Studio)
616
- ctx.ui.notify("🔍 Probing local model servers...", "info");
617
640
  await detectLocalProviders(providers);
618
641
 
619
642
  let available = providers.filter(p => p.available);
643
+ const cloudConfigured = available.filter(p => !p.local);
620
644
 
621
- // If no providers found, offer interactive API key setup
622
- if (available.length === 0) {
623
- ctx.ui.notify("⚠️ No API keys detected. Let's set one up!\n", "info");
624
- ctx.ui.notify("**Available providers:**", "info");
625
- const cloudProviders = providers.filter(p => !p.local);
626
- cloudProviders.forEach((p, i) => {
627
- ctx.ui.notify(` ${i + 1}. ${p.name}`, "info");
628
- });
629
- ctx.ui.notify(` ${cloudProviders.length + 1}. Ollama (local)`, "info");
630
- ctx.ui.notify(` ${cloudProviders.length + 2}. LM Studio (local)\n`, "info");
645
+ // Always show provider status and offer to add/change
646
+ ctx.ui.notify("**Provider Status:**", "info");
647
+ for (const p of providers) {
648
+ const status = p.available ? "✅" : "";
649
+ const tag = p.local ? " (local)" : "";
650
+ const modelCount = p.available ? ` — ${p.models.length} model(s)` : "";
651
+ ctx.ui.notify(` ${status} ${p.name}${tag}${modelCount}`, "info");
652
+ }
653
+
654
+ // If no cloud providers have keys, prompt for setup
655
+ if (cloudConfigured.length === 0) {
656
+ ctx.ui.notify("\n⚠️ No cloud API keys configured.\n", "warning");
657
+ }
658
+
659
+ // Always offer to add a provider
660
+ const addProvider = await ctx.ui.input(
661
+ "Add/change a provider? (number, or Enter to skip)",
662
+ providers.map((p, i) => `${i+1}=${p.name}`).join(", ")
663
+ );
631
664
 
632
- const providerChoice = await ctx.ui.input(
633
- "Choose provider (number)",
634
- `1-${cloudProviders.length + 2}`
635
- );
636
- const choiceNum = parseInt(providerChoice ?? "0");
665
+ const choiceNum = parseInt(addProvider ?? "0");
666
+ if (choiceNum >= 1 && choiceNum <= providers.length) {
667
+ const chosen = providers[choiceNum - 1];
637
668
 
638
- if (choiceNum >= 1 && choiceNum <= cloudProviders.length) {
669
+ if (chosen.local) {
670
+ const port = chosen.name === "Ollama" ? 11434 : 1234;
671
+ if (!chosen.available) {
672
+ ctx.ui.notify(`\n💡 **${chosen.name}** — make sure it's running on port ${port}.`, "info");
673
+ ctx.ui.notify("Then restart phi and run `/phi-init` again.", "info");
674
+ return;
675
+ }
676
+ ctx.ui.notify(`\n✅ **${chosen.name}** is running with ${chosen.models.length} model(s).`, "info");
677
+ } else {
639
678
  // Cloud provider — ask for API key
640
- const chosen = cloudProviders[choiceNum - 1];
641
- ctx.ui.notify(`\n🔑 **${chosen.name}** selected.`, "info");
679
+ ctx.ui.notify(`\n🔑 **${chosen.name}**`, "info");
642
680
 
643
681
  const apiKey = await ctx.ui.input(
644
682
  `Enter your ${chosen.name} API key`,
645
- "sk-..."
683
+ "Paste your key here"
646
684
  );
647
685
 
648
686
  if (!apiKey || apiKey.trim().length < 5) {
649
- ctx.ui.notify("❌ Invalid API key. Cancelled.", "error");
650
- return;
687
+ ctx.ui.notify("❌ Invalid API key. Skipped.", "error");
688
+ } else {
689
+ // Save to models.json
690
+ let modelsConfig: any = { providers: {} };
691
+ try {
692
+ const existing = await readFile(modelsJsonPath, "utf-8");
693
+ modelsConfig = JSON.parse(existing);
694
+ } catch { /* new file */ }
695
+
696
+ const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
697
+ modelsConfig.providers[providerId] = {
698
+ baseUrl: chosen.baseUrl,
699
+ api: "openai-completions",
700
+ apiKey: apiKey.trim(),
701
+ models: chosen.models.map((id: string) => ({
702
+ id,
703
+ name: id,
704
+ reasoning: true,
705
+ input: ["text"],
706
+ contextWindow: 131072,
707
+ maxTokens: 16384,
708
+ })),
709
+ };
710
+
711
+ await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
712
+ process.env[chosen.envVar] = apiKey.trim();
713
+ chosen.available = true;
714
+
715
+ const masked = apiKey.trim().substring(0, 6) + "..." + apiKey.trim().slice(-4);
716
+ ctx.ui.notify(`✅ **${chosen.name}** saved (${masked})`, "info");
717
+ ctx.ui.notify(` ${chosen.models.length} models configured in \`models.json\``, "info");
651
718
  }
652
-
653
- // Save to models.json for persistence
654
- const modelsJsonPath = join(agentDir, "models.json");
655
- let modelsConfig: any = { providers: {} };
656
- try {
657
- const existing = await readFile(modelsJsonPath, "utf-8");
658
- modelsConfig = JSON.parse(existing);
659
- } catch { /* file doesn't exist yet */ }
660
-
661
- // Build provider config with correct provider ID
662
- const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
663
- modelsConfig.providers[providerId] = {
664
- baseUrl: chosen.baseUrl,
665
- api: "openai-completions",
666
- apiKey: apiKey.trim(),
667
- models: chosen.models.map((id: string) => ({
668
- id,
669
- name: id,
670
- reasoning: true,
671
- input: ["text"],
672
- contextWindow: 131072,
673
- maxTokens: 16384,
674
- })),
675
- };
676
-
677
- await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
678
-
679
- // Also set in current session
680
- process.env[chosen.envVar] = apiKey.trim();
681
- chosen.available = true;
682
-
683
- ctx.ui.notify(`\n✅ API key saved to \`~/.phi/agent/models.json\``, "info");
684
- ctx.ui.notify(` ${chosen.models.length} models configured.`, "info");
685
- ctx.ui.notify(` ⚠️ **Restart phi** for models to load.\n`, "warning");
686
- ctx.ui.notify("Run `phi` again, then `/phi-init` to complete setup.", "info");
687
- return;
688
-
689
- } else if (choiceNum === cloudProviders.length + 1 || choiceNum === cloudProviders.length + 2) {
690
- const localName = choiceNum === cloudProviders.length + 1 ? "Ollama" : "LM Studio";
691
- const port = choiceNum === cloudProviders.length + 1 ? 11434 : 1234;
692
- ctx.ui.notify(`\n💡 **${localName}** selected. Make sure it's running on port ${port}.`, "info");
693
- ctx.ui.notify(`Then restart phi and run /phi-init again.`, "info");
694
- return;
695
- } else {
696
- ctx.ui.notify("❌ Invalid choice. Cancelled.", "error");
697
- return;
698
719
  }
699
720
  }
700
721
 
701
- ctx.ui.notify(`✅ Found ${available.length} provider(s):`, "info");
702
- for (const p of available) {
703
- const tag = p.local ? " (local)" : "";
704
- ctx.ui.notify(` • ${p.name}${tag} — ${p.models.length} model(s)${p.local ? ": " + p.models.join(", ") : ""}`, "info");
722
+ // Re-check available after potential additions
723
+ available = providers.filter(p => p.available);
724
+
725
+ if (available.length === 0) {
726
+ ctx.ui.notify("\n❌ No providers available. Run `/phi-init` again after setting up a provider.", "error");
727
+ return;
705
728
  }
706
729
 
707
730
  const allModels = getAllAvailableModels(providers);
708
- ctx.ui.notify(` Total: ${allModels.length} models available\n`, "info");
731
+ ctx.ui.notify(`\n✅ **${allModels.length} models** available from ${available.length} provider(s).\n`, "info");
709
732
 
710
733
  // 2. Choose mode
711
734
  ctx.ui.notify("Choose setup mode:\n" +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.59.9",
3
+ "version": "0.60.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {