@yagni-app/code-staging 0.1.0-staging.997.1 → 0.2.0-staging.1025.1

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.
Files changed (64) hide show
  1. package/README.md +58 -9
  2. package/dist/claudeCompat.d.ts +36 -5
  3. package/dist/claudeCompat.js +85 -23
  4. package/dist/claudePlugins.d.ts +109 -0
  5. package/dist/claudePlugins.js +336 -0
  6. package/dist/cli.js +14 -4
  7. package/dist/crashReport.d.ts +135 -0
  8. package/dist/crashReport.js +291 -0
  9. package/dist/doctor.d.ts +21 -0
  10. package/dist/doctor.js +52 -0
  11. package/dist/extension/askAdvisorTool.js +7 -1
  12. package/dist/extension/bless.js +16 -3
  13. package/dist/extension/boostCommand.d.ts +144 -0
  14. package/dist/extension/boostCommand.js +263 -0
  15. package/dist/extension/branding.d.ts +31 -0
  16. package/dist/extension/branding.js +37 -0
  17. package/dist/extension/chipEditor.js +7 -3
  18. package/dist/extension/claudeRules.d.ts +54 -0
  19. package/dist/extension/claudeRules.js +180 -0
  20. package/dist/extension/config.d.ts +61 -0
  21. package/dist/extension/config.js +86 -0
  22. package/dist/extension/costHud.d.ts +128 -15
  23. package/dist/extension/costHud.js +189 -19
  24. package/dist/extension/crashReport.d.ts +89 -0
  25. package/dist/extension/crashReport.js +241 -0
  26. package/dist/extension/index.d.ts +43 -4
  27. package/dist/extension/index.js +241 -32
  28. package/dist/extension/initPass.d.ts +65 -47
  29. package/dist/extension/initPass.js +145 -145
  30. package/dist/extension/mcpTools.d.ts +57 -0
  31. package/dist/extension/mcpTools.js +132 -0
  32. package/dist/extension/pipeline/eval.d.ts +42 -5
  33. package/dist/extension/pipeline/eval.js +44 -0
  34. package/dist/extension/pipeline/goCommand.d.ts +18 -0
  35. package/dist/extension/pipeline/goCommand.js +139 -26
  36. package/dist/extension/pipeline/goCompareCommand.d.ts +18 -8
  37. package/dist/extension/pipeline/goCompareCommand.js +42 -23
  38. package/dist/extension/pipeline/orchestrator.js +9 -0
  39. package/dist/extension/pipeline/runCostTable.d.ts +37 -0
  40. package/dist/extension/pipeline/runCostTable.js +165 -0
  41. package/dist/extension/pipeline/runState.d.ts +19 -0
  42. package/dist/extension/pipeline/runState.js +11 -0
  43. package/dist/extension/pipeline/runner.d.ts +19 -0
  44. package/dist/extension/pipeline/runner.js +13 -1
  45. package/dist/extension/pipeline/scrubSecrets.js +2 -2
  46. package/dist/extension/pipeline/stages.d.ts +3 -1
  47. package/dist/extension/pipeline/stages.js +3 -1
  48. package/dist/extension/pipeline/types.d.ts +7 -4
  49. package/dist/extension/pipeline/verify.js +6 -1
  50. package/dist/extension/pipeline/worktree.js +3 -1
  51. package/dist/extension/provider.d.ts +7 -1
  52. package/dist/extension/provider.js +8 -1
  53. package/dist/extension/recall.js +5 -2
  54. package/dist/extension/rerouteNotice.d.ts +42 -0
  55. package/dist/extension/rerouteNotice.js +67 -0
  56. package/dist/extension/sessionRuns.d.ts +45 -0
  57. package/dist/extension/sessionRuns.js +77 -0
  58. package/dist/extension/subagents.d.ts +17 -7
  59. package/dist/extension/subagents.js +52 -7
  60. package/dist/launch.d.ts +17 -3
  61. package/dist/launch.js +22 -9
  62. package/dist/login.d.ts +7 -0
  63. package/dist/login.js +3 -1
  64. package/package.json +2 -2
