@uniqbit/mate-core 0.15.3 → 0.15.4-canary.10

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 (94) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +4 -0
  2. package/claude-plugin/hooks/artifact-finish-nudge.mjs +8 -0
  3. package/claude-plugin/hooks/hooks.json +37 -0
  4. package/claude-plugin/hooks/session-banner.mjs +8 -0
  5. package/claude-plugin/hooks/ts-loader.mjs +28 -0
  6. package/claude-plugin/hooks/validate-artifact-path.mjs +8 -0
  7. package/package.json +4 -2
  8. package/src/cli/commands/artifact/finish/openspec.ts +1 -5
  9. package/src/cli/commands/companion/hub.ts +99 -0
  10. package/src/cli/commands/companion/link.ts +0 -5
  11. package/src/cli/commands/companion/tui.ts +3 -2
  12. package/src/cli/commands/doctor.ts +254 -170
  13. package/src/cli/commands/install.ts +18 -14
  14. package/src/cli/commands/launch/shared.ts +4 -4
  15. package/src/cli/commands/plugin/install.ts +94 -0
  16. package/src/cli/commands/plugin/plugin.ts +17 -0
  17. package/src/cli/commands/setup.ts +4 -6
  18. package/src/cli/commands/update.ts +9 -11
  19. package/src/cli/companion-link-wizard.tsx +22 -78
  20. package/src/cli/main.ts +57 -13
  21. package/src/cli/plugin-commands.ts +4 -4
  22. package/src/cli/repo-list-table.tsx +2 -7
  23. package/src/cli/usage.ts +7 -3
  24. package/src/distribution.ts +3 -5
  25. package/src/framework.ts +4 -36
  26. package/src/hooks/artifact-finish-nudge.ts +212 -0
  27. package/src/hooks/session-banner.ts +25 -0
  28. package/src/hooks/validate-artifact-path.ts +236 -0
  29. package/src/index.ts +4 -0
  30. package/src/lib/context-mode-package.ts +5 -3
  31. package/src/lib/install.ts +31 -43
  32. package/src/lib/orchestrator/adapters/base.ts +7 -8
  33. package/src/lib/orchestrator/adapters/claude.ts +16 -0
  34. package/src/lib/orchestrator/adapters/opencode.ts +10 -60
  35. package/src/lib/orchestrator/companion-hub.ts +380 -0
  36. package/src/lib/orchestrator/companion-store.ts +1 -35
  37. package/src/lib/orchestrator/config-store.ts +94 -7
  38. package/src/lib/orchestrator/editor.ts +4 -20
  39. package/src/lib/orchestrator/framework-context.ts +119 -24
  40. package/src/lib/orchestrator/global-config-store.ts +1 -5
  41. package/src/lib/orchestrator/launcher.ts +3 -9
  42. package/src/lib/orchestrator/migration.ts +0 -23
  43. package/src/lib/orchestrator/opencode-guidance.ts +1 -2
  44. package/src/lib/orchestrator/repo-local-registry.ts +3 -1
  45. package/src/lib/orchestrator/root-context.ts +117 -0
  46. package/src/lib/orchestrator/setup-compatibilities.ts +1 -1
  47. package/src/lib/orchestrator/setup-preflight.ts +5 -18
  48. package/src/lib/orchestrator/types.ts +27 -19
  49. package/src/lib/orchestrator/working-repo-store.ts +9 -3
  50. package/src/lib/package-paths.ts +37 -0
  51. package/src/lib/update-checker.ts +5 -5
  52. package/src/playbooks/companion-guidance.ts +7 -7
  53. package/src/runtime/env.ts +0 -4
  54. package/src/templates/capabilities/openspec-cap/mate-skills/{mate-artifact-finish → agents/mate-artifact-finish}/SKILL.md +8 -8
  55. package/src/templates/capabilities/openspec-cap/mate-skills/{mate-artifact-finish → agents/mate-artifact-finish}/references/openspec.md +3 -3
  56. package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/SKILL.md +58 -0
  57. package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/references/openspec.md +139 -0
  58. package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +6 -5
  59. package/src/templates/root/TEMPLATE_AGENTS.md +2 -0
  60. package/src/templates/root/TEMPLATE_CLAUDE.md +2 -0
  61. package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3541 -0
  62. package/src/tools/setup/capabilities/context-mode.ts +57 -83
  63. package/src/tools/setup/capabilities/graphify-shared.ts +27 -0
  64. package/src/tools/setup/capabilities/graphify.ts +86 -295
  65. package/src/tools/setup/capabilities/openspec.ts +27 -30
  66. package/src/tools/setup/capabilities/react-doctor.ts +37 -37
  67. package/src/tools/setup/capabilities/rtk.ts +4 -1
  68. package/src/tools/setup/capabilities/tokensave-shared.ts +6 -0
  69. package/src/tools/setup/capabilities/tokensave.ts +50 -71
  70. package/src/tools/setup/context-services.ts +29 -0
  71. package/src/tools/setup/dynamic-plugins/host.ts +2 -1
  72. package/src/tools/setup/dynamic-plugins/hydrate.ts +3 -13
  73. package/src/tools/setup/dynamic-plugins/install.ts +124 -109
  74. package/src/tools/setup/dynamic-plugins/loader.ts +23 -58
  75. package/src/tools/setup/dynamic-plugins/paths.ts +8 -19
  76. package/src/tools/setup/dynamic-plugins/registry-hint.ts +14 -0
  77. package/src/tools/setup/engine.ts +65 -1
  78. package/src/tools/setup/mate.ts +28 -31
  79. package/src/tools/setup/plugin.ts +81 -0
  80. package/src/tools/setup/plugins/gitignore.ts +19 -9
  81. package/src/tools/setup/plugins/guidance.ts +2 -6
  82. package/src/tools/setup/policy.ts +4 -7
  83. package/src/tools/setup/providers/agent-file-sections.ts +78 -0
  84. package/src/tools/setup/providers/claude-format.ts +159 -0
  85. package/src/tools/setup/providers/claude.ts +283 -292
  86. package/src/tools/setup/providers/opencode-format.ts +146 -0
  87. package/src/tools/setup/providers/opencode.ts +171 -66
  88. package/src/tools/setup/providers/skill-tree.ts +38 -0
  89. package/src/tools/setup.ts +12 -7
  90. package/src/tui.ts +5 -0
  91. package/src/templates/capabilities/openspec-cap/claude/hooks/mate-artifact-finish.sh +0 -161
  92. package/src/templates/providers/claude/.claude/hooks/mate-session-banner +0 -28
  93. package/src/templates/providers/claude/.claude/hooks/validate-artifact-path +0 -242
  94. package/src/tools/setup/dynamic-plugins/pin-store.ts +0 -30
