pi-pstack-adapter 0.3.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/src/paths.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { createHash } from "node:crypto";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+
5
+ export const CONFIG_FILE_NAME = "pi-pstack-adapter.json";
6
+
7
+ export function expandHome(value: string): string {
8
+ if (value === "~") return homedir();
9
+ if (value.startsWith("~/") || value.startsWith(`~\\`)) {
10
+ return join(homedir(), value.slice(2));
11
+ }
12
+ return value;
13
+ }
14
+
15
+ /**
16
+ * Mirrors Pi's own `getAgentDir()`, so a sandboxed `PI_CODING_AGENT_DIR` keeps its cache
17
+ * beside its own config instead of writing into the real one.
18
+ */
19
+ export function agentDir(): string {
20
+ const override = process.env.PI_CODING_AGENT_DIR;
21
+ return override ? resolve(expandHome(override)) : join(homedir(), ".pi", "agent");
22
+ }
23
+
24
+ export function cacheRoot(override?: string): string {
25
+ if (override) return resolve(expandHome(override));
26
+ return join(agentDir(), "cache", "pi-pstack-adapter");
27
+ }
28
+
29
+ export function digest(...parts: string[]): string {
30
+ const hash = createHash("sha256");
31
+ for (const part of parts) {
32
+ hash.update(part);
33
+ hash.update("\u0000");
34
+ }
35
+ return hash.digest("hex").slice(0, 16);
36
+ }
37
+
38
+ export function checkoutDir(root: string, repo: string): string {
39
+ return join(root, "checkouts", digest(repo));
40
+ }
41
+
42
+ export function generatedDir(root: string, inputsDigest: string): string {
43
+ return join(root, "generated", inputsDigest);
44
+ }
45
+
46
+ export function stateFile(root: string): string {
47
+ return join(root, "state.json");
48
+ }
49
+
50
+ /**
51
+ * A relative location inside the checkout. An upstream manifest and a config layer both
52
+ * supply these, so neither may escape the directory the adapter controls.
53
+ */
54
+ export function validateContainedPath(value: string, label: string): string {
55
+ const normalized = value.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
56
+ const escapes =
57
+ normalized === "" ||
58
+ isAbsolute(normalized) ||
59
+ /^[A-Za-z]:/.test(normalized) ||
60
+ normalized.split("/").some((segment) => segment === "..");
61
+ if (escapes) {
62
+ throw new Error(
63
+ `Refusing ${label} ${JSON.stringify(value)}. It must name a location inside the checkout, not an absolute or parent path.`,
64
+ );
65
+ }
66
+ return normalized;
67
+ }
@@ -0,0 +1,104 @@
1
+ import type { CapabilityId, RegistryEntry, SupportTier } from "./types.ts";
2
+
3
+ /**
4
+ * Support tiers for the certified upstream revision. Classification came from reading
5
+ * every upstream SKILL.md; see `docs/compatibility.md` for how a tier changes.
6
+ * Skills absent from this table are generated as `experimental`.
7
+ */
8
+ export const SKILL_REGISTRY: Readonly<Record<string, RegistryEntry>> = {
9
+ "architect": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Phase B delegates to arena runners, so it cannot run without parallel subagents." },
10
+ "arena": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git"], note: "The whole skill is spawning N candidate subagents plus a judge." },
11
+ "automate-me": { tier: "experimental", capabilities: ["delegation", "structured-question"], executables: ["git", "gh"], note: "Mining step depends on Cursor transcript files and the built-in create-skill skill, with no Pi equivalent." },
12
+ "benchmark-checklist": { tier: "dependency-gated", capabilities: [], executables: ["uptime", "nproc"], note: "Its setup step checks load with `uptime` and cores with `nproc`. macOS has no `nproc` without GNU coreutils, and Git Bash on Windows has no `uptime`." },
13
+ "blast-radius": { tier: "dependency-gated", capabilities: [], executables: ["git", "gh"], note: "Needs git and gh to read the diff, commits, and PR before any analysis." },
14
+ "bro": { tier: "native", capabilities: [], executables: [] },
15
+ "correct": { tier: "dependency-gated", capabilities: [], executables: ["git"], note: "Mines commit history, then commits fixes and edits lint, CI, and agent instruction files. Run it on a branch you will review." },
16
+ "create-verification-skill": { tier: "adapted", capabilities: [], executables: [], note: "Only the generated skill's output path needs remapping to Pi's skill directory." },
17
+ "figure-it-out": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Phase B fan-out and the architect/arena routing require parallel subagents." },
18
+ "how": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Every path spawns explorer or explainer subagents; nothing runs inline." },
19
+ "interrogate": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git"], note: "Multi-model adversarial review is entirely one subagent per configured model." },
20
+ "maintain-verification-skill": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Source wave launches one read-only subagent per feature file." },
21
+ "make-bot-ui": { tier: "unsupported", capabilities: [], executables: ["curl", "tailscale", "sudo"], note: "Built entirely on Cursor Automations webhook routines and the secret-request card." },
22
+ "no-comments": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git"], note: "Step 1 must spawn the named Comment Sicko agent; the skill only triages its report." },
23
+ "poteto-mode": { tier: "experimental", capabilities: ["delegation", "structured-question"], executables: ["git", "gh"], note: "Sticky-mode hosting plus references to Cursor built-in and plugin skills only partially map to Pi." },
24
+ "principle-attack-the-premise": { tier: "native", capabilities: [], executables: [] },
25
+ "principle-boundary-discipline": { tier: "native", capabilities: [], executables: [] },
26
+ "principle-build-the-lever": { tier: "native", capabilities: [], executables: [] },
27
+ "principle-encode-lessons-in-structure": { tier: "native", capabilities: [], executables: [] },
28
+ "principle-exhaust-the-design-space": { tier: "native", capabilities: [], executables: [] },
29
+ "principle-experience-first": { tier: "native", capabilities: [], executables: [] },
30
+ "principle-explain-the-number": { tier: "native", capabilities: [], executables: [], note: "For a performance number it hands off to benchmark-checklist, which needs uptime and nproc. Check that skill first." },
31
+ "principle-fix-root-causes": { tier: "native", capabilities: [], executables: [] },
32
+ "principle-foundational-thinking": { tier: "native", capabilities: [], executables: [] },
33
+ "principle-guard-the-context-window": { tier: "native", capabilities: [], executables: [] },
34
+ "principle-laziness-protocol": { tier: "native", capabilities: [], executables: [] },
35
+ "principle-make-operations-idempotent": { tier: "native", capabilities: [], executables: [] },
36
+ "principle-migrate-callers-then-delete-legacy-apis": { tier: "native", capabilities: [], executables: [] },
37
+ "principle-minimize-reader-load": { tier: "native", capabilities: [], executables: [] },
38
+ "principle-model-the-domain": { tier: "native", capabilities: [], executables: [] },
39
+ "principle-never-block-on-the-human": { tier: "native", capabilities: [], executables: [] },
40
+ "principle-outcome-oriented-execution": { tier: "native", capabilities: [], executables: [] },
41
+ "principle-prove-it-works": { tier: "native", capabilities: [], executables: [] },
42
+ "principle-redesign-from-first-principles": { tier: "native", capabilities: [], executables: [] },
43
+ "principle-separate-before-serializing-shared-state": { tier: "native", capabilities: [], executables: [] },
44
+ "principle-sequence-verifiable-units": { tier: "native", capabilities: [], executables: [] },
45
+ "principle-subtract-before-you-add": { tier: "native", capabilities: [], executables: [] },
46
+ "principle-test-behavior-not-implementation": { tier: "native", capabilities: [], executables: [] },
47
+ "principle-type-system-discipline": { tier: "native", capabilities: [], executables: [] },
48
+ "recall": { tier: "experimental", capabilities: ["delegation"], executables: ["git", "gh"], note: "Core corpus is Cursor's on-disk transcript JSONL, so only the git/gh and shared-record half survives." },
49
+ "reflect": { tier: "experimental", capabilities: ["delegation"], executables: [], note: "Transcript mining and create-skill routing are Cursor-specific, though the digest fallback partly works." },
50
+ "setup-pstack": { tier: "native", capabilities: [], executables: [], rename: "setup", replacement: "setup.md", note: "The upstream body only writes a Cursor .mdc rule, so the adapter replaces it with a Pi setup workflow." },
51
+ "show-me-your-work": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "The mandatory cross-model review of the trail requires spawning a different-family subagent." },
52
+ "swarm": { tier: "experimental", capabilities: ["delegation"], executables: [], note: "Cloud background workers and cloud_base_branch have no Pi mapping; only local fan-out approximates it." },
53
+ "tdd": { tier: "native", capabilities: [], executables: [] },
54
+ "teach": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "It runs the how and why skills, both of which need subagents." },
55
+ "technical-writing": { tier: "native", capabilities: [], executables: [] },
56
+ "typescript-best-practices": { tier: "native", capabilities: [], executables: [] },
57
+ "unslop": { tier: "native", capabilities: [], executables: [] },
58
+ "why": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git", "gh"], note: "Default posture spawns one investigator subagent per evidence category plus a synthesizer." },
59
+ };
60
+
61
+ /** Adapter-owned skills with no upstream counterpart. Keyed by unprefixed generated name. */
62
+ export const ADAPTER_SKILLS: Readonly<Record<string, RegistryEntry>> = {
63
+ status: { tier: "native", capabilities: [], executables: [], replacement: "status.md" },
64
+ };
65
+
66
+ export const UNKNOWN_SKILL_ENTRY: RegistryEntry = {
67
+ tier: "experimental",
68
+ capabilities: [],
69
+ executables: [],
70
+ note: "This skill is not in the adapter compatibility registry for the certified revision.",
71
+ };
72
+
73
+ export const TIER_ORDER: readonly SupportTier[] = [
74
+ "native",
75
+ "adapted",
76
+ "dependency-gated",
77
+ "experimental",
78
+ "unsupported",
79
+ ];
80
+
81
+ export const CAPABILITY_LABELS: Readonly<Record<CapabilityId, string>> = {
82
+ delegation: "named parallel background subagents with model selection and tool restrictions",
83
+ "structured-question": "structured multiple-choice questions to the user",
84
+ };
85
+
86
+ export const ADAPTER_OWNED = "(adapter-owned)";
87
+
88
+ /** Model roles pstack workflows name. A role with no configured model inherits the parent. */
89
+ export const MODEL_ROLES: readonly string[] = [
90
+ "feature",
91
+ "refactoring",
92
+ "bug-fix",
93
+ "perf-issue",
94
+ "hillclimb",
95
+ "judgment",
96
+ "strongest-judgment",
97
+ ];
98
+
99
+ export function entryFor(upstreamName: string, generatedBareName?: string): RegistryEntry {
100
+ if (upstreamName === ADAPTER_OWNED && generatedBareName) {
101
+ return ADAPTER_SKILLS[generatedBareName] ?? UNKNOWN_SKILL_ENTRY;
102
+ }
103
+ return SKILL_REGISTRY[upstreamName] ?? UNKNOWN_SKILL_ENTRY;
104
+ }
package/src/status.ts ADDED
@@ -0,0 +1,215 @@
1
+ import { CERTIFIED } from "./certified.ts";
2
+ import { CAPABILITY_LABELS, MODEL_ROLES, TIER_ORDER } from "./registry.ts";
3
+ import type { AdapterConfig } from "./config.ts";
4
+ import { trustWarning } from "./config.ts";
5
+ import type { PrerequisiteReport } from "./capabilities.ts";
6
+ import type {
7
+ CapabilityStatus,
8
+ Diagnostic,
9
+ GeneratedSkill,
10
+ GenerationResult,
11
+ PlatformSupport,
12
+ RegistryEntry,
13
+ ResolvedSource,
14
+ SupportTier,
15
+ } from "./types.ts";
16
+
17
+ export interface StatusInput {
18
+ readonly adapterVersion: string;
19
+ readonly config: AdapterConfig;
20
+ readonly source: ResolvedSource;
21
+ readonly upstreamVersion: string;
22
+ readonly generation: GenerationResult;
23
+ readonly capabilities: readonly CapabilityStatus[];
24
+ readonly platform: PlatformSupport;
25
+ readonly platformName: string;
26
+ readonly modeActive: boolean;
27
+ readonly cacheRoot: string;
28
+ readonly bootstrapped: boolean;
29
+ /** Runtime findings that generation could not know, such as agent registration results. */
30
+ readonly runtimeDiagnostics?: readonly Diagnostic[];
31
+ }
32
+
33
+ export function renderStatus(input: StatusInput): string {
34
+ const { config, source } = input;
35
+ const lines: string[] = [];
36
+
37
+ lines.push(`# ${config.namespace} adapter status`);
38
+ lines.push("");
39
+ lines.push(`| field | value |`);
40
+ lines.push(`| --- | --- |`);
41
+ lines.push(`| adapter version | ${input.adapterVersion} |`);
42
+ lines.push(`| upstream pstack version | ${input.upstreamVersion} |`);
43
+ lines.push(`| certified pstack version | ${CERTIFIED.pstackVersion} |`);
44
+ lines.push(`| configured source | ${describeSelection(input)} |`);
45
+ lines.push(`| resolved commit | ${source.commit || "(not a git checkout)"} |`);
46
+ lines.push(`| certified commit | ${CERTIFIED.commit} |`);
47
+ lines.push(`| trust | ${source.trust} |`);
48
+ lines.push(`| checkout | ${source.checkoutDir} |`);
49
+ lines.push(`| generated resources | ${input.generation.outDir} |`);
50
+ lines.push(`| cache root | ${input.cacheRoot} |`);
51
+ lines.push(`| cache state | ${input.bootstrapped ? "downloaded this session" : "reused, no network needed"} |`);
52
+ lines.push(`| namespace | ${config.namespace} |`);
53
+ lines.push(`| config layers | ${config.sources.length > 0 ? config.sources.join(", ") : "built-in defaults"} |`);
54
+ lines.push(`| platform | ${input.platformName} (${input.platform}) |`);
55
+ lines.push(`| Poteto Mode | ${input.modeActive ? "active" : "off"} |`);
56
+ lines.push("");
57
+
58
+ const warning = trustWarning(source.trust, config);
59
+ if (warning) {
60
+ lines.push(`> Warning. ${warning}`);
61
+ lines.push("");
62
+ }
63
+ if (input.platform === "experimental") {
64
+ lines.push(
65
+ `> Warning. ${input.platformName} is experimental for this release. Adapter-owned code is portable, but upstream pstack skills call POSIX shell tools that this platform may not provide. Only Linux, macOS, and Windows are certified.`,
66
+ );
67
+ lines.push("");
68
+ }
69
+
70
+ lines.push(`## Capabilities`);
71
+ lines.push("");
72
+ lines.push(`| capability | state | note |`);
73
+ lines.push(`| --- | --- | --- |`);
74
+ for (const capability of input.capabilities) {
75
+ const note = capability.available
76
+ ? CAPABILITY_LABELS[capability.id]
77
+ : capability.recommendation;
78
+ lines.push(`| ${capability.id} | ${capability.available ? "available" : "missing"} | ${note} |`);
79
+ }
80
+ lines.push("");
81
+
82
+ lines.push(`## Model roles`);
83
+ lines.push("");
84
+ lines.push(`| role | model |`);
85
+ lines.push(`| --- | --- |`);
86
+ for (const role of MODEL_ROLES) {
87
+ lines.push(`| ${role} | ${config.models[role] ?? "inherit (parent session model)"} |`);
88
+ }
89
+ const extra = Object.keys(config.models).filter((role) => !MODEL_ROLES.includes(role)).sort();
90
+ for (const role of extra) {
91
+ lines.push(`| ${role} | ${config.models[role]} |`);
92
+ }
93
+ lines.push("");
94
+
95
+ lines.push(`## Support tiers`);
96
+ lines.push("");
97
+ const byTier = groupByTier(input.generation);
98
+ lines.push(`| tier | count | skills |`);
99
+ lines.push(`| --- | --- | --- |`);
100
+ for (const tier of TIER_ORDER) {
101
+ const names = byTier.get(tier) ?? [];
102
+ if (names.length === 0) continue;
103
+ lines.push(`| ${tier} | ${names.length} | ${names.join(", ")} |`);
104
+ }
105
+ lines.push("");
106
+ lines.push(
107
+ `${input.generation.skills.length} skills and ${input.generation.agents.length} agents are registered under \`${config.namespace}\`.`,
108
+ );
109
+ lines.push("");
110
+
111
+ lines.push(`## Diagnostics`);
112
+ lines.push("");
113
+ const diagnostics = [...input.generation.diagnostics, ...(input.runtimeDiagnostics ?? [])];
114
+ if (diagnostics.length === 0) {
115
+ lines.push("No upstream resource problems found.");
116
+ } else {
117
+ lines.push(`| level | resource | message | action |`);
118
+ lines.push(`| --- | --- | --- | --- |`);
119
+ for (const diagnostic of sortDiagnostics(diagnostics)) {
120
+ lines.push(`| ${diagnostic.level} | ${diagnostic.resource} | ${diagnostic.message} | ${diagnostic.action ?? "-"} |`);
121
+ }
122
+ }
123
+ lines.push("");
124
+ return lines.join("\n");
125
+ }
126
+
127
+ function describeSelection(input: StatusInput): string {
128
+ const { config } = input;
129
+ if (config.localPath) return `local checkout ${config.localPath}`;
130
+ const pin = config.pinnedCommit ? ` pinned at ${config.pinnedCommit}` : ` at ref ${config.ref}`;
131
+ return `${config.repo}${pin}`;
132
+ }
133
+
134
+ function groupByTier(generation: GenerationResult): Map<SupportTier, string[]> {
135
+ const grouped = new Map<SupportTier, string[]>();
136
+ for (const skill of generation.skills) {
137
+ const names = grouped.get(skill.tier) ?? [];
138
+ names.push(skill.generatedName);
139
+ grouped.set(skill.tier, names);
140
+ }
141
+ return grouped;
142
+ }
143
+
144
+ const LEVEL_RANK = { error: 0, warning: 1, info: 2 } as const;
145
+
146
+ function sortDiagnostics(diagnostics: readonly Diagnostic[]): Diagnostic[] {
147
+ return [...diagnostics].sort(
148
+ (a, b) => LEVEL_RANK[a.level] - LEVEL_RANK[b.level] || a.resource.localeCompare(b.resource),
149
+ );
150
+ }
151
+
152
+ export function renderCheck(
153
+ skill: GeneratedSkill,
154
+ entry: RegistryEntry,
155
+ report: PrerequisiteReport,
156
+ ): string {
157
+ const parts = [`\`/skill:${skill.generatedName}\` is tier **${skill.tier}**.`];
158
+ if (entry.note) parts.push(entry.note);
159
+ if (skill.tier === "unsupported") {
160
+ parts.push(
161
+ "Pi cannot provide what this workflow needs, so it cannot reach its purpose here. Prerequisite checks do not change that.",
162
+ );
163
+ }
164
+ if (!report.ok) {
165
+ parts.push(`Do not start this workflow yet.\n${report.lines.map((line) => `- ${line}`).join("\n")}`);
166
+ } else if (skill.tier !== "unsupported") {
167
+ parts.push("Prerequisites satisfied. Start the workflow.");
168
+ }
169
+ if (skill.tier === "experimental" && report.ok) {
170
+ parts.push("Tier `experimental` means parts of this workflow have no Pi equivalent. Expect gaps.");
171
+ }
172
+ return parts.join("\n\n");
173
+ }
174
+
175
+ /** Platforms a release certifies. Windows means native Windows with Pi's Git Bash `bash` tool. */
176
+ export function platformSupport(platform: string): PlatformSupport {
177
+ return platform === "linux" || platform === "darwin" || platform === "win32" ? "supported" : "experimental";
178
+ }
179
+
180
+ export interface ShellState {
181
+ /** Why Pi could not resolve the shell its `bash` tool runs, or undefined when it can. */
182
+ readonly shellError?: string;
183
+ /** Why the resolved shell could not run a command, when a probe ran and failed. */
184
+ readonly runError?: string;
185
+ /** Whether the `bash` tool is in the active tool set, for example not replaced by `powershell`. */
186
+ readonly bashToolActive: boolean;
187
+ }
188
+
189
+ /**
190
+ * Upstream skill bodies are written as Bash commands for Pi's `bash` tool, so a session without
191
+ * that tool or its shell cannot run them. On Windows that shell is Git Bash.
192
+ */
193
+ export function shellDiagnostics(state: ShellState): Diagnostic[] {
194
+ const diagnostics: Diagnostic[] = [];
195
+ if (state.shellError !== undefined) {
196
+ diagnostics.push({
197
+ level: "error",
198
+ resource: "shell",
199
+ message: `Pi cannot find the Bash shell its \`bash\` tool runs, and pstack skills run their commands there. ${state.shellError.split("\n")[0]}`,
200
+ action: "Install Git for Windows, or set `shellPath` in Pi settings to a Bash executable, then run /reload.",
201
+ });
202
+ }
203
+ if (state.runError !== undefined) {
204
+ diagnostics.push({ level: "error", resource: "shell", message: state.runError });
205
+ }
206
+ if (!state.bashToolActive) {
207
+ diagnostics.push({
208
+ level: "warning",
209
+ resource: "shell",
210
+ message: "The `bash` tool is not active in this session. pstack skill bodies are written as Bash commands, and PowerShell does not run them.",
211
+ action: "Add `\"bash\"` to the `defaultTools` list in Pi settings, or remove that setting, then restart Pi.",
212
+ });
213
+ }
214
+ return diagnostics;
215
+ }
package/src/types.ts ADDED
@@ -0,0 +1,98 @@
1
+ export type SourceSelection =
2
+ | { readonly kind: "git"; readonly repo: string; readonly ref: string }
3
+ | { readonly kind: "local"; readonly path: string };
4
+
5
+ export type Trust = "tested" | "untested-ref" | "untested-repo" | "local";
6
+
7
+ export interface ResolvedSource {
8
+ readonly selection: SourceSelection;
9
+ readonly trust: Trust;
10
+ /** Empty for a local checkout that is not a git worktree. */
11
+ readonly commit: string;
12
+ readonly checkoutDir: string;
13
+ readonly pluginDir: string;
14
+ }
15
+
16
+ export type SupportTier =
17
+ | "native"
18
+ | "adapted"
19
+ | "dependency-gated"
20
+ | "experimental"
21
+ | "unsupported";
22
+
23
+ export type CapabilityId = "delegation" | "structured-question";
24
+
25
+ export interface RegistryEntry {
26
+ readonly tier: SupportTier;
27
+ readonly capabilities: readonly CapabilityId[];
28
+ readonly executables: readonly string[];
29
+ readonly note?: string;
30
+ /** Adapter-owned body file under `adapter-skills/`, replacing the upstream body. */
31
+ readonly replacement?: string;
32
+ /** Unprefixed generated name when it differs from the upstream directory name. */
33
+ readonly rename?: string;
34
+ }
35
+
36
+ export interface UpstreamManifest {
37
+ readonly name: string;
38
+ readonly version: string;
39
+ readonly skillsDir: string;
40
+ readonly agentsDir: string;
41
+ }
42
+
43
+ export interface UpstreamResource {
44
+ readonly name: string;
45
+ readonly kind: "skill" | "agent";
46
+ /** Skill directory, or the agents directory for an agent. */
47
+ readonly dir: string;
48
+ readonly entryPath: string;
49
+ }
50
+
51
+ export interface ParsedResource {
52
+ readonly resource: UpstreamResource;
53
+ readonly frontmatter: Readonly<Record<string, string>>;
54
+ readonly body: string;
55
+ }
56
+
57
+ export type DiagnosticLevel = "error" | "warning" | "info";
58
+
59
+ export interface Diagnostic {
60
+ readonly level: DiagnosticLevel;
61
+ readonly resource: string;
62
+ readonly message: string;
63
+ readonly action?: string;
64
+ }
65
+
66
+ export interface GeneratedSkill {
67
+ readonly upstreamName: string;
68
+ readonly generatedName: string;
69
+ readonly tier: SupportTier;
70
+ readonly capabilities: readonly CapabilityId[];
71
+ readonly executables: readonly string[];
72
+ readonly path: string;
73
+ }
74
+
75
+ export interface GeneratedAgent {
76
+ readonly upstreamName: string;
77
+ readonly generatedName: string;
78
+ readonly displayName: string;
79
+ readonly description: string;
80
+ readonly systemPrompt: string;
81
+ }
82
+
83
+ export interface GenerationResult {
84
+ readonly outDir: string;
85
+ readonly skillsDir: string;
86
+ readonly skills: readonly GeneratedSkill[];
87
+ readonly agents: readonly GeneratedAgent[];
88
+ readonly diagnostics: readonly Diagnostic[];
89
+ }
90
+
91
+ export interface CapabilityStatus {
92
+ readonly id: CapabilityId;
93
+ readonly available: boolean;
94
+ readonly provider?: string;
95
+ readonly recommendation: string;
96
+ }
97
+
98
+ export type PlatformSupport = "supported" | "experimental";