package/README.md CHANGED
@@ -88,12 +88,16 @@ yagni -p "explain the deploy pipeline; ask_yagni if unsure" # one-shot / print
88
88
  yagni logout # revoke the token and clear it locally
89
89
  ```
90
90
 
91
- The agent runs on three opaque model tiers routed by YAGNI: `advanced` (the strongest,
92
- for judgment), `standard` (for execution) and `efficient` (cheapest).
93
- Interactive sessions default to `advanced`; pass `--model standard` or `--model
94
- efficient` to switch a run. The `/go` pipeline routes its own steps automatically
95
- judgment steps (plan, review) on `advanced`, execution steps (map, implement, fix) on
96
- `standard`.
91
+ The agent runs on five opaque model tiers routed by YAGNI: `balanced` (the
92
+ interactive default), `peak` (the strongest, for hard judgment), `advanced`,
93
+ `standard` (for execution) and `efficient` (cheapest). `balanced` is a strategy
94
+ rather than a single rung: the session drives on the `advanced` tier and
95
+ escalates a bounded number of hard judgment calls per session to `peak`, so the
96
+ strongest model is consulted where being wrong is expensive instead of being
97
+ paid for on every turn. Pass `--model peak`, `--model standard`, or `--model
98
+ efficient` to pin a run to one tier. The `/go` pipeline routes its own steps
99
+ automatically: judgment steps (plan, review) on `peak`, execution steps (map,
100
+ implement, fix) on `standard`.
97
101
 
98
102
  `login` opens a device-code flow: it prints a short code and a URL. Open the URL
99
103
  in a browser where you are signed in to YAGNI, enter the code, and approve. The
@@ -112,6 +116,22 @@ flags like `--model` and `--print/-p` work.
112
116
  agent calls **`ask_yagni`** and gets a cited answer from the YAGNI app instead
113
117
  of guessing.
114
118
 
119
+ ### Works with your existing repo setup
120
+
121
+ - A repo that already carries Claude Code assets keeps working with nothing to
122
+ edit: `.claude/skills` and `.claude/commands/*.md` load automatically (each
123
+ command becomes a `/name` slash command). Project-local assets load only
124
+ after a one-time per-folder trust prompt; set `YAGNI_DISABLE_CLAUDE_COMPAT=1`
125
+ to turn the bridge off.
126
+ - `AGENTS.md` and `CLAUDE.md` context files load from the working directory and
127
+ its ancestors, unmodified.
128
+ - MCP servers registered in your YAGNI workspace are available in-session: run
129
+ `/mcp` to list them. Calls are proxied through the YAGNI backend with your
130
+ own identity, so server credentials never reach the laptop and every call is
131
+ audited; mutating tools go through the same permission gate as file edits.
132
+ There is no local MCP config file: servers and credentials are managed in the
133
+ web app (Connections).
134
+
115
135
  ## Environments (profiles)
116
136
 
117
137
  YAGNI Code keeps a **sticky active environment**. Each environment ("profile") binds a
@@ -139,14 +159,43 @@ yagni use prod # switch back (sticky); prod is the def
139
159
 
140
160
  ## Configuration
141
161
 
142
- | Variable | Default | Purpose |
143
- | ---------------- | ------------------------ | -------------------------------------------------- |
144
- | `YAGNI_BASE_URL` | active environment's URL | Override the base URL for a **single run** (escape hatch). Prefer `yagni use` for anything sticky. |
162
+ | Variable | Default | Purpose |
163
+ | ----------------------------- | ------------------------ | -------------------------------------------------- |
164
+ | `YAGNI_BASE_URL` | active environment's URL | Override the base URL for a **single run** (escape hatch). Prefer `yagni use` for anything sticky. |
165
+ | `YAGNI_DISABLE_BRANDING` | unset | `1` skips the YAGNI Code system-prompt rewrite entirely, so the engine's assembled prompt passes through byte-exact (no identity swap, no company-brief injection). |
166
+ | `YAGNI_DISABLE_UPDATE_CHECK` | unset | `1` silences the new-version notice and the background update check. |
167
+ | `YAGNI_DISABLE_CLAUDE_COMPAT` | unset | `1` turns off the zero-config `.claude` assets bridge (skills and commands). |
168
+ | `YAGNI_DISABLE_CRASH_REPORTS` | unset | `1` disables crash reporting (see "What leaves your machine"). |
145
169
 
146
170
  Credentials live in `~/.yagni-code/profiles/<name>.json` (mode `0600`); the active
147
171
  environment is recorded in `~/.yagni-code/config.json`. A pre-profiles
148
172
  `~/.yagni-code/credentials.json` is migrated automatically on first run.
149
173
 
174
+ Device tokens are revocable from both ends: `yagni logout` revokes the current
175
+ one, and a workspace admin can list every connected device and revoke any token
176
+ from the web app (Settings, YAGNI Code, Connected devices). Tokens are stored
177
+ hashed on the server and a revoked token stops validating immediately.
178
+
179
+ ### What leaves your machine
180
+
181
+ Everything the agent needs (model calls, grounding lookups, MCP calls, usage
182
+ metering) goes to your YAGNI backend over the authenticated session. No
183
+ third-party analytics or telemetry SDK runs on your laptop, and the underlying
184
+ engine's own telemetry and version pings are disabled at launch. Two outbound
185
+ flows exist beyond your YAGNI backend and your own repo tooling, disclosed
186
+ precisely:
187
+
188
+ - **Update check.** In the background after launch, the CLI asks the npm
189
+ registry (or your configured private registry mirror) whether a newer
190
+ version of this package exists, so the next launch can show a one-line
191
+ nudge. Package metadata only; no code or workspace data. Disable with
192
+ `YAGNI_DISABLE_UPDATE_CHECK=1`.
193
+ - **Crash reports.** When the client itself crashes, a sanitized crash report
194
+ (error class, scrubbed message and stack trace; secrets and env values
195
+ redacted, paths reduced) is sent to your YAGNI backend, not to any
196
+ third-party crash service, so we can fix the crash before you have to report
197
+ it. Disable with `YAGNI_DISABLE_CRASH_REPORTS=1`.
198
+
150
199
  ## Troubleshooting
151
200
 
152
201
  - **`Not logged in to environment "<name>" … Run \`yagni login\` first.`** — no
@@ -9,6 +9,14 @@
9
9
  * `--prompt-template` flags, so nothing repo-local is ever written and no
10
10
  * per-profile settings file accumulates stale per-repo paths.
11
11
  *
12
+ * The same seams carry installed/enabled Claude Code plugins and the repo's
13
+ * own `.claude-plugin/marketplace.json` plugins (see claudePlugins.ts):
14
+ * plugin skills → `--skill`, plugin commands → `--prompt-template`, plugin
15
+ * `agents/` dirs → YAGNI_CLAUDE_AGENT_DIRS for the extension's subagent
16
+ * discovery. `.claude/rules` dirs travel as YAGNI_CLAUDE_RULES_DIRS for the
17
+ * extension's system-prompt rules injection. Design + on-disk evidence:
18
+ * docs/superpowers/specs/2026-08-08-claude-plugins-design.md.
19
+ *
12
20
  * Trust: pi gates project-local resources behind a per-folder trust decision
13
21
  * stored in `<agentDir>/trust.json`. Explicit CLI paths bypass that gate, so
14
22
  * the launcher enforces the SAME decision itself: project `.claude` dirs are
@@ -20,14 +28,24 @@
20
28
  *
21
29
  * Set YAGNI_DISABLE_CLAUDE_COMPAT=1 to turn the whole bridge off.
22
30
  */