@@ -1,8 +1,8 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
- import type { CapabilityPlugin, SetupContext } from "../plugin";
5
- import { mergeDir, pruneEmptyAncestors } from "../utils";
4
+ import type { CapabilityPlugin, RuntimeContributionsByRuntime, SetupContext } from "../plugin";
5
+ import { pruneEmptyAncestors } from "../utils";
6
6
 
7
7
  export const SKILL_SRC = path.join(
8
8
  import.meta.dirname,
@@ -13,22 +13,6 @@ export const CLAUDE_HOOK_SRC = path.join(
13
13
  "../../../templates/capabilities/react-doctor/claude/hooks/react-doctor.sh",
14
14
  );
15
15
 
16
- export async function applyReactDoctorSkills(skillsDir: string): Promise<void> {
17
- await mergeDir(SKILL_SRC, skillsDir);
18
- }
19
-
20
- export async function teardownReactDoctorSkills(
21
- skillsDir: string,
22
- companionPath: string,
23
- ): Promise<void> {
24
- try {
25
- await fs.rm(skillsDir, { recursive: true, force: true });
26
- } catch {
27
- /* not present */
28
- }
29
- await pruneEmptyAncestors(path.dirname(skillsDir), companionPath);
30
- }
31
-
32
16
  export function createReactDoctorPlugin(): CapabilityPlugin {
33
17
  return {
34
18
  id: "react-doctor",
@@ -37,6 +21,41 @@ export function createReactDoctorPlugin(): CapabilityPlugin {
37
21
  description: "Install React Doctor skills and Claude post-edit scans.",
38
22
  defaultSelected: true,
39
23
  isEnabled: (config) => (config.capabilities ?? []).some((c) => c.name === "react-doctor"),
24
+ // Skills, session hooks, and permission pre-seeds are declared and
25
+ // reconciled by each runtime's Runtime Surface. Only the hook script file
26
+ // itself is copied imperatively below (script files are not a config
27
+ // format the surface owns).
28
+ getRuntimeContributions(ctx: SetupContext): RuntimeContributionsByRuntime {
29
+ const hookCommand = `sh "${ctx.companionPath}/.claude/hooks/react-doctor.sh"`;
30
+ const skillTrees = [{ name: "react-doctor", sourceDir: SKILL_SRC }];
31
+ return {
32
+ claude: {
33
+ hookGroups: [
34
+ // Record edits cheaply, then scan once when the edited turn finishes.
35
+ {
36
+ event: "PostToolUse",
37
+ marker: "react-doctor.sh",
38
+ group: {
39
+ matcher: "Write|Edit|MultiEdit|NotebookEdit|ApplyPatch",
40
+ hooks: [{ type: "command", command: hookCommand, timeout: 5 }],
41
+ },
42
+ },
43
+ {
44
+ event: "Stop",
45
+ marker: "react-doctor.sh",
46
+ group: { hooks: [{ type: "command", command: hookCommand, timeout: 45 }] },
47
+ },
48
+ ],
49
+ permissionEntries: [
50
+ "Skill(react-doctor)",
51
+ "Bash(npx react-doctor:*)",
52
+ "Bash(npx react-doctor@latest *)",
53
+ ],
54
+ skillTrees,
55
+ },
56
+ opencode: { skillTrees },
57
+ };
58
+ },
40
59
  async apply(ctx: SetupContext) {
41
60
  // Migrate: remove legacy shared .agents/skills/react-doctor/ from previous installs.
42
61
  try {
@@ -57,18 +76,12 @@ export function createReactDoctorPlugin(): CapabilityPlugin {
57
76
  forProvider: {
58
77
  claude: {
59
78
  async apply(ctx: SetupContext) {
60
- const skillsDir = path.join(ctx.companionPath, ".claude", "skills", "react-doctor");
61
- await applyReactDoctorSkills(skillsDir);
62
79
  const hookDest = path.join(ctx.companionPath, ".claude", "hooks", "react-doctor.sh");
63
80
  await fs.mkdir(path.dirname(hookDest), { recursive: true });
64
81
  await fs.copyFile(CLAUDE_HOOK_SRC, hookDest);
65
82
  await fs.chmod(hookDest, 0o755);
66
83
  },
67
84
  async teardown(ctx: SetupContext) {
68
- await teardownReactDoctorSkills(
69
- path.join(ctx.companionPath, ".claude", "skills", "react-doctor"),
70
- ctx.companionPath,
71
- );
72
85
  try {
73
86
  await fs.unlink(path.join(ctx.companionPath, ".claude", "hooks", "react-doctor.sh"));
74
87
  } catch {
@@ -80,19 +93,6 @@ export function createReactDoctorPlugin(): CapabilityPlugin {
80
93
  );
81
94
  },
82
95
  },
83
- opencode: {
84
- async apply(ctx: SetupContext) {
85
- await applyReactDoctorSkills(
86
- path.join(ctx.companionPath, ".opencode", "skills", "react-doctor"),
87
- );
88
- },
89
- async teardown(ctx: SetupContext) {
90
- await teardownReactDoctorSkills(
91
- path.join(ctx.companionPath, ".opencode", "skills", "react-doctor"),
92
- ctx.companionPath,
93
- );
94
- },
95
- },
96
96
  },
97
97
  };
98
98
  }
@@ -1,4 +1,4 @@
1
- import type { CapabilityPlugin, SetupContext } from "../plugin";
1
+ import type { CapabilityPlugin, RuntimeContributionsByRuntime, SetupContext } from "../plugin";
2
2
  import type { InstallRequirement } from "../install-contract";
3
3
  import { isCommandOnPath, runShellCommand, runShellCommandSilently } from "../utils";
4
4
  import { confirm } from "../../../cli/confirm";
@@ -52,6 +52,9 @@ export function createRtkPlugin(deps: RtkDeps = {}): CapabilityPlugin {
52
52
  defaultSelected: false,
53
53
  isEnabled: (config) =>
54
54
  (config.capabilities ?? []).some((capability) => capability.name === "rtk"),
55
+ getRuntimeContributions(): RuntimeContributionsByRuntime {
56
+ return { claude: { permissionEntries: ["Bash(rtk:*)"] } };
57
+ },
55
58
  getInstallRequirements: (): InstallRequirement[] => {
56
59
  const command = checkBrew() ? RTK_INSTALL_CMD_BREW : RTK_INSTALL_CMD_FALLBACK;
57
60
  return [
@@ -1 +1,7 @@
1
1
  export const TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES = [".tokensave/"];
2
+
3
+ // Marker that identifies the tokensave installer's CLAUDE.md append block.
4
+ // Declared here (capability-owned) and consumed by the Claude Runtime Surface
5
+ // when reconciling working-repo files at launch.
6
+ export const TOKENSAVE_CLAUDE_MD_MARKER =
7
+ "## MANDATORY: No Explore Agents When Tokensave Is Available";
@@ -3,29 +3,20 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
 
5
5
  import { resolveGitInfoExcludePath } from "../git-utils";
6
- import type { CapabilityPlugin } from "../plugin";
6
+ import type { CapabilityPlugin, RuntimeContributionsByRuntime } from "../plugin";
7
7
  import { isCommandOnPath, resolveCommandOnPath, runCommand, runShellCommand } from "../utils";
8
+ import { TOKENSAVE_CLAUDE_MD_MARKER } from "./tokensave-shared";
8
9
  export { TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES } from "./tokensave-shared";
9
10
 
10
11
  export const TOKENSAVE_SUPPORTED_AGENTS = new Set(["claude", "opencode"]);
11
12
  export const TOKENSAVE_STORE_DIR = ".tokensave";
12
13
  export const TOKENSAVE_MIN_RUST_VERSION = "1.91.0";
13
- const TOKENSAVE_CLAUDE_MD_MARKER = "## MANDATORY: No Explore Agents When Tokensave Is Available";
14
14
  const TOKENSAVE_STORE_EXCLUDE_ENTRY = `${TOKENSAVE_STORE_DIR}/`;
15
15
  const TOKENSAVE_BREW_INSTALL_CMD = "brew install aovestdipaperino/tap/tokensave";
16
16
  const TOKENSAVE_CARGO_INSTALL_CMD = "cargo install --locked tokensave";
17
17
  const TOKENSAVE_SCOOP_INSTALL_CMD =
18
18
  "scoop bucket add tokensave https://github.com/aovestdipaperino/scoop-bucket && scoop install tokensave";
19
19
 
20
- interface CompanionOpenCodeSettings {
21
- mcp?: Record<string, unknown>;
22
- [key: string]: unknown;
23
- }
24
-
25
- function getCompanionOpenCodeConfigPath(companionPath: string): string {
26
- return path.join(companionPath, ".opencode", "opencode.json");
27
- }
28
-
29
20
  export interface TokensaveRunResult {
30
21
  ok: boolean;
31
22
  stderr: string;
@@ -34,10 +25,14 @@ export interface TokensaveRunResult {
34
25
 
35
26
  export const tokensaveDeps = {
36
27
  run(args: string[], cwd: string): TokensaveRunResult {
28
+ // env is passed explicitly: bun's spawnSync otherwise resolves the binary
29
+ // against the process's original PATH, ignoring in-process PATH changes
30
+ // (which test stubs and shell-integration wrappers rely on).
37
31
  const result = spawnSync("tokensave", args, {
38
32
  cwd,
39
33
  encoding: "utf8",
40
34
  stdio: ["ignore", "pipe", "pipe"],
35
+ env: process.env,
41
36
  });
42
37
  return {
43
38
  ok: result.status === 0 && !result.error,
@@ -58,6 +53,7 @@ export const tokensaveDeps = {
58
53
  const result = spawnSync("rustc", ["--version"], {
59
54
  encoding: "utf8",
60
55
  stdio: ["ignore", "pipe", "pipe"],
56
+ env: process.env,
61
57
  });
62
58
  return {
63
59
  ok: result.status === 0 && !result.error,
@@ -73,41 +69,6 @@ export const tokensaveDeps = {
73
69
  },
74
70
  };
75
71
 
76
- async function readCompanionOpenCodeSettings(
77
- companionPath: string,
78
- ): Promise<CompanionOpenCodeSettings> {
79
- try {
80
- const parsed = JSON.parse(
81
- await fs.readFile(getCompanionOpenCodeConfigPath(companionPath), "utf8"),
82
- ) as unknown;
83
- if (parsed && typeof parsed === "object") {
84
- return parsed as CompanionOpenCodeSettings;
85
- }
86
- } catch {
87
- /* absent or malformed */
88
- }
89
- return {};
90
- }
91
-
92
- async function removeOpenCodeMcpServer(companionPath: string): Promise<void> {
93
- const configPath = getCompanionOpenCodeConfigPath(companionPath);
94
- const settings = await readCompanionOpenCodeSettings(companionPath);
95
- if (!settings.mcp || typeof settings.mcp !== "object") {
96
- return;
97
- }
98
-
99
- const mcp = { ...settings.mcp };
100
- delete mcp.tokensave;
101
- if (Object.keys(mcp).length > 0) {
102
- settings.mcp = mcp;
103
- } else {
104
- delete settings.mcp;
105
- }
106
-
107
- await fs.mkdir(path.dirname(configPath), { recursive: true });
108
- await fs.writeFile(configPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
109
- }
110
-
111
72
  async function cleanupRepoLocalTokensaveArtifacts(repoPath: string): Promise<void> {
112
73
  const claudeMdPath = path.join(repoPath, "CLAUDE.md");
113
74
  try {
@@ -314,22 +275,55 @@ export function createTokensavePlugin(): CapabilityPlugin {
314
275
  },
315
276
  ];
316
277
  },
278
+ // MCP access, session hooks, and permission pre-seeds are declared below
279
+ // and reconciled by each runtime's Runtime Surface. The bare `tokensave`
280
+ // command name in the MCP entry is resolved against PATH at spawn time by
281
+ // each provider, so it never pins a since-moved/upgraded binary.
282
+ getRuntimeContributions(): RuntimeContributionsByRuntime {
283
+ const mcpServers = [{ name: "tokensave", command: "tokensave", args: ["serve"] }];
284
+ // Hook commands pin the resolved binary path (hooks run without the
285
+ // launch environment's PATH guarantees).
286
+ const tokensaveCommandPath =
287
+ resolveCommandOnPath("tokensave", process.env.PATH ?? "") ?? "tokensave";
288
+ const hookGroups = [
289
+ {
290
+ event: "PreToolUse",
291
+ marker: "tokensave",
292
+ group: {
293
+ matcher: "Agent|Grep|Bash",
294
+ hooks: [
295
+ { type: "command", command: tokensaveCommandPath, args: ["hook-pre-tool-use"] },
296
+ ],
297
+ },
298
+ },
299
+ {
300
+ event: "UserPromptSubmit",
301
+ marker: "tokensave",
302
+ group: {
303
+ hooks: [
304
+ { type: "command", command: tokensaveCommandPath, args: ["hook-prompt-submit"] },
305
+ ],
306
+ },
307
+ },
308
+ {
309
+ event: "Stop",
310
+ marker: "tokensave",
311
+ group: {
312
+ hooks: [{ type: "command", command: tokensaveCommandPath, args: ["hook-stop"] }],
313
+ },
314
+ },
315
+ ];
316
+ return {
317
+ claude: { mcpServers, hookGroups, permissionEntries: ["mcp__tokensave__*"] },
318
+ opencode: { mcpServers },
319
+ };
320
+ },
317
321
  async apply(ctx) {
318
322
  // Presence only: make sure the tokensave binary is installed so the graph can be
319
323
  // built later. The graph build itself (init/sync) lives in `mate cap index`, never
320
324
  // in setup or launch-time sync — keeping launch fast and indexing an explicit step.
321
325
  if (ctx.activeProviders.filter((p) => TOKENSAVE_SUPPORTED_AGENTS.has(p)).length === 0) return;
322
326
 
323
- // MCP access is provider-mediated: every active hosting provider gets the
324
- // server in its native config, and teardown bookkeeping removes it again.
325
- const tokensaveCommandPath =
326
- resolveCommandOnPath("tokensave", process.env.PATH ?? "") ?? "tokensave";
327
- await ctx.mcp?.register({
328
- name: "tokensave",
329
- command: tokensaveCommandPath,
330
- args: ["serve"],
331
- });
332
-
333
327
  const targetPath = ctx.repoPath ?? ctx.companionPath;
334
328
  if (!(await ensureTokensaveInstalled(targetPath))) {
335
329
  return;
@@ -342,20 +336,5 @@ export function createTokensavePlugin(): CapabilityPlugin {
342
336
  if (!ctx.repoPath) return;
343
337
  await teardownDriver(ctx.repoPath, ctx.activeProviders);
344
338
  },
345
- forProvider: {
346
- claude: {
347
- async apply(_ctx) {},
348
- async teardown(_ctx) {},
349
- },
350
- opencode: {
351
- // Registration goes through ctx.mcp now. Teardown stays as a legacy
352
- // prune for entries written by releases that predate the bookkeeping
353
- // manifest.
354
- async apply(_ctx) {},
355
- async teardown(ctx) {
356
- await removeOpenCodeMcpServer(ctx.companionPath);
357
- },
358
- },
359
- },
360
339
  };
361
340
  }
@@ -99,6 +99,35 @@ export async function upsertManagedBlock(
99
99
  await fs.writeFile(filePath, existing + (existing ? separator : "") + block, "utf8");
100
100
  }
101
101
 
102
+ /**
103
+ * Remove every managed block a plugin owns in `filePath`, except blocks whose
104
+ * key is in `keepKeys`. Used by contribution reconciliation: current sections
105
+ * are upserted (content-hashed keys), then stale keys are swept here.
106
+ */
107
+ export async function removeManagedBlocksForPlugin(
108
+ filePath: string,
109
+ frameworkName: string,
110
+ pluginId: string,
111
+ keepKeys: Set<string> = new Set(),
112
+ ): Promise<void> {
113
+ let existing: string;
114
+ try {
115
+ existing = await fs.readFile(filePath, "utf8");
116
+ } catch {
117
+ return;
118
+ }
119
+ const escape = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
120
+ const keyPattern = new RegExp(
121
+ `<!-- ${escape(frameworkName)}:managed:(${escape(pluginId)}:[0-9a-f]+) start -->`,
122
+ "g",
123
+ );
124
+ const keys = [...existing.matchAll(keyPattern)].map((match) => match[1]);
125
+ for (const key of keys) {
126
+ if (keepKeys.has(key)) continue;
127
+ await removeManagedBlock(filePath, frameworkName, key);
128
+ }
129
+ }
130
+
102
131
  /** Remove a marker-delimited block if present. No-op when file or block is absent. */
103
132
  export async function removeManagedBlock(
104
133
  filePath: string,
@@ -1,4 +1,5 @@
1
1
  import { getActiveDistribution } from "../../../distribution";
2
+ import { FRAMEWORK_NAME } from "../../../framework";
2
3
  import { ensureCapabilityEnabled } from "../../../cli/plugin-commands";
3
4
  import type { CapabilityPlugin, Plugin } from "../plugin";
4
5
 
@@ -43,7 +44,7 @@ export function createPluginHost(): PluginHost {
43
44
  return {
44
45
  apiVersion: PLUGIN_API_VERSION,
45
46
  distribution: {
46
- name: distribution.config.name,
47
+ name: FRAMEWORK_NAME,
47
48
  version: distribution.config.version,
48
49
  },
49
50
  ensureCapabilityEnabled: (name) => ensureCapabilityEnabled(name),
@@ -4,14 +4,13 @@ import path from "node:path";
4
4
  import { parse } from "yaml";
5
5
 
6
6
  import { getActiveDistribution } from "../../../distribution";
7
- import { FRAMEWORK_NAME, frameworkCommandName } from "../../../framework";
7
+ import { FRAMEWORK_NAME } from "../../../framework";
8
8
  import { CompanionResolver } from "../../../lib/orchestrator/companion-resolver";
9
9
  import { GlobalConfigStore } from "../../../lib/orchestrator/global-config-store";
10
10
  import { PLUGIN_DECLARATION_POLICIES } from "../../../lib/orchestrator/config-store";
11
11
  import type { PluginDeclaration } from "../../../lib/orchestrator/types";
12
12
  import type { PluginRegistry } from "../registry";
13
13
  import { loadDynamicPlugin, type DynamicPluginLoadDeps } from "./loader";
14
- import { PluginPinStore } from "./pin-store";
15
14
 
16
15
  // Packages already registered in this process; re-hydration (e.g. right after
17
16
  // an install inside the same run) only picks up plugins that failed before.
@@ -29,14 +28,6 @@ export interface HydrateDynamicPluginsDeps extends DynamicPluginLoadDeps {
29
28
  warn?: (message: string) => void;
30
29
  }
31
30
 
32
- function commandName(): string {
33
- try {
34
- return frameworkCommandName();
35
- } catch {
36
- return FRAMEWORK_NAME;
37
- }
38
- }
39
-
40
31
  function isRecord(value: unknown): value is Record<string, unknown> {
41
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
42
33
  }
@@ -107,7 +98,7 @@ function validateDeclaration(entry: unknown): { declaration?: PluginDeclaration;
107
98
  */
108
99
  export async function hydrateDynamicPlugins(deps: HydrateDynamicPluginsDeps = {}): Promise<void> {
109
100
  const warn =
110
- deps.warn ?? ((message: string) => process.stderr.write(`${commandName()}: ${message}\n`));
101
+ deps.warn ?? ((message: string) => process.stderr.write(`${FRAMEWORK_NAME}: ${message}\n`));
111
102
  try {
112
103
  const env = deps.env ?? process.env;
113
104
  const companionPath =
@@ -117,7 +108,6 @@ export async function hydrateDynamicPlugins(deps: HydrateDynamicPluginsDeps = {}
117
108
  const entries = await readDeclarations(companionPath);
118
109
  if (entries.length === 0) return;
119
110
 
120
- const pins = (await new PluginPinStore(companionPath).load()).plugins;
121
111
  const registry = deps.registry ?? getActiveDistribution().registry;
122
112
 
123
113
  for (const entry of entries) {
@@ -129,7 +119,7 @@ export async function hydrateDynamicPlugins(deps: HydrateDynamicPluginsDeps = {}
129
119
  if (hydratedPackages.has(declaration.package)) continue;
130
120
 
131
121
  // oxlint-disable-next-line no-await-in-loop -- declared order is part of the contract
132
- const result = await loadDynamicPlugin(companionPath, declaration, pins, deps);
122
+ const result = await loadDynamicPlugin(companionPath, declaration, deps);
133
123
  if (!result.ok) {
134
124
  warn(result.warning);
135
125
  continue;