pi-profile-switch 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/README.zh-CN.md +106 -0
- package/examples/profiles.json +38 -0
- package/extensions/pi-profile-switch/index.ts +549 -0
- package/package.json +59 -0
- package/schemas/profiles.schema.json +94 -0
- package/src/json-file.ts +35 -0
- package/src/mcp-config.ts +63 -0
- package/src/mcp-coordination.ts +46 -0
- package/src/model-selection.ts +64 -0
- package/src/name-matching.ts +50 -0
- package/src/profile-catalog-store.ts +91 -0
- package/src/profile-catalog.ts +234 -0
- package/src/profile-resolver.ts +247 -0
- package/src/runtime-state-store.ts +105 -0
- package/src/skill-selection.ts +81 -0
- package/src/startup-selection.ts +147 -0
- package/src/switching/activate-profile.ts +113 -0
- package/src/switching/apply-profile.ts +131 -0
- package/src/switching/customize.ts +87 -0
- package/src/switching/list-profiles.ts +55 -0
- package/src/switching/mcp-toggle.ts +75 -0
- package/src/switching/profile-crud.ts +132 -0
- package/src/switching/profile-wizard.ts +158 -0
- package/src/switching/status.ts +113 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusReport: the `/profile status` observability surface.
|
|
3
|
+
*
|
|
4
|
+
* Pure report builder: combines the ACTIVE selection (what the runtime was
|
|
5
|
+
* resolved to), the stored overlay, and fresh MCP discovery. Markdown
|
|
6
|
+
* formatting is the only presentation; the extension ships it via
|
|
7
|
+
* `pi.sendMessage`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ProfileSource } from "../profile-catalog.ts";
|
|
11
|
+
import type { ResolvedSelection, UnresolvedRef } from "../profile-resolver.ts";
|
|
12
|
+
import type { RuntimeOverlay } from "../runtime-state-store.ts";
|
|
13
|
+
import { visibleSkillNames, type SkillsFilterOutcome } from "../skill-selection.ts";
|
|
14
|
+
|
|
15
|
+
export interface StatusReport {
|
|
16
|
+
profile: string;
|
|
17
|
+
source: ProfileSource;
|
|
18
|
+
overlay?: RuntimeOverlay;
|
|
19
|
+
/** The skills the model can see (all loaded skills when unfiltered). */
|
|
20
|
+
skills: {
|
|
21
|
+
filtered: boolean;
|
|
22
|
+
visible: Array<{ name: string; filePath: string }>;
|
|
23
|
+
loaded: number;
|
|
24
|
+
/** The last prompt-filter result; `no-filter` when the profile
|
|
25
|
+
* selects nothing. */
|
|
26
|
+
filterOutcome: SkillsFilterOutcome;
|
|
27
|
+
};
|
|
28
|
+
tools?: { active: string[]; pending: string[] };
|
|
29
|
+
mcp: { enabled: string[]; discovered: string[]; missing: string[] };
|
|
30
|
+
unresolved: { skills: UnresolvedRef[]; unmatched: string[] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function buildStatusReport(input: {
|
|
34
|
+
selection: ResolvedSelection;
|
|
35
|
+
allSkills: Array<{ name: string; filePath: string }>;
|
|
36
|
+
discoveredMcpServers: string[];
|
|
37
|
+
filterOutcome?: SkillsFilterOutcome;
|
|
38
|
+
}): StatusReport {
|
|
39
|
+
const { selection } = input;
|
|
40
|
+
const visibleNames = visibleSkillNames(input.allSkills, selection.skills);
|
|
41
|
+
const visible =
|
|
42
|
+
visibleNames === undefined
|
|
43
|
+
? input.allSkills
|
|
44
|
+
: input.allSkills.filter((skill) => visibleNames.includes(skill.name));
|
|
45
|
+
|
|
46
|
+
const enabled = selection.mcp ?? [];
|
|
47
|
+
const discovered = new Set(input.discoveredMcpServers);
|
|
48
|
+
const unmatched = [
|
|
49
|
+
...selection.warnings.skillsUnmatched,
|
|
50
|
+
...selection.warnings.mcpUnmatched,
|
|
51
|
+
...selection.warnings.toolsUnmatched,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
profile: selection.name,
|
|
56
|
+
source: selection.source,
|
|
57
|
+
skills: {
|
|
58
|
+
filtered: selection.skills !== undefined,
|
|
59
|
+
visible,
|
|
60
|
+
loaded: input.allSkills.length,
|
|
61
|
+
filterOutcome: input.filterOutcome ?? (selection.skills === undefined ? "no-filter" : "filtered"),
|
|
62
|
+
},
|
|
63
|
+
...(selection.tools !== undefined
|
|
64
|
+
? { tools: { active: selection.tools, pending: selection.pendingTools } }
|
|
65
|
+
: {}),
|
|
66
|
+
mcp: {
|
|
67
|
+
enabled,
|
|
68
|
+
discovered: input.discoveredMcpServers,
|
|
69
|
+
missing: enabled.filter((name) => !discovered.has(name)),
|
|
70
|
+
},
|
|
71
|
+
unresolved: { skills: selection.warnings.skillsUnresolved, unmatched },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function formatStatusMarkdown(report: StatusReport): string {
|
|
76
|
+
const lines: string[] = [];
|
|
77
|
+
lines.push(`### profile: ${report.profile} (${report.source})`);
|
|
78
|
+
if (report.overlay !== undefined) {
|
|
79
|
+
const parts = [
|
|
80
|
+
...(report.overlay.disabledSkills ?? []).map((name) => `-skill:${name}`),
|
|
81
|
+
...(report.overlay.disabledMcp ?? []).map((name) => `-mcp:${name}`),
|
|
82
|
+
...(report.overlay.tools !== undefined ? [`tools=[${report.overlay.tools.join(", ")}]`] : []),
|
|
83
|
+
];
|
|
84
|
+
lines.push(`overlay: ${parts.length > 0 ? parts.join(" ") : "(empty)"}`);
|
|
85
|
+
}
|
|
86
|
+
const skillScope = report.skills.filtered
|
|
87
|
+
? `${report.skills.visible.length} of ${report.skills.loaded} loaded`
|
|
88
|
+
: `all ${report.skills.loaded} loaded`;
|
|
89
|
+
lines.push(`skills: ${skillScope}`);
|
|
90
|
+
if (report.skills.filtered && report.skills.filterOutcome !== "filtered") {
|
|
91
|
+
lines.push(`skills filter: not applied (${report.skills.filterOutcome})`);
|
|
92
|
+
}
|
|
93
|
+
for (const skill of report.skills.visible) {
|
|
94
|
+
lines.push(` ${skill.name} → ${skill.filePath}`);
|
|
95
|
+
}
|
|
96
|
+
if (report.tools !== undefined) {
|
|
97
|
+
lines.push(`tools: [${report.tools.active.join(", ")}]`);
|
|
98
|
+
if (report.tools.pending.length > 0) {
|
|
99
|
+
lines.push(`tools pending (not registered yet): [${report.tools.pending.join(", ")}]`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
lines.push(
|
|
103
|
+
`mcp: enabled=[${report.mcp.enabled.join(", ")}] discovered=[${report.mcp.discovered.join(", ")}] missing=[${report.mcp.missing.join(", ")}]`,
|
|
104
|
+
);
|
|
105
|
+
for (const unresolved of report.unresolved.skills) {
|
|
106
|
+
const hint = unresolved.suggestions.length > 0 ? ` — did you mean: ${unresolved.suggestions.join(", ")}?` : "";
|
|
107
|
+
lines.push(`unresolved skill: ${unresolved.reference}${hint}`);
|
|
108
|
+
}
|
|
109
|
+
if (report.unresolved.unmatched.length > 0) {
|
|
110
|
+
lines.push(`zero-match globs: [${report.unresolved.unmatched.join(", ")}]`);
|
|
111
|
+
}
|
|
112
|
+
return lines.join("\n");
|
|
113
|
+
}
|