31
+ import { type PluginAssets } from "./claudePlugins.js";
23
32
  export declare const CLAUDE_COMPAT_DISABLE_ENV = "YAGNI_DISABLE_CLAUDE_COMPAT";
33
+ /** Plugin `agents/` dirs for the extension's subagent discovery (delimiter-joined). */
34
+ export declare const CLAUDE_AGENT_DIRS_ENV = "YAGNI_CLAUDE_AGENT_DIRS";
35
+ /** `.claude/rules` dirs for the extension's rules injection (delimiter-joined). */
36
+ export declare const CLAUDE_RULES_DIRS_ENV = "YAGNI_CLAUDE_RULES_DIRS";
24
37
  export interface ClaudeAssetDirs {
25
38
  skills: string | null;
26
39
  commands: string | null;
40
+ rules: string | null;
27
41
  }
28
42
  export interface ClaudeCompatProbe {
29
43
  project: ClaudeAssetDirs;
30
44
  user: ClaudeAssetDirs;
45
+ /** Locally-present plugins sourced from repo config; trust-gated. */
46
+ projectPlugins: PluginAssets[];
47
+ /** Plugins the user enabled in their own `~/.claude`; no ceremony. */
48
+ userPlugins: PluginAssets[];
31
49
  /** Nearest recorded trust decision for the cwd (pi trust.json), if any. */
32
50
  projectTrust: boolean | null;
33
51
  interactive: boolean;
@@ -37,7 +55,13 @@ export interface ClaudeCompatDecision {
37
55
  userArgv: string[];
38
56
  /** Flags for project dirs; pass only once trust is established. */
39
57
  projectArgv: string[];
40
- /** Ask the user for a trust decision before wiring projectArgv. */
58
+ /** Plugin agent dirs, split by the same trust posture. */
59
+ userAgentDirs: string[];
60
+ projectAgentDirs: string[];
61
+ /** `.claude/rules` dirs, user first so project rules win in the prompt. */
62
+ userRulesDirs: string[];
63
+ projectRulesDirs: string[];
64
+ /** Ask the user for a trust decision before wiring any project content. */
41
65
  needsPrompt: boolean;
42
66
  }
43
67
  /** Pure policy: what to wire, and whether a trust prompt is required first. */
@@ -67,12 +91,19 @@ export interface ClaudeCompatDeps {
67
91
  confirm?: (question: string) => Promise<boolean>;
68
92
  }
69
93
  export declare function compatDisabled(env: NodeJS.ProcessEnv): boolean;
94
+ export interface ClaudeCompatLaunch {
95
+ /** Extra pi flags (`--skill` / `--prompt-template` paths). */
96
+ argv: string[];
97
+ /** Extra child env (plugin agent dirs, rules dirs) for the extension. */
98
+ env: Record<string, string>;
99
+ }
70
100
  /**
71
- * The extra pi argv for this launch: user-scope dirs plus project dirs once
72
- * trusted, asking (and persisting) the trust decision when needed. Everything
73
- * is fail-soft compat can only add flags, never break a launch.
101
+ * The extra pi argv + child env for this launch: user-scope dirs and plugins
102
+ * unconditionally, project-sourced dirs/plugins/rules once trusted, asking
103
+ * (and persisting) the trust decision when needed. Everything is fail-soft
104
+ * compat can only add flags/env, never break a launch.
74
105
  */
75
- export declare function claudeCompatArgs(deps: ClaudeCompatDeps): Promise<string[]>;
106
+ export declare function claudeCompatArgs(deps: ClaudeCompatDeps): Promise<ClaudeCompatLaunch>;
76
107
  /** Whether pi's trust file exists yet (used only for messaging). */
77
108
  export declare function trustFileExists(agentDir: string): boolean;
78
109
  //# sourceMappingURL=claudeCompat.d.ts.map
@@ -9,6 +9,14 @@
9
9
  * `--prompt-template` flags, so nothing repo-local is ever written and no
10
10
  * per-profile settings file accumulates stale per-repo paths.
11
11
  *
12
+ * The same seams carry installed/enabled Claude Code plugins and the repo's
13
+ * own `.claude-plugin/marketplace.json` plugins (see claudePlugins.ts):
14
+ * plugin skills → `--skill`, plugin commands → `--prompt-template`, plugin
15
+ * `agents/` dirs → YAGNI_CLAUDE_AGENT_DIRS for the extension's subagent
16
+ * discovery. `.claude/rules` dirs travel as YAGNI_CLAUDE_RULES_DIRS for the
17
+ * extension's system-prompt rules injection. Design + on-disk evidence:
18
+ * docs/superpowers/specs/2026-08-08-claude-plugins-design.md.
19
+ *
12
20
  * Trust: pi gates project-local resources behind a per-folder trust decision
13
21
  * stored in `<agentDir>/trust.json`. Explicit CLI paths bypass that gate, so
14
22
  * the launcher enforces the SAME decision itself: project `.claude` dirs are
@@ -22,8 +30,13 @@
22
30
  */
23
31
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmdirSync, statSync, writeFileSync } from "node:fs";
24
32
  import { homedir } from "node:os";
