@phi-code-admin/phi-code 0.84.1 → 0.85.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 +71 -0
- package/README.md +3 -3
- package/agents/code.md +1 -0
- package/agents/explore.md +11 -5
- package/agents/plan.md +18 -3
- package/agents/review.md +18 -7
- package/agents/test.md +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +30 -28
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/version-check.d.ts +5 -5
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +13 -8
- package/dist/utils/version-check.js.map +1 -1
- package/docs/settings.md +2 -2
- package/docs/usage.md +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/extensions/phi/mcp/index.ts +10 -4
- package/extensions/phi/models.ts +170 -49
- package/extensions/phi/orchestrator.ts +51 -12
- package/extensions/phi/providers/live-models.ts +31 -1
- package/extensions/phi/providers/opencode-go.ts +7 -3
- package/extensions/phi/setup.ts +9 -0
- package/extensions/phi/skill-loader.ts +46 -25
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
import { ALIBABA_MODELS, ALIBABA_PROVIDERS, pingAlibaba } from "./alibaba.js";
|
|
28
28
|
import { inferContextWindow } from "./context-window.js";
|
|
29
29
|
|
|
30
|
-
export const LAST_VERIFIED = "2026-
|
|
30
|
+
export const LAST_VERIFIED = "2026-07-10";
|
|
31
31
|
|
|
32
32
|
export interface LiveModel {
|
|
33
33
|
id: string;
|
|
@@ -203,8 +203,27 @@ const STATIC_OPENROUTER: LiveModel[] = [
|
|
|
203
203
|
{ id: "minimax/MiniMax-M2.7", name: "MiniMax M2.7", reasoning: true },
|
|
204
204
|
];
|
|
205
205
|
|
|
206
|
+
// OpenCode Zen (https://opencode.ai/zen) — the non-Go catalog. The models
|
|
207
|
+
// endpoint is reachable without a key (like OpenRouter); inference needs one.
|
|
208
|
+
const STATIC_OPENCODE_ZEN: LiveModel[] = [
|
|
209
|
+
{ id: "claude-fable-5", name: "Claude Fable 5", contextWindow: 200_000, reasoning: true },
|
|
210
|
+
{ id: "claude-opus-4-8", name: "Claude Opus 4.8", contextWindow: 200_000, reasoning: true },
|
|
211
|
+
{ id: "claude-sonnet-5", name: "Claude Sonnet 5", contextWindow: 200_000, reasoning: true },
|
|
212
|
+
{ id: "claude-haiku-4-5", name: "Claude Haiku 4.5", contextWindow: 200_000, reasoning: true },
|
|
213
|
+
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", contextWindow: 1_000_000, reasoning: true },
|
|
214
|
+
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro", contextWindow: 1_000_000, reasoning: true },
|
|
215
|
+
{ id: "gpt-5.5", name: "GPT-5.5", contextWindow: 400_000, reasoning: true },
|
|
216
|
+
{ id: "gpt-5.4", name: "GPT-5.4", contextWindow: 400_000, reasoning: true },
|
|
217
|
+
{ id: "glm-5.2", name: "GLM 5.2", contextWindow: 200_000, reasoning: true },
|
|
218
|
+
{ id: "glm-5.1", name: "GLM 5.1", contextWindow: 200_000, reasoning: true },
|
|
219
|
+
{ id: "kimi-k2.6", name: "Kimi K2.6", contextWindow: 256_000, reasoning: true },
|
|
220
|
+
{ id: "minimax-m2.7", name: "MiniMax M2.7", contextWindow: 1_000_000, reasoning: true },
|
|
221
|
+
];
|
|
222
|
+
|
|
206
223
|
function staticFallbackFor(providerId: string): LiveModel[] {
|
|
207
224
|
switch (providerId) {
|
|
225
|
+
case "opencode":
|
|
226
|
+
return STATIC_OPENCODE_ZEN;
|
|
208
227
|
case "opencode-go":
|
|
209
228
|
return OPENCODE_GO_FALLBACK_MODELS.map((m) => ({
|
|
210
229
|
id: m.id,
|
|
@@ -274,6 +293,15 @@ async function fetchOpenRouter(apiKey: string | undefined, timeoutMs: number): P
|
|
|
274
293
|
return mapOpenAIModels(raw);
|
|
275
294
|
}
|
|
276
295
|
|
|
296
|
+
async function fetchOpenCodeZen(apiKey: string | undefined, timeoutMs: number): Promise<LiveModel[]> {
|
|
297
|
+
const headers: Record<string, string> = { Accept: "application/json" };
|
|
298
|
+
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
299
|
+
const raw = (await fetchJson("https://opencode.ai/zen/v1/models", headers, timeoutMs)) as OpenAIModelsResponse;
|
|
300
|
+
// The Zen endpoint reports neither context windows nor reasoning support;
|
|
301
|
+
// frontier catalog models all expose reasoning, so default to true.
|
|
302
|
+
return mapOpenAIModels(raw).map((m) => ({ ...m, reasoning: true }));
|
|
303
|
+
}
|
|
304
|
+
|
|
277
305
|
async function fetchGroq(apiKey: string, timeoutMs: number): Promise<LiveModel[]> {
|
|
278
306
|
const raw = (await fetchJson(
|
|
279
307
|
"https://api.groq.com/openai/v1/models",
|
|
@@ -351,6 +379,8 @@ async function dispatchFetch(providerId: string, options: FetchOptions): Promise
|
|
|
351
379
|
return await fetchGoogle(apiKey, timeoutMs);
|
|
352
380
|
case "openrouter":
|
|
353
381
|
return await fetchOpenRouter(apiKey, timeoutMs);
|
|
382
|
+
case "opencode":
|
|
383
|
+
return await fetchOpenCodeZen(apiKey, timeoutMs);
|
|
354
384
|
case "groq":
|
|
355
385
|
if (!apiKey) throw new Error("Groq requires an API key for live listing");
|
|
356
386
|
return await fetchGroq(apiKey, timeoutMs);
|
|
@@ -49,27 +49,31 @@ interface OpenCodeGoModelsResponse {
|
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Fallback static list of OpenCode Go models.
|
|
52
|
-
* Last verified: 2026-
|
|
52
|
+
* Last verified: 2026-07-10 (20 models, sourced from the live /v1/models endpoint).
|
|
53
53
|
* Used when network unreachable or auth fails before configuration.
|
|
54
54
|
*
|
|
55
55
|
* Refresh with: `curl -s https://opencode.ai/zen/go/v1/models | jq '.data[].id'`
|
|
56
56
|
*/
|
|
57
57
|
export const OPENCODE_GO_FALLBACK_MODELS: readonly OpenCodeGoModel[] = [
|
|
58
|
+
{ id: "minimax-m3", name: "MiniMax M3", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
58
59
|
{ id: "minimax-m2.7", name: "MiniMax M2.7", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
59
60
|
{ id: "minimax-m2.5", name: "MiniMax M2.5", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
61
|
+
{ id: "kimi-k2.7-code", name: "Kimi K2.7 Code", contextWindow: 256_000, maxTokens: 16_384 },
|
|
60
62
|
{ id: "kimi-k2.6", name: "Kimi K2.6", contextWindow: 256_000, maxTokens: 16_384 },
|
|
61
63
|
{ id: "kimi-k2.5", name: "Kimi K2.5", contextWindow: 256_000, maxTokens: 16_384 },
|
|
64
|
+
{ id: "glm-5.2", name: "GLM 5.2", contextWindow: 200_000, maxTokens: 128_000 },
|
|
62
65
|
{ id: "glm-5.1", name: "GLM 5.1", contextWindow: 200_000, maxTokens: 128_000 },
|
|
63
66
|
{ id: "glm-5", name: "GLM 5", contextWindow: 200_000, maxTokens: 128_000 },
|
|
64
67
|
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", contextWindow: 128_000, maxTokens: 8_192 },
|
|
65
68
|
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", contextWindow: 128_000, maxTokens: 8_192 },
|
|
69
|
+
{ id: "qwen3.7-max", name: "Qwen 3.7 Max", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
66
70
|
{ id: "qwen3.7-plus", name: "Qwen 3.7 Plus", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
67
71
|
{ id: "qwen3.6-plus", name: "Qwen 3.6 Plus", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
68
72
|
{ id: "qwen3.5-plus", name: "Qwen 3.5 Plus", contextWindow: 1_000_000, maxTokens: 16_384 },
|
|
69
|
-
{ id: "mimo-v2-pro", name: "MiMo V2 Pro", contextWindow: 200_000, maxTokens: 16_384 },
|
|
70
|
-
{ id: "mimo-v2-omni", name: "MiMo V2 Omni", contextWindow: 200_000, maxTokens: 16_384 },
|
|
71
73
|
{ id: "mimo-v2.5-pro", name: "MiMo V2.5 Pro", contextWindow: 200_000, maxTokens: 16_384 },
|
|
72
74
|
{ id: "mimo-v2.5", name: "MiMo V2.5", contextWindow: 200_000, maxTokens: 16_384 },
|
|
75
|
+
{ id: "mimo-v2-pro", name: "MiMo V2 Pro", contextWindow: 200_000, maxTokens: 16_384 },
|
|
76
|
+
{ id: "mimo-v2-omni", name: "MiMo V2 Omni", contextWindow: 200_000, maxTokens: 16_384 },
|
|
73
77
|
{ id: "hy3-preview", name: "Hy3 Preview", contextWindow: 128_000, maxTokens: 16_384 },
|
|
74
78
|
] as const;
|
|
75
79
|
|
package/extensions/phi/setup.ts
CHANGED
|
@@ -109,6 +109,15 @@ function getProviderCatalog(): ProviderEntry[] {
|
|
|
109
109
|
staticModels: [],
|
|
110
110
|
docUrl: OPENCODE_GO_AUTH_URL,
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
id: "opencode",
|
|
114
|
+
displayName: "OpenCode Zen",
|
|
115
|
+
envVar: "OPENCODE_API_KEY",
|
|
116
|
+
baseUrl: "https://opencode.ai/zen/v1",
|
|
117
|
+
api: "openai-completions",
|
|
118
|
+
staticModels: [],
|
|
119
|
+
docUrl: OPENCODE_GO_AUTH_URL,
|
|
120
|
+
},
|
|
112
121
|
{
|
|
113
122
|
id: "openai",
|
|
114
123
|
displayName: "OpenAI",
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses sigma-skills SkillScanner and SkillLoader for skill discovery and matching.
|
|
5
5
|
* Skills are folders containing SKILL.md files with specialized knowledge.
|
|
6
|
-
* When skill-related keywords are detected in user input,
|
|
7
|
-
*
|
|
6
|
+
* When skill-related keywords are detected in user input, a compact skill hint
|
|
7
|
+
* (name, description, SKILL.md path) is appended to the message so the model
|
|
8
|
+
* actually SEES it and can load the full content via the `read` tool. A UI
|
|
9
|
+
* notification alone never reaches the model.
|
|
8
10
|
*
|
|
9
11
|
* Discovery locations (in priority order):
|
|
10
12
|
* 1. .phi/skills/ (project-local, highest priority)
|
|
@@ -13,52 +15,78 @@
|
|
|
13
15
|
*
|
|
14
16
|
* Features:
|
|
15
17
|
* - Automatic skill discovery at startup
|
|
16
|
-
* - Keyword-based skill detection
|
|
18
|
+
* - Keyword-based skill detection with an injection threshold
|
|
19
|
+
* - Auto-injection of skill hints into the model context (autoInject)
|
|
17
20
|
* - /skills command to list available skills
|
|
18
|
-
* - Contextual skill notification via ui.notify
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
|
-
import type { ExtensionAPI
|
|
23
|
+
import type { ExtensionAPI } from "phi-code";
|
|
22
24
|
import { SkillScanner, SkillLoader } from "sigma-skills";
|
|
23
25
|
import type { SkillsConfig } from "sigma-skills";
|
|
26
|
+
import { existsSync } from "node:fs";
|
|
24
27
|
import { join } from "node:path";
|
|
25
28
|
import { homedir } from "node:os";
|
|
26
29
|
|
|
27
30
|
export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
31
|
+
// Bundled skills live inside the package in the repo layout; postinstall
|
|
32
|
+
// copies them to ~/.phi/agent/skills (== globalDir) in the installed layout,
|
|
33
|
+
// where the relative hop from ~/.phi/agent/extensions/ would point at the
|
|
34
|
+
// non-existent ~/skills. Probe both (the scanner dedupes by skill name).
|
|
35
|
+
const bundledCandidates = [
|
|
36
|
+
join(__dirname, "..", "..", "..", "skills"),
|
|
37
|
+
join(homedir(), ".phi", "agent", "skills"),
|
|
38
|
+
];
|
|
28
39
|
const config: SkillsConfig = {
|
|
29
40
|
globalDir: join(homedir(), ".phi", "agent", "skills"),
|
|
30
41
|
projectDir: join(process.cwd(), ".phi", "skills"),
|
|
31
|
-
bundledDir:
|
|
42
|
+
bundledDir: bundledCandidates.find((dir) => existsSync(dir)) ?? bundledCandidates[0],
|
|
32
43
|
autoInject: true,
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
const scanner = new SkillScanner(config);
|
|
36
47
|
const loader = new SkillLoader(scanner);
|
|
37
48
|
|
|
49
|
+
// Skills already hinted this session — hint each skill once, not on every
|
|
50
|
+
// message of a long conversation about the same topic.
|
|
51
|
+
const hintedSkills = new Set<string>();
|
|
52
|
+
|
|
38
53
|
// ─── Input Event: Match skills to user input ─────────────────────
|
|
39
54
|
|
|
40
55
|
pi.on("input", async (event, ctx) => {
|
|
41
56
|
if (event.source === "extension") {
|
|
42
57
|
return { action: "continue" };
|
|
43
58
|
}
|
|
59
|
+
// Slash commands and skill blocks are not prose — nothing to match.
|
|
60
|
+
if (event.text.trimStart().startsWith("/")) {
|
|
61
|
+
return { action: "continue" };
|
|
62
|
+
}
|
|
44
63
|
|
|
45
64
|
const matches = loader.findRelevantSkills(event.text);
|
|
65
|
+
// findRelevantSkills scores any shared word; require a clear signal
|
|
66
|
+
// (name match or several keywords) before surfacing a skill.
|
|
67
|
+
const strong = matches.filter((m) => m.score >= 3 && !hintedSkills.has(m.skill.name)).slice(0, 2);
|
|
68
|
+
if (strong.length === 0) {
|
|
69
|
+
return { action: "continue" };
|
|
70
|
+
}
|
|
71
|
+
for (const match of strong) {
|
|
72
|
+
hintedSkills.add(match.skill.name);
|
|
73
|
+
}
|
|
46
74
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const topMatches = matches.slice(0, 3);
|
|
50
|
-
for (const match of topMatches) {
|
|
51
|
-
ctx.ui.notify(
|
|
52
|
-
`📚 Relevant skill: **${match.skill.name}** — ${match.skill.description}\nUse \`read ${match.skill.path}/SKILL.md\` for full content.`,
|
|
53
|
-
"info"
|
|
54
|
-
);
|
|
55
|
-
}
|
|
75
|
+
const names = strong.map((m) => m.skill.name).join(", ");
|
|
76
|
+
ctx.ui.notify(`📚 Skill hint: ${names} (injected into context)`, "info");
|
|
56
77
|
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
if (!config.autoInject) {
|
|
79
|
+
return { action: "continue" };
|
|
59
80
|
}
|
|
60
81
|
|
|
61
|
-
|
|
82
|
+
const hints = strong
|
|
83
|
+
.map((m) => `- ${m.skill.name}: ${m.skill.description} — full content: read ${join(m.skill.path, "SKILL.md")}`)
|
|
84
|
+
.join("\n");
|
|
85
|
+
return {
|
|
86
|
+
action: "transform",
|
|
87
|
+
text: `${event.text}\n\n[Skill hints — relevant local skills detected for this request. Read the SKILL.md before applying:\n${hints}]`,
|
|
88
|
+
images: event.images,
|
|
89
|
+
};
|
|
62
90
|
});
|
|
63
91
|
|
|
64
92
|
// ─── /skills Command ─────────────────────────────────────────────
|
|
@@ -108,11 +136,4 @@ export default function skillLoaderExtension(pi: ExtensionAPI) {
|
|
|
108
136
|
}
|
|
109
137
|
},
|
|
110
138
|
});
|
|
111
|
-
|
|
112
|
-
// ─── Session Start: Load skills ──────────────────────────────────
|
|
113
|
-
|
|
114
|
-
pi.on("session_start", async (_event, _ctx) => {
|
|
115
|
-
const skills = loader.listSkills();
|
|
116
|
-
console.log(`[skill-loader] Loaded ${skills.length} skills from 3 locations`);
|
|
117
|
-
});
|
|
118
139
|
}
|