25
- import { dirname, join, resolve } from "node:path";
33
+ import { delimiter, dirname, join, resolve } from "node:path";
34
+ import { discoverClaudePlugins } from "./claudePlugins.js";
26
35
  export const CLAUDE_COMPAT_DISABLE_ENV = "YAGNI_DISABLE_CLAUDE_COMPAT";
36
+ /** Plugin `agents/` dirs for the extension's subagent discovery (delimiter-joined). */
37
+ export const CLAUDE_AGENT_DIRS_ENV = "YAGNI_CLAUDE_AGENT_DIRS";
38
+ /** `.claude/rules` dirs for the extension's rules injection (delimiter-joined). */
39
+ export const CLAUDE_RULES_DIRS_ENV = "YAGNI_CLAUDE_RULES_DIRS";
27
40
  function dirsToArgv(dirs) {
28
41
  const argv = [];
29
42
  if (dirs.skills)
@@ -32,13 +45,30 @@ function dirsToArgv(dirs) {
32
45
  argv.push("--prompt-template", dirs.commands);
33
46
  return argv;
34
47
  }
48
+ function pluginsToArgv(plugins) {
49
+ const argv = [];
50
+ for (const plugin of plugins) {
51
+ for (const path of plugin.skillPaths)
52
+ argv.push("--skill", path);
53
+ for (const path of plugin.commandPaths)
54
+ argv.push("--prompt-template", path);
55
+ }
56
+ return argv;
57
+ }
35
58
  /** Pure policy: what to wire, and whether a trust prompt is required first. */
36
59
  export function decideClaudeCompat(probe) {
37
- const projectArgv = dirsToArgv(probe.project);
60
+ const projectArgv = [...dirsToArgv(probe.project), ...pluginsToArgv(probe.projectPlugins)];
61
+ const projectAgentDirs = probe.projectPlugins.flatMap((p) => p.agentDirs);
62
+ const projectRulesDirs = probe.project.rules ? [probe.project.rules] : [];
63
+ const hasProjectContent = projectArgv.length > 0 || projectAgentDirs.length > 0 || projectRulesDirs.length > 0;
38
64
  return {
39
- userArgv: dirsToArgv(probe.user),
65
+ userArgv: [...dirsToArgv(probe.user), ...pluginsToArgv(probe.userPlugins)],
40
66
  projectArgv,
41
- needsPrompt: probe.interactive && projectArgv.length > 0 && probe.projectTrust === null,
67
+ userAgentDirs: probe.userPlugins.flatMap((p) => p.agentDirs),
68
+ projectAgentDirs,
69
+ userRulesDirs: probe.user.rules ? [probe.user.rules] : [],
70
+ projectRulesDirs,
71
+ needsPrompt: probe.interactive && hasProjectContent && probe.projectTrust === null,
42
72
  };
43
73
  }
44
74
  function isDirectory(path) {
@@ -53,9 +83,11 @@ function isDirectory(path) {
53
83
  export function probeClaudeDirs(root) {
54
84
  const skills = join(root, ".claude", "skills");
55
85
  const commands = join(root, ".claude", "commands");
86
+ const rules = join(root, ".claude", "rules");
56
87
  return {
57
88
  skills: isDirectory(skills) ? skills : null,
58
89
  commands: isDirectory(commands) ? commands : null,
90
+ rules: isDirectory(rules) ? rules : null,
59
91
  };
60
92
  }
61
93
  // ── pi trust.json interop ───────────────────────────────────────────────────
@@ -114,7 +146,14 @@ export function readTrustDecision(agentDir, cwd) {
114
146
  export function writeTrustDecision(agentDir, cwd, decision) {
115
147
  const path = trustPath(agentDir);
116
148
  const lock = `${path}.lock`;
117
- mkdirSync(agentDir, { recursive: true });
149
+ try {
150
+ mkdirSync(agentDir, { recursive: true });
151
+ }
152
+ catch {
153
+ // Fail-soft: an uncreatable agent dir must not discard the user's answer
154
+ // for this launch — the caller still applies it in-process.
155
+ return;
156
+ }
118
157
  let locked = false;
119
158
  for (let attempt = 0; attempt < 10; attempt++) {
120
159
  try {
@@ -158,38 +197,61 @@ export function compatDisabled(env) {
158
197
  return value !== undefined && value !== "" && value !== "0";
159
198
  }
160
199
  /**
161
- * The extra pi argv for this launch: user-scope dirs plus project dirs once
162
- * trusted, asking (and persisting) the trust decision when needed. Everything
163
- * is fail-soft compat can only add flags, never break a launch.
200
+ * The extra pi argv + child env for this launch: user-scope dirs and plugins
201
+ * unconditionally, project-sourced dirs/plugins/rules once trusted, asking
202
+ * (and persisting) the trust decision when needed. Everything is fail-soft
203
+ * compat can only add flags/env, never break a launch.
164
204
  */
165
205
  export async function claudeCompatArgs(deps) {
166
206
  const env = deps.env ?? process.env;
167
207
  if (compatDisabled(env))
168
- return [];
208
+ return { argv: [], env: {} };
169
209
  const home = deps.homeDir ?? homedir();
170
210
  const projectTrust = readTrustDecision(deps.agentDir, deps.cwd);
211
+ let plugins;
212
+ try {
213
+ plugins = discoverClaudePlugins({ cwd: deps.cwd, homeDir: home });
214
+ }
215
+ catch {
216
+ plugins = { user: [], project: [] };
217
+ }
171
218
  const decision = decideClaudeCompat({
172
219
  project: probeClaudeDirs(deps.cwd),
173
220
  user: probeClaudeDirs(home),
221
+ projectPlugins: plugins.project,
222
+ userPlugins: plugins.user,
174
223
  projectTrust,
175
224
  interactive: deps.interactive ?? (process.stdin.isTTY === true && process.stdout.isTTY === true),
176
225
  });
177
- const argv = [...decision.userArgv];
178
- if (decision.projectArgv.length > 0) {
179
- let trusted = projectTrust;
180
- if (decision.needsPrompt && deps.confirm) {
181
- try {
182
- trusted = await deps.confirm("This folder has Claude Code assets (.claude/skills, .claude/commands). Trust this folder and load them?");
183
- writeTrustDecision(deps.agentDir, deps.cwd, trusted);
184
- }
185
- catch {
186
- trusted = null;
187
- }
226
+ let trusted = projectTrust;
227
+ if (decision.needsPrompt && deps.confirm) {
228
+ try {
229
+ trusted = await deps.confirm("This folder has Claude Code assets (.claude skills, commands, rules, or plugins). Trust this folder and load them?");
230
+ writeTrustDecision(deps.agentDir, deps.cwd, trusted);
231
+ }
232
+ catch {
233
+ trusted = null;
188
234
  }
189
- if (trusted === true)
190
- argv.push(...decision.projectArgv);
191
235
  }
192
- return argv;
236
+ const argv = [...decision.userArgv];
237
+ if (trusted === true)
238
+ argv.push(...decision.projectArgv);
239
+ const agentDirs = [
240
+ ...decision.userAgentDirs,
241
+ ...(trusted === true ? decision.projectAgentDirs : []),
242
+ ];
243
+ // User rules first, project rules last: the extension appends in order, so
244
+ // project rules end up closest to the task — matching Claude Code priority.
245
+ const rulesDirs = [
246
+ ...decision.userRulesDirs,
247
+ ...(trusted === true ? decision.projectRulesDirs : []),
248
+ ];
249
+ const extraEnv = {};
250
+ if (agentDirs.length > 0)
251
+ extraEnv[CLAUDE_AGENT_DIRS_ENV] = agentDirs.join(delimiter);
252
+ if (rulesDirs.length > 0)
253
+ extraEnv[CLAUDE_RULES_DIRS_ENV] = rulesDirs.join(delimiter);
254
+ return { argv, env: extraEnv };
193
255
  }
194
256
  /** Whether pi's trust file exists yet (used only for messaging). */
195
257
  export function trustFileExists(agentDir) {
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Claude Code plugin + marketplace discovery (local content only).
3
+ *
4
+ * Finds the plugin content a Claude Code user already has on disk and exposes
5
+ * it as asset paths the launcher can feed through the existing compat seams:
6
+ * skills → `--skill`, commands → `--prompt-template`, agents → the subagent
7
+ * discovery env var. Two sources:
8
+ *
9
+ * 1. Installed plugins: `~/.claude/plugins/installed_plugins.json` (version-2
10
+ * ledger of `name@marketplace` → cache checkouts), enabled through
11
+ * `enabledPlugins` in the user's `~/.claude/settings.json` (user scope) or
12
+ * the repo's `.claude/settings{,.local}.json` (project scope). When a
13
+ * ledger entry is gone, the marketplace checkout recorded in
14
+ * `known_marketplaces.json` is tried as a fallback.
15
+ * 2. The repo's own `.claude-plugin/marketplace.json` (the internal-
16
+ * marketplace pattern): every plugin it defines with a locally-present
17
+ * relative-path source loads on the project side, unless explicitly
18
+ * disabled via `enabledPlugins`.
19
+ *
20
+ * Deliberately NOT here: network installation of any kind, plugin hooks, MCP
21
+ * servers, LSP servers, themes, output styles. Discovery is read-only.
22
+ *
23
+ * Everything is fail-soft: malformed JSON, missing dirs, or hostile path
24
+ * entries degrade to "that plugin absent" — never a failed launch. Path
25
+ * entries from marketplace/plugin manifests are containment-checked (realpath
26
+ * inside the expected root) so a malicious `source` or component override
27
+ * cannot reach outside its repo/plugin.
28
+ */
29
+ export interface PluginAssets {
30
+ /** The plugin's name (marketplace-entry name — the `enabledPlugins` key half). */
31
+ name: string;
32
+ /** Absolute plugin root directory. */
33
+ root: string;
34
+ /** Dirs (or a lone root SKILL.md file) for pi `--skill`. */
35
+ skillPaths: string[];
36
+ /** Dirs or files for pi `--prompt-template`. */
37
+ commandPaths: string[];
38
+ /** Dirs of Claude Code-format agent markdown for subagent discovery. */
39
+ agentDirs: string[];
40
+ }
41
+ export interface DiscoveredClaudePlugins {
42
+ /** Enabled via the user's own `~/.claude` config; loads without ceremony. */
43
+ user: PluginAssets[];
44
+ /** Sourced from repo config; wire only once the folder is trusted. */
45
+ project: PluginAssets[];
46
+ }
47
+ /**
48
+ * Resolve `candidate` (a relative path from untrusted JSON) against `root` and
49
+ * return its realpath only when it exists AND stays inside `root` after
50
+ * symlink resolution. Absolute candidates and any `../` escape are rejected.
51
+ */
52
+ export declare function containedExistingPath(root: string, candidate: string): string | null;
53
+ export interface InstalledPluginEntry {
54
+ installPath: string;
55
+ projectPath?: string;
56
+ }
57
+ /** `~/.claude/plugins/installed_plugins.json` → `name@marketplace` → entries. */
58
+ export declare function readInstalledPlugins(homeDir: string): Map<string, InstalledPluginEntry[]>;
59
+ export type EnableScope = "user" | "project";
60
+ export interface EnableState {
61
+ enabled: boolean;
62
+ scope: EnableScope;
63
+ }
64
+ /**
65
+ * `enabledPlugins` merged across scopes, later (more specific) winning:
66
+ * user `~/.claude/settings.json` < project `.claude/settings.json` <
67
+ * project `.claude/settings.local.json`.
68
+ */
69
+ export declare function readEnabledPlugins(cwd: string, homeDir: string): Map<string, EnableState>;
70
+ /** `~/.claude/plugins/known_marketplaces.json` → marketplace name → checkout dir. */
71
+ export declare function readKnownMarketplaces(homeDir: string): Map<string, string>;
72
+ export interface MarketplaceEntry {
73
+ name: string;
74
+ source: unknown;
75
+ }
76
+ export interface Marketplace {
77
+ name: string;
78
+ /** Marketplace root: the dir containing `.claude-plugin/`. */
79
+ root: string;
80
+ /** `metadata.pluginRoot`, prepended to bare relative sources. */
81
+ pluginRoot: string | null;
82
+ plugins: MarketplaceEntry[];
83
+ }
84
+ /** Parse `<root>/.claude-plugin/marketplace.json`; null when absent/unusable. */
85
+ export declare function readMarketplace(root: string): Marketplace | null;
86
+ /**
87
+ * The local directory a marketplace entry's source points at, when it is a
88
+ * relative-path source that exists inside the marketplace root. Object
89
+ * sources (github/url/npm/...) are network installs → null here.
90
+ */
91
+ export declare function resolveLocalPluginRoot(mp: Marketplace, entry: MarketplaceEntry): string | null;
92
+ /**
93
+ * The bridgeable assets inside one plugin checkout, per Claude Code's
94
+ * component rules: `skills/` (manifest `skills` ADDS dirs), `commands/`
95
+ * (manifest REPLACES), `agents/` (manifest REPLACES; dirs only — pi's
96
+ * discovery reads whole dirs), root `SKILL.md` fallback. Null when the plugin
97
+ * has nothing we can bridge.
98
+ */
99
+ export declare function pluginAssets(root: string, name: string): PluginAssets | null;
100
+ export interface DiscoverPluginsDeps {
101
+ cwd: string;
102
+ homeDir: string;
103
+ }
104
+ /**
105
+ * All locally-present Claude Code plugin content relevant to `cwd`, split by
106
+ * the trust posture the launcher must apply. Never throws.
107
+ */
108
+ export declare function discoverClaudePlugins(deps: DiscoverPluginsDeps): DiscoveredClaudePlugins;
109
+ //# sourceMappingURL=claudePlugins.d.ts.map