@serviceme/devtools-cli 1.0.0 → 2.0.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.
Files changed (3) hide show
  1. package/dist/bridgeServer.js +1237 -10
  2. package/dist/cli.js +1638 -161
  3. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
9
16
  var __copyProps = (to, from, except, desc) => {
10
17
  if (from && typeof from === "object" || typeof from === "function") {
11
18
  for (let key of __getOwnPropNames(from))
@@ -23,6 +30,197 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
30
  mod
24
31
  ));
25
32
 
33
+ // src/bridge/CopilotContentBridgeHandler.ts
34
+ var CopilotContentBridgeHandler_exports = {};
35
+ __export(CopilotContentBridgeHandler_exports, {
36
+ CopilotContentBridgeHandler: () => CopilotContentBridgeHandler
37
+ });
38
+ async function readOwnershipConfig(configPath) {
39
+ try {
40
+ const raw = await fsp.readFile(configPath, "utf8");
41
+ const parsed = JSON.parse(raw);
42
+ const ownershipNode = parsed._serviceme;
43
+ const ownership = ownershipNode && typeof ownershipNode === "object" ? normalizeOwnership(ownershipNode) ?? {} : {};
44
+ return { exists: true, configPath, ownership };
45
+ } catch (error) {
46
+ if (error.code === "ENOENT") {
47
+ return { exists: false, configPath, ownership: {} };
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+ function normalizeOwnership(node) {
53
+ if (node === null || typeof node !== "object" || Array.isArray(node)) {
54
+ return void 0;
55
+ }
56
+ const result = {};
57
+ for (const [identity, names] of Object.entries(node)) {
58
+ if (Array.isArray(names) && names.every((name) => typeof name === "string")) {
59
+ result[identity] = names;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ var fsp, path2, import_devtools_core2, CopilotContentBridgeHandler;
65
+ var init_CopilotContentBridgeHandler = __esm({
66
+ "src/bridge/CopilotContentBridgeHandler.ts"() {
67
+ "use strict";
68
+ fsp = __toESM(require("fs/promises"));
69
+ path2 = __toESM(require("path"));
70
+ import_devtools_core2 = require("@serviceme/devtools-core");
71
+ CopilotContentBridgeHandler = class {
72
+ constructor(opts) {
73
+ this.repoManager = new import_devtools_core2.RepoManager({
74
+ store: opts.store,
75
+ gitClient: opts.gitClient,
76
+ skipClone: opts.skipClone ?? false
77
+ });
78
+ }
79
+ /** Ensure every declared repository is checked out at its pinned commit. */
80
+ async ensureRepositories(manifest) {
81
+ const sources = /* @__PURE__ */ new Map();
82
+ for (const source of (0, import_devtools_core2.getWorkspaceManifestSources)(
83
+ manifest ?? { version: 2, sources: [], plugins: [] }
84
+ )) {
85
+ if (source.type === "catalog") {
86
+ const localPath = path2.resolve((0, import_devtools_core2.getReposDir)(), source.id);
87
+ const stat4 = await fsp.stat(localPath).catch(() => null);
88
+ sources.set(
89
+ source.id,
90
+ stat4?.isDirectory() ? { ready: true } : {
91
+ ready: false,
92
+ error: "Source not materialized under ~/.serviceme/repos"
93
+ }
94
+ );
95
+ continue;
96
+ }
97
+ try {
98
+ await this.repoManager.ensureAtCommit({
99
+ repository: {
100
+ id: source.id,
101
+ url: source.url,
102
+ useProxy: true
103
+ },
104
+ commit: source.commit
105
+ });
106
+ sources.set(source.id, { ready: true });
107
+ } catch (error) {
108
+ sources.set(source.id, {
109
+ ready: false,
110
+ error: error instanceof Error ? error.message : String(error)
111
+ });
112
+ }
113
+ }
114
+ return sources;
115
+ }
116
+ async reconcile(workspaceDir) {
117
+ const reconciler = await this.makeReconciler(workspaceDir);
118
+ return reconciler.reconcile();
119
+ }
120
+ /**
121
+ * Reconciler with machine-local disable marks applied: identities
122
+ * disabled for THIS workspace behave as if undeclared (links are
123
+ * removed and never resurrected) while the manifest stays intact.
124
+ */
125
+ async makeReconciler(workspaceDir) {
126
+ const disabledStore = new import_devtools_core2.DisabledContentStore();
127
+ const marks = await disabledStore.list();
128
+ const matches = (identity) => {
129
+ const segments = identity.split("::");
130
+ if (segments.length < 3) return false;
131
+ const [repoId, pluginId, kindName] = segments;
132
+ return marks.some(
133
+ (mark) => mark.scope === "workspace" && mark.workspaceDir === workspaceDir && mark.repoId === repoId && mark.name === pluginId && kindName === mark.kind
134
+ );
135
+ };
136
+ return new import_devtools_core2.WorkspaceCopilotContentReconciler({
137
+ workspaceDir,
138
+ ensureRepository: (manifest) => this.ensureRepositories(manifest),
139
+ isDisabled: async (identity) => matches(identity)
140
+ });
141
+ }
142
+ /** `copilotContent.restore` — reconcile declared content now. */
143
+ async restore(params) {
144
+ return this.reconcile(params.workspaceDir);
145
+ }
146
+ /** `copilotContent.status` — declaration + current status without mutations. */
147
+ async status(params) {
148
+ const manifest = await (0, import_devtools_core2.loadWorkspaceCopilotManifest)(params.workspaceDir);
149
+ if (!manifest) {
150
+ return { changed: false, entries: [] };
151
+ }
152
+ return this.reconcile(params.workspaceDir);
153
+ }
154
+ /** `copilotContent.approve` — record local approval, then re-reconcile. */
155
+ async approve(params) {
156
+ const reconciler = await this.makeReconciler(params.workspaceDir);
157
+ return reconciler.approve({ identities: params.identities });
158
+ }
159
+ /** `copilotContent.migrateLegacy` — report legacy ~/.agents links for migration. */
160
+ async migrateLegacy(params) {
161
+ const manifest = await (0, import_devtools_core2.loadWorkspaceCopilotManifest)(params.workspaceDir);
162
+ if (!manifest) {
163
+ return { entries: [] };
164
+ }
165
+ const plan = await (0, import_devtools_core2.resolveWorkspaceContentPlan)({
166
+ manifest,
167
+ reposDir: (0, import_devtools_core2.getReposDir)()
168
+ });
169
+ const materializer = new import_devtools_core2.CopilotLinkMaterializer();
170
+ const entries = [];
171
+ for (const entry of plan.entries) {
172
+ const legacy = await materializer.inspectLegacyUserLink({
173
+ homeDir: (0, import_devtools_core2.getServicemeHome)(),
174
+ entry
175
+ });
176
+ entries.push({
177
+ identity: legacy.identity,
178
+ status: legacy.status,
179
+ message: legacy.message
180
+ });
181
+ }
182
+ return { entries };
183
+ }
184
+ /**
185
+ * `copilotContent.integrationStatus` — read-only diagnostics for the
186
+ * machine-local MCP / hook integrations of a declared workspace.
187
+ *
188
+ * The state store provides the declared identities and approval flags;
189
+ * the two generated config files and their `_serviceme` ownership maps
190
+ * prove which names are actually present on this machine. This method
191
+ * never mutates state or configuration.
192
+ */
193
+ async integrationStatus(params) {
194
+ const state = await new import_devtools_core2.WorkspaceContentStateStore({
195
+ workspaceDir: params.workspaceDir
196
+ }).read();
197
+ const mcp = await readOwnershipConfig(
198
+ path2.join(params.workspaceDir, ".vscode", "mcp.serviceme.json")
199
+ );
200
+ const hooks = await readOwnershipConfig(
201
+ path2.join(params.workspaceDir, ".vscode", "hooks.serviceme.json")
202
+ );
203
+ const integrations = [];
204
+ for (const entry of state.entries) {
205
+ if (entry.kind !== "mcp" && entry.kind !== "hook") continue;
206
+ const config = entry.kind === "mcp" ? mcp : hooks;
207
+ const owned = config.ownership[entry.identity] ?? [];
208
+ const active = config.exists && owned.length > 0;
209
+ const status = active ? "active" : entry.approved ? "missing_local_configuration" : "pending_approval";
210
+ integrations.push({
211
+ identity: entry.identity,
212
+ kind: entry.kind,
213
+ configPath: config.configPath,
214
+ names: owned,
215
+ status
216
+ });
217
+ }
218
+ return { integrations };
219
+ }
220
+ };
221
+ }
222
+ });
223
+
26
224
  // ../../packages/serviceme-protocol/src/auth.ts
27
225
  var AUTH_PROVIDERS = ["github", "microsoft"];
28
226
  function isAuthProvider(value) {
@@ -31,7 +229,7 @@ function isAuthProvider(value) {
31
229
 
32
230
  // ../../packages/serviceme-protocol/src/toolbox.ts
33
231
  var TOOLBOX_SCOPES = ["user", "workspace"];
34
- function isRecord(value) {
232
+ function isRecord2(value) {
35
233
  return typeof value === "object" && value !== null;
36
234
  }
37
235
  function isStringOrUndefined(value) {
@@ -44,7 +242,7 @@ function isToolboxScope(value) {
44
242
  return value === "user" || value === "workspace";
45
243
  }
46
244
  function isExternalTool(value) {
47
- if (!isRecord(value)) return false;
245
+ if (!isRecord2(value)) return false;
48
246
  if (typeof value.id !== "string") return false;
49
247
  if (value.id.length === 0) return false;
50
248
  if (typeof value.name !== "string") return false;
@@ -62,7 +260,7 @@ function isExternalTool(value) {
62
260
  return true;
63
261
  }
64
262
  function isExternalToolPatch(value) {
65
- if (!isRecord(value)) return false;
263
+ if (!isRecord2(value)) return false;
66
264
  if (value.name !== void 0) {
67
265
  if (typeof value.name !== "string" || value.name.length === 0) return false;
68
266
  }
@@ -84,7 +282,7 @@ function isExternalToolPatch(value) {
84
282
 
85
283
  // ../../packages/serviceme-protocol/src/bridge.ts
86
284
  var SERVICEME_PROTOCOL_VERSION = 2;
87
- function isRecord2(value) {
285
+ function isRecord3(value) {
88
286
  return typeof value === "object" && value !== null;
89
287
  }
90
288
  function isValidProtocolVersion(value) {
@@ -97,11 +295,23 @@ var BRIDGE_METHODS = [
97
295
  "task.execute",
98
296
  "task.cancel",
99
297
  "task.list-running",
298
+ "copilotContent.list",
299
+ "copilotContent.get",
300
+ "copilotContent.install",
301
+ "copilotContent.convertToSymlink",
302
+ "copilotContent.uninstall",
303
+ "copilotContent.setEntryEnabled",
304
+ "copilotContent.listLinked",
305
+ "copilotContent.draft.create",
306
+ "copilotContent.draft.commit",
307
+ "copilotContent.draft.list",
308
+ "copilotContent.draft.delete",
100
309
  "skillRepo.list",
101
310
  "skillRepo.get",
102
311
  "skillRepo.install",
103
312
  "skillRepo.convertToSymlink",
104
313
  "skillRepo.uninstall",
314
+ "skillRepo.setEntryEnabled",
105
315
  "skillRepo.listLinked",
106
316
  "skillRepo.draft.create",
107
317
  "skillRepo.draft.commit",
@@ -115,6 +325,25 @@ var BRIDGE_METHODS = [
115
325
  "repo.update",
116
326
  "repo.sync",
117
327
  "repo.syncAll",
328
+ "repo.resetParseCache",
329
+ "copilotContent.status",
330
+ "copilotContent.restore",
331
+ "copilotContent.approve",
332
+ "copilotContent.migrateLegacy",
333
+ "copilotContent.integrationStatus",
334
+ "copilotContent.customizations.list",
335
+ "copilotContent.package.install",
336
+ "copilotContent.package.update",
337
+ "copilotContent.package.uninstall",
338
+ "copilotContent.package.move",
339
+ "copilotPlugin.list",
340
+ "copilotPlugin.register",
341
+ "copilotPlugin.unregister",
342
+ "copilotContent.legacy.preview",
343
+ "copilotContent.legacy.migrate",
344
+ "copilotContent.sources.list",
345
+ "copilotContent.sources.remove",
346
+ "copilotContent.package.previewUpdate",
118
347
  "auth.status",
119
348
  "auth.login",
120
349
  "auth.logout",
@@ -132,7 +361,7 @@ function isBridgeMethod(value) {
132
361
  return typeof value === "string" && BRIDGE_METHODS.includes(value);
133
362
  }
134
363
  function isBridgeRequest(value) {
135
- if (!isRecord2(value)) {
364
+ if (!isRecord3(value)) {
136
365
  return false;
137
366
  }
138
367
  return isValidProtocolVersion(value.protocolVersion) && value.kind === "request" && typeof value.id === "string" && isBridgeMethod(value.method) && "params" in value;
@@ -219,14 +448,14 @@ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
219
448
  "executor_timeout",
220
449
  "internal_error"
221
450
  ]);
222
- function isRecord3(value) {
451
+ function isRecord4(value) {
223
452
  return typeof value === "object" && value !== null;
224
453
  }
225
454
  function isServicemeErrorCode(value) {
226
455
  return typeof value === "string" && SERVICEME_ERROR_CODES.includes(value);
227
456
  }
228
457
  function isServicemeErrorDetails(value) {
229
- if (!isRecord3(value)) {
458
+ if (!isRecord4(value)) {
230
459
  return false;
231
460
  }
232
461
  return isServicemeErrorCode(value.code) && typeof value.message === "string" && typeof value.retryable === "boolean";
@@ -266,7 +495,7 @@ function normalizeServicemeError(error, fallbackCode = "internal_error") {
266
495
  if (isServicemeErrorDetails(error)) {
267
496
  return error;
268
497
  }
269
- if (isRecord3(error)) {
498
+ if (isRecord4(error)) {
270
499
  const code = isServicemeErrorCode(error.code) ? error.code : fallbackCode;
271
500
  const message = typeof error.message === "string" ? error.message : "Unexpected serviceme error.";
272
501
  const retryable = typeof error.retryable === "boolean" ? error.retryable : isRetryableErrorCode(code);
@@ -772,14 +1001,867 @@ async function handleSwitch(parsed) {
772
1001
  }
773
1002
 
774
1003
  // src/commands/bridge.ts
775
- var import_devtools_core4 = require("@serviceme/devtools-core");
1004
+ var import_devtools_core7 = require("@serviceme/devtools-core");
776
1005
 
777
1006
  // src/bridge/BridgeServer.ts
778
1007
  var readline = __toESM(require("readline"));
779
1008
 
780
1009
  // src/version.ts
781
1010
  var SERVICEME_CLI_NAME = "serviceme";
782
- var SERVICEME_CLI_VERSION = "1.0.0";
1011
+ var SERVICEME_CLI_VERSION = "2.0.0";
1012
+
1013
+ // src/bridge/BridgeServer.ts
1014
+ init_CopilotContentBridgeHandler();
1015
+
1016
+ // src/bridge/CopilotCustomizationsBridgeHandler.ts
1017
+ var fsp2 = __toESM(require("fs/promises"));
1018
+ var path3 = __toESM(require("path"));
1019
+ var import_devtools_core3 = require("@serviceme/devtools-core");
1020
+ var import_skill_linker = require("@serviceme/devtools-core/skill-linker");
1021
+ function createCopilotCustomizationsProductionDeps(options) {
1022
+ return {
1023
+ snapshot: async (params) => {
1024
+ if (params.scope === "personal") {
1025
+ return createFullPersonalSnapshot(options, await options.readers.listPersonalLinks());
1026
+ }
1027
+ return createWorkspaceSnapshot(
1028
+ options.repoDisplayName,
1029
+ options.readers,
1030
+ params.workspaceDir ?? process.cwd()
1031
+ );
1032
+ }
1033
+ };
1034
+ }
1035
+ var CopilotCustomizationsBridgeHandler = class {
1036
+ constructor(deps) {
1037
+ if ("snapshot" in deps && typeof deps.snapshot === "function") {
1038
+ this.snapshot = deps.snapshot;
1039
+ this.listAvailablePackages = deps.listAvailablePackages;
1040
+ this.mutations = createUnimplementedMutations(
1041
+ "Injected snapshot deps do not support package mutations"
1042
+ );
1043
+ } else if ("store" in deps && deps.store !== void 0) {
1044
+ this.snapshot = createCopilotCustomizationsProductionDeps({
1045
+ repoDisplayName: (repoId) => deps.store.get(repoId)?.name,
1046
+ readers: createDefaultProductionReaders(),
1047
+ personal: createDefaultPersonalSnapshotInput(deps.store)
1048
+ }).snapshot;
1049
+ this.mutations = createProductionMutations(deps.store);
1050
+ this.listAvailablePackages = createAvailablePackagesEnumerator(deps.store);
1051
+ } else {
1052
+ this.snapshot = async () => emptySnapshot();
1053
+ this.mutations = createUnimplementedMutations(
1054
+ "Package mutations require production dependencies"
1055
+ );
1056
+ }
1057
+ }
1058
+ async list(params) {
1059
+ const snapshot = await this.snapshot(params);
1060
+ const shared = snapshot.workspaceManifest !== void 0 ? { workspaceManifest: snapshot.workspaceManifest } : {};
1061
+ const availablePackages = await this.listAvailablePackages?.(params, shared).catch(
1062
+ () => void 0
1063
+ );
1064
+ return {
1065
+ view: (0, import_devtools_core3.buildCopilotCustomizationView)({
1066
+ scope: params.scope,
1067
+ sources: snapshot.sources.map(toPublicSource),
1068
+ packages: snapshot.packages.map(toPublicPackage),
1069
+ installations: snapshot.installations.map(toPublicInstallation),
1070
+ statesByArtifactId: toPublicStates(snapshot.statesByArtifactId),
1071
+ legacyCount: snapshot.legacyCount,
1072
+ generatedAt: snapshot.generatedAt
1073
+ }),
1074
+ ...availablePackages ? { availablePackages } : {}
1075
+ };
1076
+ }
1077
+ async install(params) {
1078
+ return this.mutations.install(params);
1079
+ }
1080
+ async update(params) {
1081
+ return this.mutations.update(params);
1082
+ }
1083
+ async uninstall(params) {
1084
+ const result = await this.mutations.uninstall(params);
1085
+ try {
1086
+ const registrar = new import_devtools_core3.CopilotPluginRegistrar({
1087
+ copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
1088
+ });
1089
+ await registrar.unregister({
1090
+ registrationId: params.packageId.replace("::", ":"),
1091
+ scope: params.scope
1092
+ });
1093
+ const otherScope = params.scope === "personal" ? "workspace" : "personal";
1094
+ await registrar.unregister({
1095
+ registrationId: params.packageId.replace("::", ":"),
1096
+ scope: otherScope
1097
+ });
1098
+ } catch {
1099
+ }
1100
+ return result;
1101
+ }
1102
+ async move(params) {
1103
+ return this.mutations.move(params);
1104
+ }
1105
+ async legacyPreview(params) {
1106
+ return this.mutations.legacyPreview(params);
1107
+ }
1108
+ async legacyMigrate(params) {
1109
+ return this.mutations.legacyMigrate(params);
1110
+ }
1111
+ async sources(params) {
1112
+ return this.mutations.sources(params);
1113
+ }
1114
+ async removeSource(params) {
1115
+ return this.mutations.removeSource(params);
1116
+ }
1117
+ async previewUpdate(params) {
1118
+ return this.mutations.previewUpdate(params);
1119
+ }
1120
+ };
1121
+ function createDefaultPersonalSnapshotInput(store) {
1122
+ return {
1123
+ // Capabilities are intentionally omitted: createFullPersonalSnapshot
1124
+ // falls back to the real default host-capability provider, which
1125
+ // claims the verified ~/.copilot agent/skill targets. Pinning an
1126
+ // empty personalTargets map here regressed every link artifact to
1127
+ // unsupported. The explicit package resolver is the part Task 8
1128
+ // flagged as silently missing.
1129
+ resolvePersonalPackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
1130
+ };
1131
+ }
1132
+ async function resolvePersonalPackageFromRepos(packageId, store) {
1133
+ const [sourceId, pluginId] = packageId.split("::");
1134
+ if (!sourceId || !pluginId) return [];
1135
+ const repoRoot = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
1136
+ const stat4 = await fsp2.stat(repoRoot).catch(() => null);
1137
+ if (!stat4?.isDirectory()) return [];
1138
+ const repo = store.get(sourceId);
1139
+ const manifest = {
1140
+ version: 1,
1141
+ repositories: [
1142
+ {
1143
+ id: sourceId,
1144
+ url: repo?.url ?? `https://serviceme.local/catalog/${encodeURIComponent(sourceId)}`,
1145
+ commit: repo?.lastSyncCommitSha ?? "".padEnd(40, "0")
1146
+ }
1147
+ ],
1148
+ plugins: [
1149
+ {
1150
+ repository: sourceId,
1151
+ id: pluginId,
1152
+ artifacts: {
1153
+ agent: true,
1154
+ skill: true,
1155
+ instruction: true,
1156
+ prompt: true,
1157
+ hook: true,
1158
+ mcp: true
1159
+ }
1160
+ }
1161
+ ]
1162
+ };
1163
+ const resolved = await (0, import_devtools_core3.resolveWorkspaceContentPlan)({
1164
+ manifest,
1165
+ reposDir: (0, import_devtools_core3.getReposDir)()
1166
+ }).catch(() => ({ entries: [] }));
1167
+ return resolved.entries;
1168
+ }
1169
+ function createAvailablePackagesEnumerator(store) {
1170
+ const catalog = (0, import_devtools_core3.createPluginCatalogService)();
1171
+ const personalStore = new import_devtools_core3.PersonalInstallationStore({});
1172
+ const pluginRegistrar = new import_devtools_core3.CopilotPluginRegistrar({
1173
+ copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
1174
+ });
1175
+ return async (params, shared) => {
1176
+ const repos = store.list().filter((repo) => repo.enabled).map((repo) => ({ id: repo.id }));
1177
+ if (repos.length === 0) return [];
1178
+ const catalogPackages = await catalog.list({
1179
+ reposDir: (0, import_devtools_core3.getReposDir)(),
1180
+ repos
1181
+ });
1182
+ const [workspaceManifest, personalIntent, registrations] = await Promise.all([
1183
+ // Shared read from the same list() call when provided; only a
1184
+ // standalone enumerator invocation (personal scope, tests,
1185
+ // direct calls) hits the disk here.
1186
+ shared?.workspaceManifest !== void 0 ? Promise.resolve(shared.workspaceManifest) : params.workspaceDir ? (0, import_devtools_core3.loadWorkspaceCopilotManifest)(params.workspaceDir).catch(() => void 0) : Promise.resolve(void 0),
1187
+ personalStore.read().catch(() => void 0),
1188
+ // Whole-package registry — prune-on-list keeps it clean.
1189
+ pluginRegistrar.list({}).catch(() => [])
1190
+ ]);
1191
+ const manifest = workspaceManifest ?? {
1192
+ version: 1,
1193
+ repositories: [],
1194
+ plugins: []
1195
+ };
1196
+ const workspaceInstalled = new Set(
1197
+ manifest.plugins.map(
1198
+ (plugin) => `${(0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin)}::${plugin.id}`
1199
+ )
1200
+ );
1201
+ const personalInstalled = new Set(
1202
+ (personalIntent?.installations ?? []).filter((installation) => installation.scope === "personal").map((installation) => installation.packageId)
1203
+ );
1204
+ const registeredScopes = /* @__PURE__ */ new Map();
1205
+ for (const reg of registrations) {
1206
+ const packageId = `${reg.repoId}::${reg.pluginId}`;
1207
+ const scopes = registeredScopes.get(packageId) ?? /* @__PURE__ */ new Set();
1208
+ for (const scope of reg.scopes) scopes.add(scope);
1209
+ registeredScopes.set(packageId, scopes);
1210
+ }
1211
+ return catalogPackages.map((pkg) => {
1212
+ const installed = registeredScopes.get(pkg.packageId) !== void 0 || workspaceInstalled.has(pkg.packageId) || personalInstalled.has(pkg.packageId);
1213
+ return {
1214
+ ...pkg,
1215
+ installedScopes: installed ? ["personal"] : []
1216
+ };
1217
+ });
1218
+ };
1219
+ }
1220
+ function createProductionMutations(store) {
1221
+ let servicePromise;
1222
+ let sourceCatalogPromise;
1223
+ const service = () => servicePromise ??= createPackageInstallationService(store);
1224
+ const sourceCatalog = () => sourceCatalogPromise ??= createSourceCatalogService(store);
1225
+ return {
1226
+ install: async (params) => toResult(await (await service()).install(params)),
1227
+ update: async (params) => toResult(await (await service()).update(params)),
1228
+ uninstall: async (params) => toResult(await (await service()).uninstall(params)),
1229
+ move: async (params) => toResult(await (await service()).move(params)),
1230
+ legacyPreview: async () => {
1231
+ const preview = await (await service()).previewLegacy();
1232
+ return {
1233
+ sourceLabel: preview.sourceLabel,
1234
+ count: preview.entries.length,
1235
+ artifacts: preview.entries.map((entry) => ({
1236
+ id: entry.artifactId,
1237
+ packageId: "legacy",
1238
+ kind: entry.kind,
1239
+ displayName: entry.name,
1240
+ installStrategy: "link",
1241
+ risk: "none"
1242
+ }))
1243
+ };
1244
+ },
1245
+ legacyMigrate: async (params) => toResult(await (await service()).migrateLegacy(params)),
1246
+ sources: async (params) => {
1247
+ const records = await (await sourceCatalog()).listSources({
1248
+ scope: params.scope,
1249
+ ...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
1250
+ });
1251
+ return {
1252
+ sources: records.map((record) => ({
1253
+ id: record.id,
1254
+ type: record.type,
1255
+ displayName: record.displayName,
1256
+ updateCapability: record.updateCapability,
1257
+ available: record.available,
1258
+ declaredIn: [...record.declaredIn]
1259
+ }))
1260
+ };
1261
+ },
1262
+ removeSource: async (params) => {
1263
+ await (await sourceCatalog()).removeSource(params.sourceId, params.workspaceDir);
1264
+ return { removed: true };
1265
+ },
1266
+ previewUpdate: async (params) => ({
1267
+ preview: await (await sourceCatalog()).previewUpdate(params)
1268
+ })
1269
+ };
1270
+ }
1271
+ async function createSourceCatalogService(store) {
1272
+ const personalStore = new import_devtools_core3.PersonalInstallationStore({});
1273
+ const loadManifest = async (workspaceDir) => {
1274
+ return (0, import_devtools_core3.loadWorkspaceCopilotManifest)(workspaceDir).catch(() => void 0);
1275
+ };
1276
+ return new import_devtools_core3.CopilotSourceCatalogService({
1277
+ personalStore,
1278
+ // The source manager is the single repository management surface;
1279
+ // surface every ReposStore entry (built-in defaults + user git
1280
+ // repos) so they can be synced / toggled / edited from there even
1281
+ // when no installation or workspace declaration references them.
1282
+ // Read per call: repos.json can change under the long-lived bridge.
1283
+ listStoreSources: async () => store.list().map((repo) => ({
1284
+ id: repo.id,
1285
+ name: repo.name,
1286
+ enabled: repo.enabled
1287
+ })),
1288
+ resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
1289
+ resolveActualRevision: async (packageId) => {
1290
+ const [sourceId] = packageId.split("::");
1291
+ if (!sourceId) return void 0;
1292
+ const repo = store.get(sourceId);
1293
+ if (repo?.lastSyncCommitSha) return repo.lastSyncCommitSha;
1294
+ const repoDir = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
1295
+ const head = await new import_devtools_core3.GitClient({ serverProxyBase: "" }).revParseHead(repoDir).catch(() => void 0);
1296
+ return head;
1297
+ },
1298
+ listWorkspaceSources: async (workspaceDir) => {
1299
+ const manifest = await loadManifest(workspaceDir);
1300
+ if (!manifest) return [];
1301
+ return (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).map((source) => source.id);
1302
+ },
1303
+ readWorkspaceInstallations: async (workspaceDir) => {
1304
+ const manifest = await loadManifest(workspaceDir);
1305
+ if (!manifest) return [];
1306
+ return manifest.plugins.map((plugin) => {
1307
+ const sourceId = (0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin);
1308
+ const source = (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).find((s) => s.id === sourceId);
1309
+ return {
1310
+ packageId: `${sourceId}::${plugin.id}`,
1311
+ scope: "workspace",
1312
+ selectedArtifactIds: Object.entries(plugin.artifacts).filter(([, enabled]) => enabled !== false).map(([kind]) => `${sourceId}::${plugin.id}::${kind}:`),
1313
+ pinnedVersion: source?.type === "git" ? source.commit : void 0
1314
+ };
1315
+ });
1316
+ },
1317
+ artifactSelected: (marker, artifactId) => artifactId.startsWith(marker),
1318
+ ensureCatalogSource: async (sourceId) => {
1319
+ const mgr = new import_devtools_core3.RepoManager({
1320
+ store,
1321
+ gitClient: new import_devtools_core3.GitClient({ serverProxyBase: "" })
1322
+ });
1323
+ return mgr.ensureCatalogSource({
1324
+ sourceId,
1325
+ provider: {
1326
+ // Placeholder staging: the marketplace catalog client is
1327
+ // not yet wired to per-source payloads, so the first
1328
+ // materialization establishes the directory contract.
1329
+ // Real payload extraction lands with the catalog client.
1330
+ materialize: async (targetDir) => {
1331
+ await fsp2.mkdir(targetDir, { recursive: true });
1332
+ }
1333
+ }
1334
+ });
1335
+ },
1336
+ isCatalogSource: async (sourceId, workspaceDir) => {
1337
+ if (!workspaceDir) return false;
1338
+ const declared = await loadManifest(workspaceDir);
1339
+ const source = declared ? (0, import_devtools_core3.getWorkspaceManifestSources)(declared).find((s) => s.id === sourceId) : void 0;
1340
+ return source?.type === "catalog";
1341
+ },
1342
+ hasCatalogPayload: async (localPath) => {
1343
+ const pluginsDir = path3.join(localPath, "plugins");
1344
+ const entries = await fsp2.readdir(pluginsDir).catch(() => []);
1345
+ if (entries.length === 0) return false;
1346
+ for (const entry of entries) {
1347
+ const stat4 = await fsp2.stat(path3.join(pluginsDir, entry)).catch(() => null);
1348
+ if (stat4?.isDirectory()) {
1349
+ const manifest = await fsp2.stat(path3.join(pluginsDir, entry, "plugin.json")).catch(() => null);
1350
+ if (manifest?.isFile()) return true;
1351
+ }
1352
+ }
1353
+ return false;
1354
+ }
1355
+ });
1356
+ }
1357
+ async function createPackageInstallationService(store) {
1358
+ const personalStore = new import_devtools_core3.PersonalInstallationStore({});
1359
+ const personalReconciler = new import_devtools_core3.PersonalCopilotContentReconciler({
1360
+ capabilities: await (0, import_devtools_core3.createDefaultCopilotHostCapabilities)(),
1361
+ resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store)
1362
+ });
1363
+ return new import_devtools_core3.PackageInstallationService({
1364
+ personalStore,
1365
+ personalReconciler,
1366
+ userHomeDir: (0, import_devtools_core3.getHomeDir)(),
1367
+ resolvePackage: (packageId) => resolvePersonalPackageFromRepos(packageId, store),
1368
+ resolveWorkspaceSource: async (sourceId) => {
1369
+ const repo = store.get(sourceId);
1370
+ if (!repo) return void 0;
1371
+ const repoDir = path3.resolve((0, import_devtools_core3.getReposDir)(), sourceId);
1372
+ const stat4 = await fsp2.stat(repoDir).catch(() => null);
1373
+ if (!stat4?.isDirectory()) return void 0;
1374
+ return {
1375
+ id: sourceId,
1376
+ url: repo.url,
1377
+ commit: repo.lastSyncCommitSha ?? "0".repeat(40)
1378
+ };
1379
+ },
1380
+ createWorkspaceReconciler: (workspaceDir) => new import_devtools_core3.WorkspaceCopilotContentReconciler({ workspaceDir }),
1381
+ readWorkspaceView: async (workspaceDir) => {
1382
+ const snapshot = await createWorkspaceSnapshot(
1383
+ (repoId) => store.get(repoId)?.name,
1384
+ createDefaultProductionReaders(),
1385
+ workspaceDir
1386
+ );
1387
+ return (0, import_devtools_core3.buildCopilotCustomizationView)({
1388
+ scope: "workspace",
1389
+ sources: snapshot.sources,
1390
+ packages: snapshot.packages,
1391
+ installations: snapshot.installations,
1392
+ statesByArtifactId: snapshot.statesByArtifactId,
1393
+ generatedAt: snapshot.generatedAt
1394
+ });
1395
+ },
1396
+ readWorkspaceInstallations: import_devtools_core3.readDeclaredWorkspaceInstallations
1397
+ });
1398
+ }
1399
+ function toResult(view) {
1400
+ return { view: toPublicView(view) };
1401
+ }
1402
+ function toPublicView(view) {
1403
+ return {
1404
+ scope: view.scope,
1405
+ generatedAt: view.generatedAt,
1406
+ sources: view.sources.map((source) => ({
1407
+ ...toPublicSource(source),
1408
+ packages: source.packages.map((pkg) => toPublicPackageView(pkg))
1409
+ })),
1410
+ packages: view.packages.map((pkg) => ({
1411
+ ...toPublicPackageView(pkg)
1412
+ })),
1413
+ attention: view.attention.map(({ artifact, state }) => ({
1414
+ artifact,
1415
+ state
1416
+ })),
1417
+ summary: view.summary,
1418
+ ...view.legacyMigration ? { legacyMigration: view.legacyMigration } : {}
1419
+ };
1420
+ }
1421
+ function toPublicPackageView(pkg) {
1422
+ return {
1423
+ definition: toPublicPackage(pkg.definition),
1424
+ installation: toPublicInstallation(pkg.installation),
1425
+ artifacts: pkg.artifacts.map(({ artifact, state }) => ({
1426
+ artifact,
1427
+ state
1428
+ })),
1429
+ selectedArtifactCount: pkg.selectedArtifactCount,
1430
+ status: pkg.status
1431
+ };
1432
+ }
1433
+ function createUnimplementedMutations(reason) {
1434
+ const reject = async () => {
1435
+ throw new Error(reason);
1436
+ };
1437
+ return {
1438
+ install: reject,
1439
+ update: reject,
1440
+ uninstall: reject,
1441
+ move: reject,
1442
+ legacyPreview: reject,
1443
+ legacyMigrate: reject,
1444
+ sources: reject,
1445
+ removeSource: reject,
1446
+ previewUpdate: reject
1447
+ };
1448
+ }
1449
+ async function mergeRegistrarPackages(sources, packages, installations, statesByArtifactId) {
1450
+ const registrar = new import_devtools_core3.CopilotPluginRegistrar({
1451
+ copilotDir: path3.join((0, import_devtools_core3.getHomeDir)(), ".copilot")
1452
+ });
1453
+ const registrations = await registrar.list({}).catch(() => []);
1454
+ if (registrations.length === 0) return;
1455
+ const asMap = packages instanceof Map ? packages : new Map(packages.map((p) => [p.id, p]));
1456
+ const knownIds = new Set(asMap.keys());
1457
+ const pushed = packages instanceof Map ? void 0 : packages;
1458
+ for (const reg of registrations) {
1459
+ const packageId = `${reg.repoId}::${reg.pluginId}`;
1460
+ if (knownIds.has(packageId)) continue;
1461
+ const pluginJsonPath = path3.join(reg.pluginDir, "plugin.json");
1462
+ let definition;
1463
+ try {
1464
+ const raw = JSON.parse(await fsp2.readFile(pluginJsonPath, "utf8"));
1465
+ const repoRoot = path3.basename(path3.dirname(reg.pluginDir)) === "plugins" ? path3.dirname(path3.dirname(reg.pluginDir)) : reg.pluginDir;
1466
+ const entries = await (0, import_devtools_core3.resolvePluginEntriesLenient)({
1467
+ repoRoot,
1468
+ repositoryId: reg.repoId,
1469
+ pluginId: reg.pluginId,
1470
+ manifest: {
1471
+ name: typeof raw.name === "string" && raw.name.trim() !== "" ? raw.name : reg.pluginId,
1472
+ ...typeof raw.description === "string" ? { description: raw.description } : {},
1473
+ ...typeof raw.version === "string" ? { version: raw.version } : {},
1474
+ extensions: raw.extensions ?? {}
1475
+ }
1476
+ }).catch(() => []);
1477
+ const artifacts = entries.map(import_devtools_core3.toArtifactSummary);
1478
+ if (artifacts.length > 0) {
1479
+ definition = {
1480
+ id: packageId,
1481
+ sourceId: reg.repoId,
1482
+ displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
1483
+ ...typeof raw.description === "string" ? { description: raw.description } : {},
1484
+ ...typeof raw.version === "string" ? { version: raw.version } : {},
1485
+ artifacts,
1486
+ wholePackage: true
1487
+ };
1488
+ for (const artifact of artifacts) {
1489
+ statesByArtifactId[artifact.id] = {
1490
+ intent: "selected",
1491
+ health: "healthy",
1492
+ gate: "ready"
1493
+ };
1494
+ }
1495
+ } else {
1496
+ const artifact = {
1497
+ id: `${packageId}::package:${reg.pluginId}`,
1498
+ packageId,
1499
+ kind: "skill",
1500
+ displayName: reg.pluginId,
1501
+ installStrategy: "link",
1502
+ risk: "none"
1503
+ };
1504
+ definition = {
1505
+ id: packageId,
1506
+ sourceId: reg.repoId,
1507
+ displayName: typeof raw.name === "string" && raw.name || reg.displayName || reg.pluginId,
1508
+ ...typeof raw.description === "string" ? { description: raw.description } : {},
1509
+ ...typeof raw.version === "string" ? { version: raw.version } : {},
1510
+ artifacts: [artifact],
1511
+ wholePackage: true
1512
+ };
1513
+ statesByArtifactId[artifact.id] = {
1514
+ intent: "selected",
1515
+ health: "healthy",
1516
+ gate: "ready"
1517
+ };
1518
+ }
1519
+ } catch {
1520
+ definition = {
1521
+ id: packageId,
1522
+ sourceId: reg.repoId,
1523
+ displayName: reg.displayName || reg.pluginId,
1524
+ ...reg.version !== void 0 ? { version: reg.version } : {},
1525
+ artifacts: [],
1526
+ wholePackage: true
1527
+ };
1528
+ }
1529
+ if (pushed) {
1530
+ pushed.push(definition);
1531
+ } else {
1532
+ asMap.set(packageId, definition);
1533
+ }
1534
+ if (!sources.some((source) => source.id === reg.repoId)) {
1535
+ sources.push({
1536
+ id: reg.repoId,
1537
+ type: "git",
1538
+ displayName: reg.repoId,
1539
+ updateCapability: "pinned"
1540
+ });
1541
+ }
1542
+ installations.push({
1543
+ packageId,
1544
+ scope: "personal",
1545
+ selectedArtifactIds: definition.artifacts.map((artifact) => artifact.id)
1546
+ });
1547
+ }
1548
+ }
1549
+ async function createWorkspaceSnapshot(repoDisplayName, readers, workspaceDir) {
1550
+ const manifest = await readers.loadWorkspaceManifest(workspaceDir);
1551
+ if (!manifest) return emptySnapshot();
1552
+ const sources = (0, import_devtools_core3.getWorkspaceManifestSources)(manifest).map(
1553
+ (source) => ({
1554
+ id: source.id,
1555
+ type: source.type === "catalog" ? "marketplace" : "git",
1556
+ displayName: source.type === "catalog" ? source.catalogId : repoDisplayName(source.id) ?? source.id,
1557
+ updateCapability: "pinned"
1558
+ })
1559
+ );
1560
+ const state = await readers.readWorkspaceState(workspaceDir);
1561
+ const packages = [];
1562
+ const installations = [];
1563
+ const statesByArtifactId = {};
1564
+ const resolvedPlugins = await Promise.all(
1565
+ manifest.plugins.map(async (plugin) => {
1566
+ const sourceId = (0, import_devtools_core3.getWorkspaceManifestPluginSourceId)(manifest, plugin);
1567
+ const packageId = `${sourceId}::${plugin.id}`;
1568
+ let fallbackHealth;
1569
+ let entries;
1570
+ if (!await readers.isSourceAvailable(sourceId)) {
1571
+ fallbackHealth = "source-unavailable";
1572
+ entries = fallbackEntries(plugin, packageId);
1573
+ } else {
1574
+ try {
1575
+ entries = await readers.resolveWorkspacePlugin({ manifest, plugin });
1576
+ } catch {
1577
+ fallbackHealth = "conflict";
1578
+ entries = fallbackEntries(plugin, packageId);
1579
+ }
1580
+ }
1581
+ return { plugin, sourceId, packageId, entries, fallbackHealth };
1582
+ })
1583
+ );
1584
+ for (const resolved of resolvedPlugins) {
1585
+ const { plugin, sourceId, packageId, entries, fallbackHealth } = resolved;
1586
+ const artifacts = entries.map(import_devtools_core3.toArtifactSummary);
1587
+ packages.push({
1588
+ id: packageId,
1589
+ sourceId,
1590
+ displayName: entries[0]?.packageDisplayName ?? plugin.id,
1591
+ ...entries[0]?.packageDescription ? { description: entries[0].packageDescription } : {},
1592
+ ...entries[0]?.packageVersion ? { version: entries[0].packageVersion } : {},
1593
+ artifacts
1594
+ });
1595
+ installations.push({
1596
+ packageId,
1597
+ scope: "workspace",
1598
+ selectedArtifactIds: artifacts.map((artifact) => artifact.id)
1599
+ });
1600
+ for (const entry of entries) {
1601
+ const previous = state.entries.find((candidate) => candidate.identity === entry.artifactId);
1602
+ statesByArtifactId[entry.artifactId] = fallbackHealth ? { intent: "selected", health: fallbackHealth, gate: "ready" } : toArtifactState(entry, previous);
1603
+ }
1604
+ }
1605
+ await mergeRegistrarPackages(sources, packages, installations, statesByArtifactId);
1606
+ return { sources, packages, installations, statesByArtifactId, workspaceManifest: manifest };
1607
+ }
1608
+ async function createFullPersonalSnapshot(options, legacyLinks) {
1609
+ const capabilities = options.personal?.capabilities ?? await (0, import_devtools_core3.createDefaultCopilotHostCapabilities)();
1610
+ const homeDir = options.personal?.homeDir;
1611
+ const store = new import_devtools_core3.PersonalInstallationStore(homeDir !== void 0 ? { homeDir } : {});
1612
+ const reconciler = new import_devtools_core3.PersonalCopilotContentReconciler({
1613
+ capabilities,
1614
+ ...homeDir ? { homeDir } : {},
1615
+ ...options.personal?.resolvePersonalPackage ? { resolvePackage: options.personal.resolvePersonalPackage } : {}
1616
+ });
1617
+ const intent = await store.read();
1618
+ const inspection = await reconciler.inspect();
1619
+ const sources = [];
1620
+ const sourceIds = /* @__PURE__ */ new Set();
1621
+ const packages = /* @__PURE__ */ new Map();
1622
+ const statesByArtifactId = {
1623
+ ...inspection.statesByArtifactId
1624
+ };
1625
+ let legacyCount = 0;
1626
+ for (const link of legacyLinks) {
1627
+ if (link.legacy) legacyCount += 1;
1628
+ }
1629
+ for (const installation of intent.installations) {
1630
+ if (installation.scope !== "personal") continue;
1631
+ const resolved = options.personal ? await options.personal.resolvePersonalPackage(installation.packageId) : [];
1632
+ const artifacts = resolved.filter((entry) => installation.selectedArtifactIds.includes(entry.artifactId)).map(import_devtools_core3.toArtifactSummary);
1633
+ const [sourceId] = installation.packageId.split("::");
1634
+ if (sourceId && !sourceIds.has(sourceId)) {
1635
+ sourceIds.add(sourceId);
1636
+ sources.push({
1637
+ id: sourceId,
1638
+ type: "git",
1639
+ displayName: options.repoDisplayName(sourceId) ?? sourceId,
1640
+ updateCapability: "pinned"
1641
+ });
1642
+ }
1643
+ if (!packages.has(installation.packageId) && artifacts.length > 0) {
1644
+ packages.set(installation.packageId, {
1645
+ id: installation.packageId,
1646
+ sourceId: sourceId ?? "personal-local",
1647
+ displayName: resolved[0]?.packageDisplayName ?? installation.packageId,
1648
+ ...resolved[0]?.packageDescription ? { description: resolved[0].packageDescription } : {},
1649
+ ...resolved[0]?.packageVersion ? { version: resolved[0].packageVersion } : {},
1650
+ artifacts
1651
+ });
1652
+ }
1653
+ }
1654
+ await mergeRegistrarPackages(sources, packages, intent.installations, statesByArtifactId);
1655
+ return {
1656
+ sources,
1657
+ packages: [...packages.values()],
1658
+ installations: intent.installations.filter((installation) => installation.scope === "personal"),
1659
+ statesByArtifactId,
1660
+ ...legacyCount > 0 ? { legacyCount } : {}
1661
+ };
1662
+ }
1663
+ function fallbackEntries(plugin, packageId) {
1664
+ const kinds = Object.keys(plugin.artifacts);
1665
+ return kinds.filter((kind) => plugin.artifacts[kind] !== false).map((kind) => ({
1666
+ identity: `${packageId}::${kind}:${plugin.id}`,
1667
+ artifactId: `${packageId}::${kind}:${plugin.id}`,
1668
+ packageId,
1669
+ packageDisplayName: plugin.id,
1670
+ repositoryId: packageId.split("::")[0] ?? packageId,
1671
+ pluginId: plugin.id,
1672
+ kind,
1673
+ sourcePath: "",
1674
+ sourceIsFile: false,
1675
+ name: plugin.id,
1676
+ digest: "",
1677
+ requiresApproval: kind === "hook" || kind === "mcp"
1678
+ }));
1679
+ }
1680
+ function toArtifactState(entry, previous) {
1681
+ if (!previous) {
1682
+ return {
1683
+ intent: "selected",
1684
+ health: "missing",
1685
+ gate: entry.requiresApproval ? "approval-required" : "ready"
1686
+ };
1687
+ }
1688
+ return {
1689
+ intent: "selected",
1690
+ health: previous.digest === entry.digest ? "healthy" : "drifted",
1691
+ gate: entry.requiresApproval && !previous.approved ? "approval-required" : "ready"
1692
+ };
1693
+ }
1694
+ function createDefaultProductionReaders() {
1695
+ return {
1696
+ loadWorkspaceManifest: import_devtools_core3.loadWorkspaceCopilotManifest,
1697
+ readWorkspaceState: async (workspaceDir) => new import_devtools_core3.WorkspaceContentStateStore({ workspaceDir }).read(),
1698
+ isSourceAvailable: async (sourceId) => {
1699
+ const stat4 = await fsp2.stat(path3.join((0, import_devtools_core3.getReposDir)(), sourceId)).catch(() => void 0);
1700
+ return stat4?.isDirectory() === true;
1701
+ },
1702
+ resolveWorkspacePlugin: async ({ manifest, plugin }) => {
1703
+ const scopedManifest = manifest.version === 1 ? { ...manifest, plugins: [plugin] } : { ...manifest, plugins: [plugin] };
1704
+ return (await (0, import_devtools_core3.resolveWorkspaceContentPlan)({
1705
+ manifest: scopedManifest,
1706
+ reposDir: (0, import_devtools_core3.getReposDir)()
1707
+ })).entries;
1708
+ },
1709
+ listPersonalLinks: async () => [
1710
+ ...(await (0, import_skill_linker.listLinkedSkills)("", "skill", "user")).map((link) => ({
1711
+ repoId: link.repoId,
1712
+ name: link.skillName,
1713
+ kind: "skill",
1714
+ legacy: link.linkPath.includes(`${path3.sep}.agents${path3.sep}`)
1715
+ })),
1716
+ ...(await (0, import_skill_linker.listLinkedSkills)("", "agent", "user")).map((link) => ({
1717
+ repoId: link.repoId,
1718
+ name: link.skillName,
1719
+ kind: "agent",
1720
+ legacy: link.linkPath.includes(`${path3.sep}.agents${path3.sep}`)
1721
+ }))
1722
+ ]
1723
+ };
1724
+ }
1725
+ function emptySnapshot() {
1726
+ return {
1727
+ sources: [],
1728
+ packages: [],
1729
+ installations: [],
1730
+ statesByArtifactId: {}
1731
+ };
1732
+ }
1733
+ function toPublicSource(source) {
1734
+ return {
1735
+ id: source.id,
1736
+ type: source.type,
1737
+ displayName: source.displayName,
1738
+ updateCapability: source.updateCapability
1739
+ };
1740
+ }
1741
+ function toPublicPackage(pkg) {
1742
+ return {
1743
+ id: pkg.id,
1744
+ sourceId: pkg.sourceId,
1745
+ displayName: pkg.displayName,
1746
+ ...pkg.description !== void 0 ? { description: pkg.description } : {},
1747
+ ...pkg.version !== void 0 ? { version: pkg.version } : {},
1748
+ artifacts: pkg.artifacts.map((artifact) => ({
1749
+ id: artifact.id,
1750
+ packageId: artifact.packageId,
1751
+ kind: artifact.kind,
1752
+ displayName: artifact.displayName,
1753
+ ...artifact.description !== void 0 ? { description: artifact.description } : {},
1754
+ installStrategy: artifact.installStrategy,
1755
+ risk: artifact.risk
1756
+ })),
1757
+ ...pkg.wholePackage === true ? { wholePackage: true } : {}
1758
+ };
1759
+ }
1760
+ function toPublicInstallation(installation) {
1761
+ return {
1762
+ packageId: installation.packageId,
1763
+ scope: installation.scope,
1764
+ selectedArtifactIds: [...installation.selectedArtifactIds],
1765
+ ...installation.pinnedVersion !== void 0 ? { pinnedVersion: installation.pinnedVersion } : {}
1766
+ };
1767
+ }
1768
+ function toPublicStates(states) {
1769
+ return Object.fromEntries(
1770
+ Object.entries(states).map(([artifactId, state]) => [artifactId, { ...state }])
1771
+ );
1772
+ }
1773
+
1774
+ // src/bridge/CopilotPluginBridgeHandler.ts
1775
+ var fs2 = __toESM(require("fs/promises"));
1776
+ var path4 = __toESM(require("path"));
1777
+ var import_devtools_core4 = require("@serviceme/devtools-core");
1778
+ async function readManifestInfo(pluginDir) {
1779
+ try {
1780
+ const raw = JSON.parse(await fs2.readFile(path4.join(pluginDir, "plugin.json"), "utf8"));
1781
+ let mcpServers = [];
1782
+ const mcpPath = path4.join(pluginDir, "mcp.json");
1783
+ try {
1784
+ const mcp = JSON.parse(await fs2.readFile(mcpPath, "utf8"));
1785
+ mcpServers = Object.keys(mcp.mcpServers ?? {});
1786
+ } catch {
1787
+ }
1788
+ return {
1789
+ ...typeof raw.name === "string" ? { displayName: raw.name } : {},
1790
+ ...typeof raw.version === "string" ? { version: raw.version } : {},
1791
+ mcpServers,
1792
+ hasExtensions: Boolean(raw.extensions?.["com.github.copilot"])
1793
+ };
1794
+ } catch {
1795
+ return { mcpServers: [], hasExtensions: false };
1796
+ }
1797
+ }
1798
+ function toPayload(reg, info) {
1799
+ return {
1800
+ registrationId: reg.registrationId,
1801
+ repoId: reg.repoId,
1802
+ pluginId: reg.pluginId,
1803
+ ...reg.displayName ?? info.displayName ? { displayName: reg.displayName ?? info.displayName } : {},
1804
+ ...reg.version ?? info.version ? { version: reg.version ?? info.version } : {},
1805
+ scopes: reg.scopes,
1806
+ mcpServers: info.mcpServers,
1807
+ hasExtensions: info.hasExtensions
1808
+ };
1809
+ }
1810
+ var CopilotPluginBridgeHandler = class {
1811
+ constructor(options = {}) {
1812
+ const homeDir = options.homeDir ?? (0, import_devtools_core4.getHomeDir)();
1813
+ this.registrar = new import_devtools_core4.CopilotPluginRegistrar({
1814
+ // copilotDir is the migration source only — projections live
1815
+ // under the SERVICEME home, out of VS Code's ~/.copilot
1816
+ // reconciliation reach.
1817
+ copilotDir: path4.join(homeDir, ".copilot"),
1818
+ pluginsDir: path4.join(homeDir, ".serviceme", "copilot-plugins")
1819
+ });
1820
+ this.reposDir = path4.join(homeDir, ".serviceme", "repos");
1821
+ }
1822
+ pluginDir(repoId, pluginId) {
1823
+ return path4.join(this.reposDir, (0, import_devtools_core4.assertSafeRepoId)(repoId), "plugins", pluginId);
1824
+ }
1825
+ async list(_params) {
1826
+ const registrations = await this.registrar.list({});
1827
+ const plugins = await Promise.all(
1828
+ registrations.map(async (reg) => toPayload(reg, await readManifestInfo(reg.pluginDir)))
1829
+ );
1830
+ return { plugins };
1831
+ }
1832
+ async register(params) {
1833
+ const pluginDir = this.pluginDir(params.repoId, params.pluginId);
1834
+ const info = await readManifestInfo(pluginDir);
1835
+ await this.registrar.register({
1836
+ repoId: params.repoId,
1837
+ pluginId: params.pluginId,
1838
+ pluginDir,
1839
+ scope: params.scope,
1840
+ ...info.displayName !== void 0 ? { displayName: info.displayName } : {},
1841
+ ...info.version !== void 0 ? { version: info.version } : {}
1842
+ });
1843
+ return this.list({});
1844
+ }
1845
+ async unregister(params) {
1846
+ const normalized = params.registrationId.replace("::", ":");
1847
+ await this.registrar.unregister({
1848
+ registrationId: normalized,
1849
+ scope: params.scope
1850
+ });
1851
+ if (normalized !== params.registrationId) {
1852
+ await this.registrar.unregister({
1853
+ registrationId: params.registrationId,
1854
+ scope: params.scope
1855
+ });
1856
+ }
1857
+ const otherScope = params.scope === "personal" ? "workspace" : "personal";
1858
+ await this.registrar.unregister({
1859
+ registrationId: normalized,
1860
+ scope: otherScope
1861
+ });
1862
+ return this.list({});
1863
+ }
1864
+ };
783
1865
 
784
1866
  // src/bridge/handlers/AuthBridgeHandlers.ts
785
1867
  var import_auth3 = require("@serviceme/devtools-core/auth");
@@ -894,12 +1976,12 @@ function writeBridgeMessage(message) {
894
1976
  }
895
1977
 
896
1978
  // src/bridge/TaskBridgeHandler.ts
897
- var import_devtools_core2 = require("@serviceme/devtools-core");
1979
+ var import_devtools_core5 = require("@serviceme/devtools-core");
898
1980
  var TaskBridgeHandler = class {
899
1981
  constructor(logger, emitEvent) {
900
1982
  this.logger = logger;
901
1983
  this.emitEvent = emitEvent;
902
- this.engine = new import_devtools_core2.TaskExecutionEngine((taskType) => (0, import_devtools_core2.getExecutor)(taskType));
1984
+ this.engine = new import_devtools_core5.TaskExecutionEngine((taskType) => (0, import_devtools_core5.getExecutor)(taskType));
903
1985
  this.engine.setListener({
904
1986
  onStarted: (params) => this.emitEvent("task.started", params),
905
1987
  onOutput: (params) => this.emitEvent("task.output", params),
@@ -942,6 +2024,7 @@ var CAPABILITIES = {
942
2024
  tasks: 1,
943
2025
  skillRepo: 1,
944
2026
  repoMgmt: 1,
2027
+ copilotContent: 1,
945
2028
  auth: 1,
946
2029
  device: 1,
947
2030
  toolbox: 1
@@ -954,6 +2037,9 @@ var BridgeServer = class {
954
2037
  this.writeEvent(event, params);
955
2038
  });
956
2039
  this.skillRepoHandler = opts.skillRepoHandler;
2040
+ this.copilotContentHandler = opts.skillRepoHandler ? new CopilotContentBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
2041
+ this.copilotCustomizationsHandler = opts.skillRepoHandler ? new CopilotCustomizationsBridgeHandler(opts.skillRepoHandler.copilotContentDependencies) : void 0;
2042
+ this.copilotPluginHandler = opts.skillRepoHandler ? new CopilotPluginBridgeHandler() : void 0;
957
2043
  this.authHandler = new AuthBridgeHandlers();
958
2044
  this.deviceHandler = new DeviceBridgeHandlers();
959
2045
  this.toolboxHandler = new ToolboxBridgeHandlers();
@@ -963,12 +2049,12 @@ var BridgeServer = class {
963
2049
  input: process.stdin,
964
2050
  crlfDelay: Number.POSITIVE_INFINITY
965
2051
  });
966
- await new Promise((resolve) => {
2052
+ await new Promise((resolve4) => {
967
2053
  reader.on("line", (line) => {
968
2054
  void this.handleLine(line);
969
2055
  });
970
2056
  reader.on("close", () => {
971
- resolve();
2057
+ resolve4();
972
2058
  });
973
2059
  });
974
2060
  }
@@ -1059,6 +2145,7 @@ var BridgeServer = class {
1059
2145
  this.writeSuccess(request.id, result);
1060
2146
  return;
1061
2147
  }
2148
+ case "copilotContent.list":
1062
2149
  case "skillRepo.list": {
1063
2150
  const r = request;
1064
2151
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1066,6 +2153,7 @@ var BridgeServer = class {
1066
2153
  this.writeSuccess(request.id, result);
1067
2154
  return;
1068
2155
  }
2156
+ case "copilotContent.get":
1069
2157
  case "skillRepo.get": {
1070
2158
  const r = request;
1071
2159
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1073,6 +2161,7 @@ var BridgeServer = class {
1073
2161
  this.writeSuccess(request.id, result);
1074
2162
  return;
1075
2163
  }
2164
+ case "copilotContent.install":
1076
2165
  case "skillRepo.install": {
1077
2166
  const r = request;
1078
2167
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1080,6 +2169,7 @@ var BridgeServer = class {
1080
2169
  this.writeSuccess(request.id, result);
1081
2170
  return;
1082
2171
  }
2172
+ case "copilotContent.convertToSymlink":
1083
2173
  case "skillRepo.convertToSymlink": {
1084
2174
  const r = request;
1085
2175
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1087,6 +2177,7 @@ var BridgeServer = class {
1087
2177
  this.writeSuccess(request.id, result);
1088
2178
  return;
1089
2179
  }
2180
+ case "copilotContent.uninstall":
1090
2181
  case "skillRepo.uninstall": {
1091
2182
  const r = request;
1092
2183
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1094,6 +2185,15 @@ var BridgeServer = class {
1094
2185
  this.writeSuccess(request.id, result);
1095
2186
  return;
1096
2187
  }
2188
+ case "copilotContent.setEntryEnabled":
2189
+ case "skillRepo.setEntryEnabled": {
2190
+ const r = request;
2191
+ if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
2192
+ const result = await this.skillRepoHandler.setEntryEnabled(r.params);
2193
+ this.writeSuccess(request.id, result);
2194
+ return;
2195
+ }
2196
+ case "copilotContent.listLinked":
1097
2197
  case "skillRepo.listLinked": {
1098
2198
  const r = request;
1099
2199
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1101,6 +2201,7 @@ var BridgeServer = class {
1101
2201
  this.writeSuccess(request.id, result);
1102
2202
  return;
1103
2203
  }
2204
+ case "copilotContent.draft.create":
1104
2205
  case "skillRepo.draft.create": {
1105
2206
  const r = request;
1106
2207
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1108,6 +2209,7 @@ var BridgeServer = class {
1108
2209
  this.writeSuccess(request.id, result);
1109
2210
  return;
1110
2211
  }
2212
+ case "copilotContent.draft.commit":
1111
2213
  case "skillRepo.draft.commit": {
1112
2214
  const r = request;
1113
2215
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1115,6 +2217,7 @@ var BridgeServer = class {
1115
2217
  this.writeSuccess(request.id, result);
1116
2218
  return;
1117
2219
  }
2220
+ case "copilotContent.draft.list":
1118
2221
  case "skillRepo.draft.list": {
1119
2222
  const r = request;
1120
2223
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1122,6 +2225,7 @@ var BridgeServer = class {
1122
2225
  this.writeSuccess(request.id, result);
1123
2226
  return;
1124
2227
  }
2228
+ case "copilotContent.draft.delete":
1125
2229
  case "skillRepo.draft.delete": {
1126
2230
  const r = request;
1127
2231
  if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
@@ -1185,6 +2289,140 @@ var BridgeServer = class {
1185
2289
  this.writeSuccess(request.id, result);
1186
2290
  return;
1187
2291
  }
2292
+ case "repo.resetParseCache": {
2293
+ const r = request;
2294
+ if (!this.skillRepoHandler) return this.unsupportedV2(request.id);
2295
+ const result = await this.skillRepoHandler.repoResetParseCache(r.params);
2296
+ this.writeSuccess(request.id, result);
2297
+ return;
2298
+ }
2299
+ case "copilotPlugin.list": {
2300
+ const r = request;
2301
+ if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
2302
+ const listResult = await this.copilotPluginHandler.list(r.params);
2303
+ this.writeSuccess(request.id, listResult);
2304
+ return;
2305
+ }
2306
+ case "copilotPlugin.register": {
2307
+ const r = request;
2308
+ if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
2309
+ const regResult = await this.copilotPluginHandler.register(r.params);
2310
+ this.writeSuccess(request.id, regResult);
2311
+ return;
2312
+ }
2313
+ case "copilotPlugin.unregister": {
2314
+ const r = request;
2315
+ if (!this.copilotPluginHandler) return this.unsupportedV2(request.id);
2316
+ const unregResult = await this.copilotPluginHandler.unregister(r.params);
2317
+ this.writeSuccess(request.id, unregResult);
2318
+ return;
2319
+ }
2320
+ // ── copilotContent.* (declarative reconciliation) ──────────────
2321
+ case "copilotContent.status": {
2322
+ const r = request;
2323
+ if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
2324
+ const result = await this.copilotContentHandler.status(r.params);
2325
+ this.writeSuccess(request.id, result);
2326
+ return;
2327
+ }
2328
+ case "copilotContent.restore": {
2329
+ const r = request;
2330
+ if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
2331
+ const result = await this.copilotContentHandler.restore(r.params);
2332
+ this.writeSuccess(request.id, result);
2333
+ return;
2334
+ }
2335
+ case "copilotContent.approve": {
2336
+ const r = request;
2337
+ if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
2338
+ const result = await this.copilotContentHandler.approve(r.params);
2339
+ this.writeSuccess(request.id, result);
2340
+ return;
2341
+ }
2342
+ case "copilotContent.migrateLegacy": {
2343
+ const r = request;
2344
+ if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
2345
+ const result = await this.copilotContentHandler.migrateLegacy(r.params);
2346
+ this.writeSuccess(request.id, result);
2347
+ return;
2348
+ }
2349
+ case "copilotContent.integrationStatus": {
2350
+ const r = request;
2351
+ if (!this.copilotContentHandler) return this.unsupportedCopilotContent(request.id);
2352
+ const result = await this.copilotContentHandler.integrationStatus(r.params);
2353
+ this.writeSuccess(request.id, result);
2354
+ return;
2355
+ }
2356
+ case "copilotContent.customizations.list": {
2357
+ const r = request;
2358
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2359
+ const result = await this.copilotCustomizationsHandler.list(r.params);
2360
+ this.writeSuccess(request.id, result);
2361
+ return;
2362
+ }
2363
+ case "copilotContent.package.install": {
2364
+ const r = request;
2365
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2366
+ const result = await this.copilotCustomizationsHandler.install(r.params);
2367
+ this.writeSuccess(request.id, result);
2368
+ return;
2369
+ }
2370
+ case "copilotContent.package.update": {
2371
+ const r = request;
2372
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2373
+ const result = await this.copilotCustomizationsHandler.update(r.params);
2374
+ this.writeSuccess(request.id, result);
2375
+ return;
2376
+ }
2377
+ case "copilotContent.package.uninstall": {
2378
+ const r = request;
2379
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2380
+ const result = await this.copilotCustomizationsHandler.uninstall(r.params);
2381
+ this.writeSuccess(request.id, result);
2382
+ return;
2383
+ }
2384
+ case "copilotContent.package.move": {
2385
+ const r = request;
2386
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2387
+ const result = await this.copilotCustomizationsHandler.move(r.params);
2388
+ this.writeSuccess(request.id, result);
2389
+ return;
2390
+ }
2391
+ case "copilotContent.legacy.preview": {
2392
+ const r = request;
2393
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2394
+ const result = await this.copilotCustomizationsHandler.legacyPreview(r.params);
2395
+ this.writeSuccess(request.id, result);
2396
+ return;
2397
+ }
2398
+ case "copilotContent.legacy.migrate": {
2399
+ const r = request;
2400
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2401
+ const result = await this.copilotCustomizationsHandler.legacyMigrate(r.params);
2402
+ this.writeSuccess(request.id, result);
2403
+ return;
2404
+ }
2405
+ case "copilotContent.sources.list": {
2406
+ const r = request;
2407
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2408
+ const result = await this.copilotCustomizationsHandler.sources(r.params);
2409
+ this.writeSuccess(request.id, result);
2410
+ return;
2411
+ }
2412
+ case "copilotContent.sources.remove": {
2413
+ const r = request;
2414
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2415
+ const result = await this.copilotCustomizationsHandler.removeSource(r.params);
2416
+ this.writeSuccess(request.id, result);
2417
+ return;
2418
+ }
2419
+ case "copilotContent.package.previewUpdate": {
2420
+ const r = request;
2421
+ if (!this.copilotCustomizationsHandler) return this.unsupportedCopilotContent(request.id);
2422
+ const result = await this.copilotCustomizationsHandler.previewUpdate(r.params);
2423
+ this.writeSuccess(request.id, result);
2424
+ return;
2425
+ }
1188
2426
  // ── auth.* (Phase 5.4) ─────────────────────────────────────────
1189
2427
  case "auth.status": {
1190
2428
  const r = request;
@@ -1288,6 +2526,15 @@ var BridgeServer = class {
1288
2526
  )
1289
2527
  );
1290
2528
  }
2529
+ unsupportedCopilotContent(id) {
2530
+ this.writeError(
2531
+ id,
2532
+ createServicemeError(
2533
+ "invalid_params",
2534
+ "copilotContent.* methods require v2 CLI deps; the bridge was started without a CopilotContentBridgeHandler."
2535
+ )
2536
+ );
2537
+ }
1291
2538
  writeError(id, error) {
1292
2539
  writeBridgeMessage({
1293
2540
  protocolVersion: SERVICEME_PROTOCOL_VERSION,
@@ -1308,16 +2555,37 @@ var BridgeServer = class {
1308
2555
  };
1309
2556
 
1310
2557
  // src/bridge/SkillRepoBridgeHandler.ts
1311
- var import_devtools_core3 = require("@serviceme/devtools-core");
1312
- var import_skill_linker = require("@serviceme/devtools-core/skill-linker");
2558
+ var fs3 = __toESM(require("fs/promises"));
2559
+ var path5 = __toESM(require("path"));
2560
+ var import_devtools_core6 = require("@serviceme/devtools-core");
2561
+ var import_skill_linker2 = require("@serviceme/devtools-core/skill-linker");
1313
2562
  var import_skill_store = require("@serviceme/devtools-core/skill-store");
1314
2563
  var import_submit = require("@serviceme/devtools-core/submit");
2564
+ var DEFAULT_SKILL_STORE_CACHE_TTL_MS = 5e3;
1315
2565
  var SkillRepoBridgeHandler = class {
1316
2566
  constructor(opts) {
1317
2567
  this.reposStore = opts.reposStore;
1318
2568
  this.gitClient = opts.gitClient;
1319
2569
  this.draftsStore = opts.draftsStore;
1320
2570
  this.submitClient = opts.submitClient;
2571
+ this.skillStoreCacheTtlMs = opts.skillStoreCacheTtlMs ?? DEFAULT_SKILL_STORE_CACHE_TTL_MS;
2572
+ }
2573
+ /** Shared deps for sibling handlers (copilotContent.*). */
2574
+ get dependencies() {
2575
+ return {
2576
+ reposStore: this.reposStore,
2577
+ gitClient: this.gitClient,
2578
+ draftsStore: this.draftsStore,
2579
+ submitClient: this.submitClient
2580
+ };
2581
+ }
2582
+ /** Narrowed deps for CopilotContentBridgeHandler construction. */
2583
+ get copilotContentDependencies() {
2584
+ return {
2585
+ store: this.reposStore,
2586
+ gitClient: this.gitClient,
2587
+ skipClone: true
2588
+ };
1321
2589
  }
1322
2590
  // ─────────────────────────────────────────────────────────────────
1323
2591
  // skillRepo.*
@@ -1327,11 +2595,22 @@ var SkillRepoBridgeHandler = class {
1327
2595
  * filter by `repoId` or `kind`. Local-only — no git, no network.
1328
2596
  */
1329
2597
  async list(params) {
1330
- const store = this.buildSkillStore(params.repoId);
2598
+ const store = this.buildSkillStore();
1331
2599
  const entries = params.repoId ? await store.listByRepo(params.repoId) : await store.listAll();
1332
2600
  const filtered = params.kind ? entries.filter((e) => e.kind === params.kind) : entries;
2601
+ let marks = [];
2602
+ try {
2603
+ marks = await new import_devtools_core6.DisabledContentStore().list();
2604
+ } catch {
2605
+ }
2606
+ const isDisabled = (entry) => marks.some(
2607
+ (mark) => mark.repoId === entry.repoId && mark.name === entry.name && mark.kind === entry.kind && (mark.scope === "user" || params.workspaceDir !== void 0 && mark.workspaceDir === params.workspaceDir)
2608
+ );
1333
2609
  return {
1334
- entries: filtered.map(toBridgeEntry)
2610
+ entries: filtered.map((entry) => ({
2611
+ ...toBridgeEntry(entry),
2612
+ disabled: isDisabled(entry)
2613
+ }))
1335
2614
  };
1336
2615
  }
1337
2616
  /**
@@ -1339,7 +2618,7 @@ var SkillRepoBridgeHandler = class {
1339
2618
  * Throws `skill_not_found` when the entry doesn't exist.
1340
2619
  */
1341
2620
  async get(params) {
1342
- const store = this.buildSkillStore(params.repoId);
2621
+ const store = this.buildSkillStore();
1343
2622
  let detail;
1344
2623
  try {
1345
2624
  detail = await store.get(params.repoId, params.name);
@@ -1368,31 +2647,100 @@ var SkillRepoBridgeHandler = class {
1368
2647
  async install(params) {
1369
2648
  const kind = params.kind ?? "skill";
1370
2649
  const source = await this.resolveEntrySource(params.repoId, params.name, kind);
1371
- try {
1372
- const result = await (0, import_skill_linker.installSkillToWorkspace)({
1373
- repoId: params.repoId,
1374
- skillName: params.name,
1375
- workspaceDir: params.workspaceDir,
1376
- mode: params.mode,
1377
- kind,
1378
- scope: params.scope,
1379
- sourcePath: source.sourcePath,
1380
- sourceIsFile: source.sourceIsFile
1381
- });
1382
- return {
1383
- mode: result.mode,
1384
- linkPath: result.linkPath,
1385
- targetPath: result.targetPath
1386
- };
1387
- } catch (err) {
1388
- if (err instanceof import_skill_linker.LinkError) {
1389
- throw createServicemeError(
1390
- "internal_error",
1391
- `Failed to link ${params.repoId}/${params.name}: ${err.message}`
1392
- );
2650
+ if ((params.scope ?? "workspace") === "workspace") {
2651
+ return this.installThroughDeclaration(params, kind, source);
2652
+ }
2653
+ return this.installUserScope(params, kind, source);
2654
+ }
2655
+ /**
2656
+ * Workspace installs go through the shared declaration: pin the
2657
+ * repository at its current commit, add the selection to
2658
+ * .github/serviceme-plugins.json, then let the reconciler create
2659
+ * the owned link. This keeps the manifest the single source of
2660
+ * truth teammates restore from.
2661
+ */
2662
+ async installThroughDeclaration(params, kind, source) {
2663
+ const repoRoot = (0, import_devtools_core6.getRepoDir)(params.repoId);
2664
+ const repo = this.reposStore.get(params.repoId);
2665
+ if (!repo) {
2666
+ throw createServicemeError("not_found", `Repository ${params.repoId} not found`);
2667
+ }
2668
+ const commit = await this.resolvePinnedCommit(params.workspaceDir, repoRoot, params.repoId);
2669
+ if (!/^[0-9a-f]{40}$/.test(commit)) {
2670
+ throw createServicemeError(
2671
+ "internal_error",
2672
+ `Cannot pin repository ${params.repoId}: git returned an invalid commit '${commit}'`
2673
+ );
2674
+ }
2675
+ await (0, import_devtools_core6.upsertWorkspaceContentSelection)({
2676
+ workspaceDir: params.workspaceDir,
2677
+ repository: { id: repo.id, url: repo.url, commit },
2678
+ pluginId: params.name,
2679
+ kind
2680
+ });
2681
+ const restored = await this.reconcileInstalledContent(params.workspaceDir);
2682
+ const linkBasename = source.sourceIsFile ? path5.basename(source.sourcePath) : params.name;
2683
+ const expectedIdentity = `${params.repoId}::${params.name}::${kind}:${stripAgentSuffix(linkBasename)}`;
2684
+ const entry = restored.entries.find((candidate) => candidate.identity === expectedIdentity);
2685
+ if (!entry || entry.status !== "restored" && entry.status !== "adopted") {
2686
+ throw createServicemeError(
2687
+ "internal_error",
2688
+ `Copilot content ${expectedIdentity} failed to activate: ${entry?.message ?? "no matching entry after reconcile"}`
2689
+ );
2690
+ }
2691
+ const linkPath = path5.join(
2692
+ params.workspaceDir,
2693
+ ".github",
2694
+ kind === "agent" ? "agents" : "skills",
2695
+ linkBasename
2696
+ );
2697
+ const targetStat = await fs3.lstat(linkPath);
2698
+ if (!targetStat.isSymbolicLink() && !targetStat.isDirectory()) {
2699
+ throw createServicemeError(
2700
+ "internal_error",
2701
+ `Copilot content link at ${linkPath} was not created by reconcile`
2702
+ );
2703
+ }
2704
+ const target = await fs3.readlink(linkPath).catch(() => linkPath);
2705
+ return { mode: "symlink", linkPath, targetPath: target };
2706
+ }
2707
+ /**
2708
+ * User installs create a local ~/.copilot link without touching the shared
2709
+ * declaration. Idempotent: an already-correct link is a no-op, any other
2710
+ * stale entry at the path is replaced. No copy fallback.
2711
+ */
2712
+ async installUserScope(params, kind, source) {
2713
+ const userRoot = path5.join((0, import_devtools_core6.getHomeDir)(), ".copilot", kind === "agent" ? "agents" : "skills");
2714
+ const linkPath = path5.join(
2715
+ userRoot,
2716
+ source.sourceIsFile ? path5.basename(source.sourcePath) : params.name
2717
+ );
2718
+ await fs3.mkdir(path5.dirname(linkPath), { recursive: true });
2719
+ const existingTarget = await fs3.readlink(linkPath).catch(() => void 0);
2720
+ if (existingTarget !== void 0) {
2721
+ const normalizedExisting = path5.resolve(path5.dirname(linkPath), existingTarget);
2722
+ if (normalizedExisting === path5.resolve(source.sourcePath)) {
2723
+ return { mode: "symlink", linkPath, targetPath: source.sourcePath };
1393
2724
  }
1394
- throw err;
2725
+ await fs3.rm(linkPath, { recursive: true, force: true });
2726
+ }
2727
+ await fs3.symlink(source.sourcePath, linkPath, source.sourceIsFile ? "file" : "dir");
2728
+ return { mode: "symlink", linkPath, targetPath: source.sourcePath };
2729
+ }
2730
+ async resolvePinnedCommit(workspaceDir, repoRoot, repoId) {
2731
+ const existing = await (0, import_devtools_core6.loadWorkspaceCopilotManifest)(workspaceDir);
2732
+ if (existing) {
2733
+ const declared = (0, import_devtools_core6.getWorkspaceManifestSources)(existing).find(
2734
+ (source) => source.id === repoId && source.type === "git"
2735
+ );
2736
+ if (declared) return declared.commit;
1395
2737
  }
2738
+ return this.gitClient.revParseHead(repoRoot);
2739
+ }
2740
+ async reconcileInstalledContent(workspaceDir) {
2741
+ const { CopilotContentBridgeHandler: CopilotContentBridgeHandler2 } = await Promise.resolve().then(() => (init_CopilotContentBridgeHandler(), CopilotContentBridgeHandler_exports));
2742
+ const handler = new CopilotContentBridgeHandler2(this.copilotContentDependencies);
2743
+ return handler.restore({ workspaceDir });
1396
2744
  }
1397
2745
  /**
1398
2746
  * Replace an existing, real (non-symlink) skill/agent directory at
@@ -1404,7 +2752,7 @@ var SkillRepoBridgeHandler = class {
1404
2752
  const kind = params.kind ?? "skill";
1405
2753
  const source = await this.resolveEntrySource(params.repoId, params.name, kind);
1406
2754
  try {
1407
- const result = await (0, import_skill_linker.convertToSymlink)({
2755
+ const result = await (0, import_skill_linker2.convertToSymlink)({
1408
2756
  repoId: params.repoId,
1409
2757
  skillName: params.name,
1410
2758
  workspaceDir: params.workspaceDir,
@@ -1420,7 +2768,7 @@ var SkillRepoBridgeHandler = class {
1420
2768
  targetPath: result.targetPath
1421
2769
  };
1422
2770
  } catch (err) {
1423
- if (err instanceof import_skill_linker.LinkError) {
2771
+ if (err instanceof import_skill_linker2.LinkError) {
1424
2772
  throw createServicemeError(
1425
2773
  "internal_error",
1426
2774
  `Failed to link ${params.repoId}/${params.name}: ${err.message}`
@@ -1434,12 +2782,22 @@ var SkillRepoBridgeHandler = class {
1434
2782
  }
1435
2783
  /** Inverse of `install`. Throws when no link exists. */
1436
2784
  async uninstall(params) {
2785
+ const kind = params.kind ?? "skill";
2786
+ if ((params.scope ?? "workspace") === "workspace") {
2787
+ await (0, import_devtools_core6.removeWorkspaceContentSelection)({
2788
+ workspaceDir: params.workspaceDir,
2789
+ repositoryId: params.repoId,
2790
+ pluginId: params.name,
2791
+ kind
2792
+ });
2793
+ await this.reconcileInstalledContent(params.workspaceDir);
2794
+ }
1437
2795
  try {
1438
- await (0, import_skill_linker.uninstallSkillFromWorkspace)({
2796
+ await (0, import_skill_linker2.uninstallSkillFromWorkspace)({
1439
2797
  repoId: params.repoId,
1440
2798
  skillName: params.name,
1441
2799
  workspaceDir: params.workspaceDir,
1442
- kind: params.kind ?? "skill",
2800
+ kind,
1443
2801
  scope: params.scope
1444
2802
  });
1445
2803
  } catch (err) {
@@ -1453,6 +2811,69 @@ var SkillRepoBridgeHandler = class {
1453
2811
  }
1454
2812
  return { removed: true };
1455
2813
  }
2814
+ /**
2815
+ * Enable/disable a per-artifact skill/agent WITHOUT dropping its
2816
+ * installation. Disable records a machine-local mark and removes the
2817
+ * materialized link (the workspace manifest declaration stays); the
2818
+ * reconciler treats marked identities as undeclared, so a restore
2819
+ * never resurrects them. Enable clears the mark and re-links.
2820
+ */
2821
+ async setEntryEnabled(params) {
2822
+ const kind = params.kind ?? "skill";
2823
+ const scope = params.scope ?? "workspace";
2824
+ if (scope === "workspace" && !params.workspaceDir) {
2825
+ throw createServicemeError("invalid_params", "workspaceDir is required in workspace scope");
2826
+ }
2827
+ const store = new import_devtools_core6.DisabledContentStore();
2828
+ const mark = {
2829
+ scope,
2830
+ repoId: params.repoId,
2831
+ name: params.name,
2832
+ kind,
2833
+ ...scope === "workspace" && params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}
2834
+ };
2835
+ if (params.enabled) {
2836
+ await store.remove(mark);
2837
+ if (scope === "workspace") {
2838
+ await this.reconcileInstalledContent(params.workspaceDir);
2839
+ } else {
2840
+ const source = await this.resolveEntrySource(params.repoId, params.name, kind);
2841
+ await this.installUserScope(
2842
+ { ...params, workspaceDir: params.workspaceDir ?? "", scope: "user" },
2843
+ kind,
2844
+ source
2845
+ );
2846
+ }
2847
+ return { enabled: true };
2848
+ }
2849
+ await store.add(mark);
2850
+ if (scope === "workspace") {
2851
+ const stateStore = new import_devtools_core6.WorkspaceContentStateStore({
2852
+ workspaceDir: params.workspaceDir
2853
+ });
2854
+ const state = await stateStore.read();
2855
+ for (const previous of state.entries) {
2856
+ if (previous.repositoryId !== params.repoId || previous.pluginId !== params.name || previous.kind !== kind || !previous.linkPath.replace(/\\/g, "/").includes(".github")) {
2857
+ continue;
2858
+ }
2859
+ await fs3.rm(path5.resolve(params.workspaceDir, previous.linkPath), {
2860
+ force: true,
2861
+ recursive: true
2862
+ });
2863
+ }
2864
+ await this.reconcileInstalledContent(params.workspaceDir);
2865
+ } else {
2866
+ let base = params.name;
2867
+ try {
2868
+ const source = await this.resolveEntrySource(params.repoId, params.name, kind);
2869
+ base = source.sourceIsFile ? path5.basename(source.sourcePath) : params.name;
2870
+ } catch {
2871
+ }
2872
+ const userRoot = path5.join((0, import_devtools_core6.getHomeDir)(), ".copilot", kind === "agent" ? "agents" : "skills");
2873
+ await fs3.rm(path5.join(userRoot, base), { force: true, recursive: true });
2874
+ }
2875
+ return { enabled: false };
2876
+ }
1456
2877
  /**
1457
2878
  * List every skill currently linked into a workspace and/or the
1458
2879
  * global user scope. `scope` defaults to `"workspace"` for
@@ -1463,7 +2884,7 @@ var SkillRepoBridgeHandler = class {
1463
2884
  const kind = params.kind ?? "skill";
1464
2885
  const scope = params.scope ?? "workspace";
1465
2886
  const scopesToScan = scope === "all" ? ["workspace", "user"] : [scope];
1466
- const linked = (await Promise.all(scopesToScan.map((s) => (0, import_skill_linker.listLinkedSkills)(params.workspaceDir, kind, s)))).flat();
2887
+ const linked = (await Promise.all(scopesToScan.map((s) => (0, import_skill_linker2.listLinkedSkills)(params.workspaceDir, kind, s)))).flat();
1467
2888
  return {
1468
2889
  links: linked.map((l) => ({
1469
2890
  repoId: l.repoId,
@@ -1493,7 +2914,7 @@ var SkillRepoBridgeHandler = class {
1493
2914
  });
1494
2915
  return { id };
1495
2916
  } catch (err) {
1496
- if (err instanceof import_devtools_core3.InvalidDraftError) {
2917
+ if (err instanceof import_devtools_core6.InvalidDraftError) {
1497
2918
  throw createServicemeError("invalid_params", err.message);
1498
2919
  }
1499
2920
  throw err;
@@ -1518,6 +2939,7 @@ var SkillRepoBridgeHandler = class {
1518
2939
  detail.files,
1519
2940
  { branch: params.branch, skipPush: params.skipPush }
1520
2941
  );
2942
+ this.invalidateSkillStoreCache();
1521
2943
  return {
1522
2944
  repoId: result.repoId,
1523
2945
  skillName: result.skillName,
@@ -1639,6 +3061,7 @@ var SkillRepoBridgeHandler = class {
1639
3061
  async repoSync(params) {
1640
3062
  const repoManager = this.buildRepoManager();
1641
3063
  const result = await repoManager.pullOne(params.repoId);
3064
+ this.invalidateSkillStoreCache();
1642
3065
  return {
1643
3066
  pulls: [
1644
3067
  {
@@ -1653,6 +3076,7 @@ var SkillRepoBridgeHandler = class {
1653
3076
  async repoSyncAll(_params) {
1654
3077
  const repoManager = this.buildRepoManager();
1655
3078
  const report = await repoManager.pullAll();
3079
+ this.invalidateSkillStoreCache();
1656
3080
  return {
1657
3081
  pulls: report.pulls.map((p) => ({
1658
3082
  repoId: p.repoId,
@@ -1662,19 +3086,69 @@ var SkillRepoBridgeHandler = class {
1662
3086
  }))
1663
3087
  };
1664
3088
  }
3089
+ /**
3090
+ * `repo.resetParseCache` — recover from historical-parser drift.
3091
+ * Deletes each enabled repo's local checkout so the follow-up
3092
+ * pull re-clones it, then re-lists through the current SkillStore
3093
+ * parsing rules. Fresh mtimes also invalidate the plugin-catalog
3094
+ * mtime cache, so every downstream list rebuilds from scratch.
3095
+ */
3096
+ async repoResetParseCache(_params) {
3097
+ const repoManager = this.buildRepoManager();
3098
+ const pulls = [];
3099
+ for (const repo of this.reposStore.list().filter((r) => r.enabled)) {
3100
+ const checkout = (0, import_devtools_core6.getRepoDir)(repo.id);
3101
+ await fs3.rm(checkout, { recursive: true, force: true });
3102
+ try {
3103
+ const result = await repoManager.pullOne(repo.id);
3104
+ pulls.push({
3105
+ repoId: repo.id,
3106
+ status: "ok",
3107
+ commitSha: result.commitSha || void 0
3108
+ });
3109
+ } catch (error) {
3110
+ pulls.push({
3111
+ repoId: repo.id,
3112
+ status: "error",
3113
+ error: error instanceof Error ? error.message : String(error)
3114
+ });
3115
+ }
3116
+ }
3117
+ this.invalidateSkillStoreCache();
3118
+ return { pulls };
3119
+ }
1665
3120
  // ─────────────────────────────────────────────────────────────────
1666
3121
  // Internals
1667
3122
  // ─────────────────────────────────────────────────────────────────
1668
3123
  /**
1669
- * Build a fresh SkillStore from the current repos.json state. The
1670
- * SkillStore is read-only it does not mutate the repos store —
1671
- * so per-call instantiation is safe and keeps us in sync with
1672
- * fs.watch-triggered writes from the CLI's `repos` subcommand.
3124
+ * Return a SkillStore over the current enabled repos, reusing the
3125
+ * previously built one while the enabled-repo set is unchanged and
3126
+ * the reuse window hasn't elapsed. The store is read-only it does
3127
+ * not mutate the repos store — and its memoized catalog walk is the
3128
+ * expensive part, so reuse is what keeps the webview's startup
3129
+ * polling from rescanning every checkout. A `repoIdFilter` no longer
3130
+ * narrows the constructed store: the shared store spans all enabled
3131
+ * repos and the caller walks just the one it asked for, which keeps
3132
+ * cache reuse across differently-filtered calls. Disabled repos are
3133
+ * never in the store, so a filtered walk over one yields [] exactly
3134
+ * as before.
1673
3135
  */
1674
- buildSkillStore(repoIdFilter) {
1675
- const all = this.reposStore.list();
1676
- const repos = all.filter((r) => r.enabled).filter((r) => !repoIdFilter || r.id === repoIdFilter).map((r) => ({ id: r.id, rootPath: (0, import_devtools_core3.getRepoDir)(r.id) }));
1677
- return new import_skill_store.SkillStore({ repos });
3136
+ buildSkillStore() {
3137
+ const enabled = this.reposStore.list().filter((r) => r.enabled);
3138
+ const signature = enabled.map((r) => r.id).sort().join("\0");
3139
+ const cached = this.skillStoreCache;
3140
+ if (this.skillStoreCacheTtlMs > 0 && cached && cached.signature === signature && Date.now() - cached.computedAt < this.skillStoreCacheTtlMs) {
3141
+ return cached.store;
3142
+ }
3143
+ const store = new import_skill_store.SkillStore({
3144
+ repos: enabled.map((r) => ({ id: r.id, rootPath: (0, import_devtools_core6.getRepoDir)(r.id) }))
3145
+ });
3146
+ this.skillStoreCache = { signature, store, computedAt: Date.now() };
3147
+ return store;
3148
+ }
3149
+ /** Drop the cached store so the next list/get re-walks from disk. */
3150
+ invalidateSkillStoreCache() {
3151
+ this.skillStoreCache = void 0;
1678
3152
  }
1679
3153
  /**
1680
3154
  * Resolve an entry's actual on-disk location via `SkillStore` for
@@ -1688,7 +3162,7 @@ var SkillRepoBridgeHandler = class {
1688
3162
  * for.
1689
3163
  */
1690
3164
  async resolveEntrySource(repoId, name, kind) {
1691
- const store = this.buildSkillStore(repoId);
3165
+ const store = this.buildSkillStore();
1692
3166
  let entry;
1693
3167
  try {
1694
3168
  entry = await store.get(repoId, name);
@@ -1709,9 +3183,12 @@ var SkillRepoBridgeHandler = class {
1709
3183
  store: this.reposStore,
1710
3184
  gitClient: this.gitClient
1711
3185
  };
1712
- return new import_devtools_core3.RepoManager(opts);
3186
+ return new import_devtools_core6.RepoManager(opts);
1713
3187
  }
1714
3188
  };
3189
+ function stripAgentSuffix(basename4) {
3190
+ return basename4.replace(/\.agent\.md$/, "");
3191
+ }
1715
3192
  function toBridgeEntry(e) {
1716
3193
  return {
1717
3194
  repoId: e.repoId,
@@ -1745,20 +3222,20 @@ function toBridgeRepoEntry(repo) {
1745
3222
  // src/commands/bridge.ts
1746
3223
  var DEFAULT_GIT_PROXY_BASE = "http://127.0.0.1:3000/git-proxy";
1747
3224
  async function runBridgeCommand() {
1748
- const logger = (0, import_devtools_core4.createConsoleLogger)("serviceme:bridge");
1749
- const configPath = (0, import_devtools_core4.getReposConfigPath)();
1750
- const reposStore = new import_devtools_core4.ReposStore({
1751
- loader: new import_devtools_core4.ReposLoader({ configPath })
3225
+ const logger = (0, import_devtools_core7.createConsoleLogger)("serviceme:bridge");
3226
+ const configPath = (0, import_devtools_core7.getReposConfigPath)();
3227
+ const reposStore = new import_devtools_core7.ReposStore({
3228
+ loader: new import_devtools_core7.ReposLoader({ configPath })
1752
3229
  });
1753
3230
  try {
1754
- await (0, import_devtools_core4.bootstrapDefaults)(reposStore);
3231
+ await (0, import_devtools_core7.bootstrapDefaults)(reposStore);
1755
3232
  } catch (err) {
1756
3233
  logger.warn("repos.json bootstrap failed; continuing with empty config", err);
1757
3234
  }
1758
3235
  const serverProxyBase = process.env.SERVICEME_GIT_PROXY_BASE ?? DEFAULT_GIT_PROXY_BASE;
1759
- const gitClient = new import_devtools_core4.GitClient({ serverProxyBase });
1760
- const draftsStore = new import_devtools_core4.DraftsStore();
1761
- const submitClient = new import_devtools_core4.SubmitClient({ gitClient });
3236
+ const gitClient = new import_devtools_core7.GitClient({ serverProxyBase });
3237
+ const draftsStore = new import_devtools_core7.DraftsStore();
3238
+ const submitClient = new import_devtools_core7.SubmitClient({ gitClient });
1762
3239
  const skillRepoHandler = new SkillRepoBridgeHandler({
1763
3240
  reposStore,
1764
3241
  gitClient,
@@ -1770,7 +3247,7 @@ async function runBridgeCommand() {
1770
3247
  }
1771
3248
 
1772
3249
  // src/commands/copilot.ts
1773
- var import_devtools_core5 = require("@serviceme/devtools-core");
3250
+ var import_devtools_core8 = require("@serviceme/devtools-core");
1774
3251
  async function runCopilotCommand(parsed) {
1775
3252
  const action = parsed.positionals[1];
1776
3253
  switch (action) {
@@ -1785,12 +3262,12 @@ async function runCopilotCommand(parsed) {
1785
3262
  }
1786
3263
  }
1787
3264
  async function handleDoctor() {
1788
- const result = await (0, import_devtools_core5.copilotDoctor)();
3265
+ const result = await (0, import_devtools_core8.copilotDoctor)();
1789
3266
  if (!result.installed) {
1790
- throw (0, import_devtools_core5.createCopilotNotInstalledError)();
3267
+ throw (0, import_devtools_core8.createCopilotNotInstalledError)();
1791
3268
  }
1792
3269
  if (!result.authenticated) {
1793
- throw (0, import_devtools_core5.createCopilotAuthRequiredError)();
3270
+ throw (0, import_devtools_core8.createCopilotAuthRequiredError)();
1794
3271
  }
1795
3272
  writeSuccess(result);
1796
3273
  }
@@ -1807,7 +3284,7 @@ async function handlePrompt(parsed) {
1807
3284
  const allowTools = allowToolRaw ? String(allowToolRaw).split(",") : void 0;
1808
3285
  const model = parsed.flags.get("model");
1809
3286
  const agent = parsed.flags.get("agent");
1810
- const result = await (0, import_devtools_core5.copilotPrompt)({
3287
+ const result = await (0, import_devtools_core8.copilotPrompt)({
1811
3288
  prompt,
1812
3289
  workspace,
1813
3290
  autopilot,
@@ -1883,9 +3360,9 @@ async function handleRotateSecret(parsed) {
1883
3360
  }
1884
3361
 
1885
3362
  // src/commands/env.ts
1886
- var import_devtools_core6 = require("@serviceme/devtools-core");
3363
+ var import_devtools_core9 = require("@serviceme/devtools-core");
1887
3364
  async function runEnvCommand(parsed) {
1888
- const inspector = new import_devtools_core6.EnvironmentInspector();
3365
+ const inspector = new import_devtools_core9.EnvironmentInspector();
1889
3366
  const action = parsed.positionals[1];
1890
3367
  if (action !== "check") {
1891
3368
  throw new Error("Unsupported env command. Use check.");
@@ -1902,10 +3379,10 @@ async function runEnvCommand(parsed) {
1902
3379
  }
1903
3380
 
1904
3381
  // src/commands/image.ts
1905
- var import_devtools_core7 = require("@serviceme/devtools-core");
3382
+ var import_devtools_core10 = require("@serviceme/devtools-core");
1906
3383
  var IMAGE_FORMATS = /* @__PURE__ */ new Set(["jpeg", "png", "webp"]);
1907
3384
  async function runImageCommand(parsed) {
1908
- const imageTools = (0, import_devtools_core7.createImageTools)();
3385
+ const imageTools = (0, import_devtools_core10.createImageTools)();
1909
3386
  const action = parsed.positionals[1];
1910
3387
  const filePath = getStringFlag(parsed, "file");
1911
3388
  const sharpModulePath = getStringFlag(parsed, "sharpModulePath");
@@ -1940,13 +3417,13 @@ async function runImageCommand(parsed) {
1940
3417
  }
1941
3418
 
1942
3419
  // src/commands/json.ts
1943
- var import_devtools_core8 = require("@serviceme/devtools-core");
3420
+ var import_devtools_core11 = require("@serviceme/devtools-core");
1944
3421
 
1945
3422
  // src/input.ts
1946
- var fs2 = __toESM(require("fs/promises"));
3423
+ var fs4 = __toESM(require("fs/promises"));
1947
3424
  async function readCommandInput(options) {
1948
3425
  if (options.filePath) {
1949
- return fs2.readFile(options.filePath, "utf8");
3426
+ return fs4.readFile(options.filePath, "utf8");
1950
3427
  }
1951
3428
  if (options.stdin) {
1952
3429
  return readStdin();
@@ -1963,7 +3440,7 @@ async function readStdin() {
1963
3440
 
1964
3441
  // src/commands/json.ts
1965
3442
  async function runJsonCommand(parsed) {
1966
- const jsonTools = (0, import_devtools_core8.createJsonTools)();
3443
+ const jsonTools = (0, import_devtools_core11.createJsonTools)();
1967
3444
  const action = parsed.positionals[1];
1968
3445
  const input = await readCommandInput({
1969
3446
  stdin: getBooleanFlag(parsed, "stdin"),
@@ -1999,9 +3476,9 @@ async function runJsonCommand(parsed) {
1999
3476
  }
2000
3477
 
2001
3478
  // src/commands/project.ts
2002
- var import_devtools_core9 = require("@serviceme/devtools-core");
3479
+ var import_devtools_core12 = require("@serviceme/devtools-core");
2003
3480
  async function runProjectCommand(parsed) {
2004
- const projectTools = (0, import_devtools_core9.createProjectTools)();
3481
+ const projectTools = (0, import_devtools_core12.createProjectTools)();
2005
3482
  const action = parsed.positionals[1];
2006
3483
  const workspacePath = getStringFlag(parsed, "workspacePath");
2007
3484
  if (!workspacePath) {
@@ -2052,7 +3529,7 @@ async function runProjectCommand(parsed) {
2052
3529
  }
2053
3530
 
2054
3531
  // src/commands/repos.ts
2055
- var import_devtools_core10 = require("@serviceme/devtools-core");
3532
+ var import_devtools_core13 = require("@serviceme/devtools-core");
2056
3533
  function requireRepoId(parsed) {
2057
3534
  const repoId = getStringFlag(parsed, "repo-id");
2058
3535
  if (!repoId) {
@@ -2062,10 +3539,10 @@ function requireRepoId(parsed) {
2062
3539
  }
2063
3540
  function newStoreAndManager(parsed) {
2064
3541
  const proxyBase = getStringFlag(parsed, "git-proxy-base") ?? "http://127.0.0.1:3000/git-proxy";
2065
- const loader = new import_devtools_core10.ReposLoader({ configPath: (0, import_devtools_core10.getReposConfigPath)() });
2066
- const store = new import_devtools_core10.ReposStore({ loader });
2067
- const gitClient = new import_devtools_core10.GitClient({ serverProxyBase: proxyBase });
2068
- const manager = new import_devtools_core10.RepoManager({ store, gitClient });
3542
+ const loader = new import_devtools_core13.ReposLoader({ configPath: (0, import_devtools_core13.getReposConfigPath)() });
3543
+ const store = new import_devtools_core13.ReposStore({ loader });
3544
+ const gitClient = new import_devtools_core13.GitClient({ serverProxyBase: proxyBase });
3545
+ const manager = new import_devtools_core13.RepoManager({ store, gitClient });
2069
3546
  return { store, manager };
2070
3547
  }
2071
3548
  async function ensureStore(store) {
@@ -2084,7 +3561,7 @@ async function handleList2(parsed) {
2084
3561
  enabled: r.enabled,
2085
3562
  writeEnabled: r.writeEnabled,
2086
3563
  source: r.source,
2087
- description: (0, import_devtools_core10.isDefaultRepo)(r) ? r.description : void 0,
3564
+ description: (0, import_devtools_core13.isDefaultRepo)(r) ? r.description : void 0,
2088
3565
  addedAt: r.addedAt,
2089
3566
  lastSyncAt: r.lastSyncAt,
2090
3567
  lastSyncCommitSha: r.lastSyncCommitSha,
@@ -2137,7 +3614,7 @@ async function handleEnable(parsed) {
2137
3614
  enabled: repo.enabled,
2138
3615
  writeEnabled: repo.writeEnabled,
2139
3616
  source: repo.source,
2140
- description: (0, import_devtools_core10.isDefaultRepo)(repo) ? repo.description : void 0,
3617
+ description: (0, import_devtools_core13.isDefaultRepo)(repo) ? repo.description : void 0,
2141
3618
  addedAt: repo.addedAt
2142
3619
  }
2143
3620
  };
@@ -2154,7 +3631,7 @@ async function handleDisable(parsed) {
2154
3631
  enabled: repo.enabled,
2155
3632
  writeEnabled: repo.writeEnabled,
2156
3633
  source: repo.source,
2157
- description: (0, import_devtools_core10.isDefaultRepo)(repo) ? repo.description : void 0,
3634
+ description: (0, import_devtools_core13.isDefaultRepo)(repo) ? repo.description : void 0,
2158
3635
  addedAt: repo.addedAt
2159
3636
  }
2160
3637
  };
@@ -2223,9 +3700,9 @@ async function runReposCommand(parsed) {
2223
3700
  }
2224
3701
 
2225
3702
  // src/commands/schedule.ts
2226
- var fs3 = __toESM(require("fs"));
2227
- var path2 = __toESM(require("path"));
2228
- var import_devtools_core11 = require("@serviceme/devtools-core");
3703
+ var fs5 = __toESM(require("fs"));
3704
+ var path6 = __toESM(require("path"));
3705
+ var import_devtools_core14 = require("@serviceme/devtools-core");
2229
3706
  async function runScheduleCommand(parsed) {
2230
3707
  const action = parsed.positionals[1];
2231
3708
  if (getBooleanFlag(parsed, "describe")) {
@@ -2269,7 +3746,7 @@ function requireWorkspace(parsed) {
2269
3746
  if (!wp) {
2270
3747
  throw createServicemeError("invalid_params", "Missing required flag: --workspacePath");
2271
3748
  }
2272
- if (!fs3.existsSync(wp)) {
3749
+ if (!fs5.existsSync(wp)) {
2273
3750
  throw createServicemeError("workspace_not_found", `Workspace path does not exist: ${wp}`);
2274
3751
  }
2275
3752
  return wp;
@@ -2408,7 +3885,7 @@ async function handleCreate(parsed) {
2408
3885
  const payload = parsePayload(parsed, taskType);
2409
3886
  const description = getStringFlag(parsed, "description");
2410
3887
  const enabled = getStringFlag(parsed, "enabled") !== "false";
2411
- const mgr = new import_devtools_core11.TaskConfigManager();
3888
+ const mgr = new import_devtools_core14.TaskConfigManager();
2412
3889
  const existing = mgr.getTaskByName(name);
2413
3890
  if (existing) {
2414
3891
  throw createServicemeError(
@@ -2425,7 +3902,7 @@ async function handleCreate(parsed) {
2425
3902
  payload,
2426
3903
  workspace: {
2427
3904
  path: wp,
2428
- name: path2.basename(wp) || wp
3905
+ name: path6.basename(wp) || wp
2429
3906
  },
2430
3907
  enabled
2431
3908
  };
@@ -2450,7 +3927,7 @@ async function handleCreate(parsed) {
2450
3927
  }
2451
3928
  function handleList3(parsed) {
2452
3929
  requireWorkspace(parsed);
2453
- const mgr = new import_devtools_core11.TaskConfigManager();
3930
+ const mgr = new import_devtools_core14.TaskConfigManager();
2454
3931
  const tasks = mgr.listTasks();
2455
3932
  const fields = getStringFlag(parsed, "fields");
2456
3933
  const limitStr = getStringFlag(parsed, "limit");
@@ -2466,7 +3943,7 @@ function handleList3(parsed) {
2466
3943
  function handleGet(parsed) {
2467
3944
  requireWorkspace(parsed);
2468
3945
  const id = requireId(parsed);
2469
- const mgr = new import_devtools_core11.TaskConfigManager();
3946
+ const mgr = new import_devtools_core14.TaskConfigManager();
2470
3947
  const task = mgr.getTask(id);
2471
3948
  if (!task) {
2472
3949
  throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
@@ -2478,7 +3955,7 @@ function handleGet(parsed) {
2478
3955
  async function handleEdit(parsed) {
2479
3956
  requireWorkspace(parsed);
2480
3957
  const id = requireId(parsed);
2481
- const mgr = new import_devtools_core11.TaskConfigManager();
3958
+ const mgr = new import_devtools_core14.TaskConfigManager();
2482
3959
  const existing = mgr.getTask(id);
2483
3960
  if (!existing) {
2484
3961
  throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
@@ -2523,7 +4000,7 @@ function handleDelete(parsed) {
2523
4000
  requireWorkspace(parsed);
2524
4001
  const id = requireId(parsed);
2525
4002
  requireConfirmation(parsed);
2526
- const mgr = new import_devtools_core11.TaskConfigManager();
4003
+ const mgr = new import_devtools_core14.TaskConfigManager();
2527
4004
  const existing = mgr.getTask(id);
2528
4005
  if (!existing) {
2529
4006
  throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
@@ -2543,7 +4020,7 @@ function handleToggle(parsed) {
2543
4020
  throw createServicemeError("invalid_params", "Missing required flag: --enabled <true|false>");
2544
4021
  }
2545
4022
  const enabled = enabledStr !== "false";
2546
- const mgr = new import_devtools_core11.TaskConfigManager();
4023
+ const mgr = new import_devtools_core14.TaskConfigManager();
2547
4024
  const existing = mgr.getTask(id);
2548
4025
  if (!existing) {
2549
4026
  throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
@@ -2554,22 +4031,22 @@ function handleToggle(parsed) {
2554
4031
  async function handleTrigger(parsed) {
2555
4032
  requireWorkspace(parsed);
2556
4033
  const id = requireId(parsed);
2557
- const mgr = new import_devtools_core11.TaskConfigManager();
4034
+ const mgr = new import_devtools_core14.TaskConfigManager();
2558
4035
  const task = mgr.getTask(id);
2559
4036
  if (!task) {
2560
4037
  throw createServicemeError("task_not_found", `Task '${id}' does not exist in this workspace`);
2561
4038
  }
2562
4039
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
2563
- const executionPayload = (0, import_devtools_core11.resolveTaskExecutionPayload)(
4040
+ const executionPayload = (0, import_devtools_core14.resolveTaskExecutionPayload)(
2564
4041
  task.taskType,
2565
4042
  task.payload,
2566
4043
  task.workspace.path
2567
4044
  );
2568
- (0, import_devtools_core11.validateTaskPayload)(task.taskType, executionPayload);
2569
- const executor = (0, import_devtools_core11.getExecutor)(task.taskType);
4045
+ (0, import_devtools_core14.validateTaskPayload)(task.taskType, executionPayload);
4046
+ const executor = (0, import_devtools_core14.getExecutor)(task.taskType);
2570
4047
  const result = await executor.execute(executionPayload);
2571
4048
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
2572
- const logMgr = new import_devtools_core11.TaskLogManager();
4049
+ const logMgr = new import_devtools_core14.TaskLogManager();
2573
4050
  const log = logMgr.appendLog({
2574
4051
  taskId: task.id,
2575
4052
  taskName: task.name,
@@ -2587,7 +4064,7 @@ function handleLogs(parsed) {
2587
4064
  const limitStr = getStringFlag(parsed, "limit");
2588
4065
  const limit = limitStr ? Math.min(Number(limitStr), 200) : 20;
2589
4066
  const fields = getStringFlag(parsed, "fields");
2590
- const logMgr = new import_devtools_core11.TaskLogManager();
4067
+ const logMgr = new import_devtools_core14.TaskLogManager();
2591
4068
  const { logs, total } = logMgr.getLogs({ taskId, limit });
2592
4069
  const result = logs.map((l) => filterFields(l, fields));
2593
4070
  writeSuccess({ logs: result, total });
@@ -2759,9 +4236,9 @@ function writeDescribe(action) {
2759
4236
 
2760
4237
  // src/commands/scheduler.ts
2761
4238
  var import_node_child_process = require("child_process");
2762
- var fs4 = __toESM(require("fs"));
2763
- var path3 = __toESM(require("path"));
2764
- var import_devtools_core12 = require("@serviceme/devtools-core");
4239
+ var fs6 = __toESM(require("fs"));
4240
+ var path7 = __toESM(require("path"));
4241
+ var import_devtools_core15 = require("@serviceme/devtools-core");
2765
4242
  var REPO_SYNC_INTERVAL_MS = 5 * 60 * 1e3;
2766
4243
  async function runSchedulerCommand(parsed) {
2767
4244
  const action = parsed.positionals[1];
@@ -2799,25 +4276,25 @@ async function runSchedulerCommand(parsed) {
2799
4276
  }
2800
4277
  }
2801
4278
  function listTasks() {
2802
- const mgr = new import_devtools_core12.TaskConfigManager();
4279
+ const mgr = new import_devtools_core15.TaskConfigManager();
2803
4280
  const tasks = mgr.listTasks();
2804
4281
  return [...tasks].sort((a, b) => a.name.localeCompare(b.name));
2805
4282
  }
2806
4283
  function readLogs(options = {}) {
2807
- const mgr = new import_devtools_core12.TaskLogManager();
4284
+ const mgr = new import_devtools_core15.TaskLogManager();
2808
4285
  const limit = options.limit && options.limit > 0 ? Math.min(options.limit, 200) : 50;
2809
4286
  return mgr.getLogs({ taskId: options.taskId, limit });
2810
4287
  }
2811
4288
  function getSchedulerStatus() {
2812
- const pidMgr = new import_devtools_core12.PidManager("", { pidPath: (0, import_devtools_core12.getSchedulerPidPath)() });
2813
- const configMgr = new import_devtools_core12.TaskConfigManager();
4289
+ const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
4290
+ const configMgr = new import_devtools_core15.TaskConfigManager();
2814
4291
  const pid = pidMgr.getRunningPid();
2815
4292
  const config = configMgr.readConfig();
2816
4293
  let uptimeSeconds = null;
2817
4294
  if (pid !== null) {
2818
4295
  try {
2819
- const stat2 = fs4.statSync(pidMgr.getPidPath());
2820
- uptimeSeconds = Math.floor((Date.now() - stat2.mtimeMs) / 1e3);
4296
+ const stat4 = fs6.statSync(pidMgr.getPidPath());
4297
+ uptimeSeconds = Math.floor((Date.now() - stat4.mtimeMs) / 1e3);
2821
4298
  } catch {
2822
4299
  uptimeSeconds = null;
2823
4300
  }
@@ -2834,7 +4311,7 @@ function getSchedulerStatus() {
2834
4311
  };
2835
4312
  }
2836
4313
  function handleStart(_parsed) {
2837
- const pidMgr = new import_devtools_core12.PidManager("", { pidPath: (0, import_devtools_core12.getSchedulerPidPath)() });
4314
+ const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
2838
4315
  const existingPid = pidMgr.getRunningPid();
2839
4316
  if (existingPid !== null) {
2840
4317
  writeSuccess({
@@ -2847,15 +4324,15 @@ function handleStart(_parsed) {
2847
4324
  if (!cliPath) {
2848
4325
  throw createServicemeError("internal_error", "Cannot determine CLI path for daemon spawn");
2849
4326
  }
2850
- const logPath = (0, import_devtools_core12.getSchedulerLogPath)();
2851
- const logDir = path3.dirname(logPath);
2852
- if (!fs4.existsSync(logDir)) {
2853
- fs4.mkdirSync(logDir, { recursive: true });
4327
+ const logPath = (0, import_devtools_core15.getSchedulerLogPath)();
4328
+ const logDir = path7.dirname(logPath);
4329
+ if (!fs6.existsSync(logDir)) {
4330
+ fs6.mkdirSync(logDir, { recursive: true });
2854
4331
  }
2855
4332
  const spawnCmd = process.execPath;
2856
4333
  const spawnArgs = [cliPath, "scheduler", "__daemon", "--logPath", logPath];
2857
4334
  if (process.platform === "win32") {
2858
- fs4.appendFileSync(
4335
+ fs6.appendFileSync(
2859
4336
  logPath,
2860
4337
  `[scheduler:start] spawning daemon via hidden PowerShell Start-Process: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, windowsHide=true
2861
4338
  `
@@ -2865,9 +4342,9 @@ function handleStart(_parsed) {
2865
4342
  writeSuccess({ pid: pid2, status: "started" });
2866
4343
  return;
2867
4344
  }
2868
- const out = fs4.openSync(logPath, "a");
2869
- const err = fs4.openSync(logPath, "a");
2870
- fs4.appendFileSync(
4345
+ const out = fs6.openSync(logPath, "a");
4346
+ const err = fs6.openSync(logPath, "a");
4347
+ fs6.appendFileSync(
2871
4348
  logPath,
2872
4349
  `[scheduler:start] spawning daemon: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, detached=true, windowsHide=true
2873
4350
  `
@@ -2946,7 +4423,7 @@ function handleStop(parsed) {
2946
4423
  );
2947
4424
  }
2948
4425
  }
2949
- const pidMgr = new import_devtools_core12.PidManager("", { pidPath: (0, import_devtools_core12.getSchedulerPidPath)() });
4426
+ const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
2950
4427
  const pid = pidMgr.getRunningPid();
2951
4428
  if (pid === null) {
2952
4429
  throw createServicemeError("daemon_not_running", "Scheduler daemon is not running");
@@ -2988,7 +4465,7 @@ async function runDaemon(parsed) {
2988
4465
  if (logPath) {
2989
4466
  process.env.SERVICEME_SCHEDULER_LOG_PATH = logPath;
2990
4467
  }
2991
- const pidMgr = new import_devtools_core12.PidManager("", { pidPath: (0, import_devtools_core12.getSchedulerPidPath)() });
4468
+ const pidMgr = new import_devtools_core15.PidManager("", { pidPath: (0, import_devtools_core15.getSchedulerPidPath)() });
2992
4469
  const existingPid = pidMgr.getRunningPid();
2993
4470
  if (existingPid !== null && existingPid !== process.pid) {
2994
4471
  process.exit(0);
@@ -3013,11 +4490,11 @@ function startRepoSyncTick() {
3013
4490
  }
3014
4491
  async function runRepoSyncOnce() {
3015
4492
  const proxyBase = process.env.SERVICEME_GIT_PROXY_BASE ?? "http://127.0.0.1:3000/git-proxy";
3016
- const loader = new import_devtools_core12.ReposLoader({ configPath: (0, import_devtools_core12.getReposConfigPath)() });
3017
- const store = new import_devtools_core12.ReposStore({ loader });
4493
+ const loader = new import_devtools_core15.ReposLoader({ configPath: (0, import_devtools_core15.getReposConfigPath)() });
4494
+ const store = new import_devtools_core15.ReposStore({ loader });
3018
4495
  await store.ensureLoaded();
3019
- const gitClient = new import_devtools_core12.GitClient({ serverProxyBase: proxyBase });
3020
- const manager = new import_devtools_core12.RepoManager({ store, gitClient });
4496
+ const gitClient = new import_devtools_core15.GitClient({ serverProxyBase: proxyBase });
4497
+ const manager = new import_devtools_core15.RepoManager({ store, gitClient });
3021
4498
  const report = await manager.pullAll();
3022
4499
  const ok = report.pulls.filter((p) => p.status === "ok").length;
3023
4500
  const err = report.pulls.filter((p) => p.status === "error").length;
@@ -3027,8 +4504,8 @@ async function runRepoSyncOnce() {
3027
4504
  }
3028
4505
  function appendSchedulerLog(message) {
3029
4506
  try {
3030
- const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH ?? (0, import_devtools_core12.getSchedulerLogPath)();
3031
- fs4.appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}
4507
+ const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH ?? (0, import_devtools_core15.getSchedulerLogPath)();
4508
+ fs6.appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}
3032
4509
  `, "utf8");
3033
4510
  } catch {
3034
4511
  }
@@ -3087,10 +4564,10 @@ function writeDescribe2(action) {
3087
4564
  }
3088
4565
 
3089
4566
  // src/commands/skill.ts
3090
- var fs5 = __toESM(require("fs/promises"));
4567
+ var fs7 = __toESM(require("fs/promises"));
3091
4568
  var os2 = __toESM(require("os"));
3092
- var path4 = __toESM(require("path"));
3093
- var import_devtools_core13 = require("@serviceme/devtools-core");
4569
+ var path8 = __toESM(require("path"));
4570
+ var import_devtools_core16 = require("@serviceme/devtools-core");
3094
4571
  function normalizeSkillIdOrThrow(store, remoteId) {
3095
4572
  try {
3096
4573
  return store.normalizeRemoteSkillId(remoteId);
@@ -3104,14 +4581,14 @@ async function runSkillCommand(parsed) {
3104
4581
  if (!workspacePath) {
3105
4582
  throw createServicemeError("invalid_params", "Expected --workspacePath <path>.");
3106
4583
  }
3107
- const store = new import_devtools_core13.SkillStore({
4584
+ const store = new import_devtools_core16.SkillStore({
3108
4585
  workspacePath,
3109
- userSkillsRoot: path4.join(os2.homedir(), ".agents", "skills")
4586
+ userSkillsRoot: path8.join(os2.homedir(), ".copilot", "skills")
3110
4587
  });
3111
- const catalogClient = new import_devtools_core13.SkillCatalogClient({
4588
+ const catalogClient = new import_devtools_core16.SkillCatalogClient({
3112
4589
  baseUrl: getStringFlag(parsed, "baseUrl")
3113
4590
  });
3114
- const reconciler = new import_devtools_core13.SkillReconciler({
4591
+ const reconciler = new import_devtools_core16.SkillReconciler({
3115
4592
  skillStore: store,
3116
4593
  catalogClient
3117
4594
  });
@@ -3220,11 +4697,11 @@ async function handleUninstall2(store, workspacePath, parsed) {
3220
4697
  throw createServicemeError("invalid_params", "Expected --id <remoteSkillId>.");
3221
4698
  }
3222
4699
  const skillId = normalizeSkillIdOrThrow(store, remoteId);
3223
- await fs5.rm(path4.join(workspacePath, store.getWorkspaceSkillPath(skillId)), {
4700
+ await fs7.rm(path8.join(workspacePath, store.getWorkspaceSkillPath(skillId)), {
3224
4701
  recursive: true,
3225
4702
  force: true
3226
4703
  });
3227
- await fs5.rm(store.getUserSkillPath(skillId), {
4704
+ await fs7.rm(store.getUserSkillPath(skillId), {
3228
4705
  recursive: true,
3229
4706
  force: true
3230
4707
  });
@@ -3246,7 +4723,7 @@ async function handleMove2(store, workspacePath, parsed) {
3246
4723
  throw createServicemeError("invalid_params", "Expected --to value to be workspace or user.");
3247
4724
  }
3248
4725
  const skillId = normalizeSkillIdOrThrow(store, remoteId);
3249
- const workspacePathForSkill = path4.join(workspacePath, store.getWorkspaceSkillPath(skillId));
4726
+ const workspacePathForSkill = path8.join(workspacePath, store.getWorkspaceSkillPath(skillId));
3250
4727
  const userPathForSkill = store.getUserSkillPath(skillId);
3251
4728
  if (to === "user") {
3252
4729
  await moveDirectory(workspacePathForSkill, userPathForSkill);
@@ -3267,12 +4744,12 @@ async function handleMove2(store, workspacePath, parsed) {
3267
4744
  };
3268
4745
  }
3269
4746
  async function moveDirectory(fromPath, toPath) {
3270
- await fs5.mkdir(path4.dirname(toPath), { recursive: true });
4747
+ await fs7.mkdir(path8.dirname(toPath), { recursive: true });
3271
4748
  try {
3272
- await fs5.rename(fromPath, toPath);
4749
+ await fs7.rename(fromPath, toPath);
3273
4750
  } catch {
3274
- await fs5.cp(fromPath, toPath, { recursive: true });
3275
- await fs5.rm(fromPath, { recursive: true, force: true });
4751
+ await fs7.cp(fromPath, toPath, { recursive: true });
4752
+ await fs7.rm(fromPath, { recursive: true, force: true });
3276
4753
  }
3277
4754
  }
3278
4755
  async function handleMarketplace2(store, catalogClient) {
@@ -3373,15 +4850,15 @@ async function handlePublishable(store, workspacePath) {
3373
4850
  skills: workspaceSkillIds.map((skillId) => ({
3374
4851
  id: skillId,
3375
4852
  displayName: skillId,
3376
- path: path4.join(workspacePath, store.getWorkspaceSkillPath(skillId))
4853
+ path: path8.join(workspacePath, store.getWorkspaceSkillPath(skillId))
3377
4854
  }))
3378
4855
  };
3379
4856
  }
3380
4857
 
3381
4858
  // src/commands/skills.ts
3382
- var fs6 = __toESM(require("fs/promises"));
3383
- var path5 = __toESM(require("path"));
3384
- var import_devtools_core14 = require("@serviceme/devtools-core");
4859
+ var fs8 = __toESM(require("fs/promises"));
4860
+ var path9 = __toESM(require("path"));
4861
+ var import_devtools_core17 = require("@serviceme/devtools-core");
3385
4862
  function requireFlag(parsed, name) {
3386
4863
  const value = getStringFlag(parsed, name);
3387
4864
  if (!value) {
@@ -3422,13 +4899,13 @@ function parseMode(raw) {
3422
4899
  }
3423
4900
  async function newSkillRepoHandler(parsed) {
3424
4901
  const proxyBase = getStringFlag(parsed, "git-proxy-base") ?? "http://127.0.0.1:3000/git-proxy";
3425
- const reposStore = new import_devtools_core14.ReposStore({
3426
- loader: new import_devtools_core14.ReposLoader({ configPath: (0, import_devtools_core14.getReposConfigPath)() })
4902
+ const reposStore = new import_devtools_core17.ReposStore({
4903
+ loader: new import_devtools_core17.ReposLoader({ configPath: (0, import_devtools_core17.getReposConfigPath)() })
3427
4904
  });
3428
4905
  await reposStore.ensureLoaded();
3429
- const gitClient = new import_devtools_core14.GitClient({ serverProxyBase: proxyBase });
3430
- const draftsStore = new import_devtools_core14.DraftsStore();
3431
- const submitClient = new import_devtools_core14.SubmitClient({ gitClient });
4906
+ const gitClient = new import_devtools_core17.GitClient({ serverProxyBase: proxyBase });
4907
+ const draftsStore = new import_devtools_core17.DraftsStore();
4908
+ const submitClient = new import_devtools_core17.SubmitClient({ gitClient });
3432
4909
  return new SkillRepoBridgeHandler({
3433
4910
  reposStore,
3434
4911
  gitClient,
@@ -3437,9 +4914,9 @@ async function newSkillRepoHandler(parsed) {
3437
4914
  });
3438
4915
  }
3439
4916
  async function readDraftFilesFromDir(dirPath) {
3440
- let stat2;
4917
+ let stat4;
3441
4918
  try {
3442
- stat2 = await fs6.stat(dirPath);
4919
+ stat4 = await fs8.stat(dirPath);
3443
4920
  } catch (err) {
3444
4921
  const code = err.code;
3445
4922
  throw createServicemeError(
@@ -3447,17 +4924,17 @@ async function readDraftFilesFromDir(dirPath) {
3447
4924
  `--dir '${dirPath}' is not readable${code === "ENOENT" ? " (path does not exist)" : `: ${err.message}`}`
3448
4925
  );
3449
4926
  }
3450
- if (!stat2.isDirectory()) {
4927
+ if (!stat4.isDirectory()) {
3451
4928
  throw createServicemeError("invalid_params", `--dir '${dirPath}' is not a directory`);
3452
4929
  }
3453
4930
  const files = [];
3454
4931
  let manifestFound = false;
3455
4932
  async function walk(currentAbs, currentRel) {
3456
- const entries = await fs6.readdir(currentAbs, { withFileTypes: true });
4933
+ const entries = await fs8.readdir(currentAbs, { withFileTypes: true });
3457
4934
  for (const entry of entries) {
3458
4935
  if (entry.name.startsWith(".")) continue;
3459
4936
  if (entry.name === "node_modules") continue;
3460
- const childAbs = path5.join(currentAbs, entry.name);
4937
+ const childAbs = path9.join(currentAbs, entry.name);
3461
4938
  const childRel = currentRel ? `${currentRel}/${entry.name}` : entry.name;
3462
4939
  if (entry.isSymbolicLink()) continue;
3463
4940
  if (entry.isDirectory()) {
@@ -3465,7 +4942,7 @@ async function readDraftFilesFromDir(dirPath) {
3465
4942
  continue;
3466
4943
  }
3467
4944
  if (!entry.isFile()) continue;
3468
- const content = await fs6.readFile(childAbs, "utf8");
4945
+ const content = await fs8.readFile(childAbs, "utf8");
3469
4946
  if (childRel === "SKILL.md" || childRel === "AGENT.md") {
3470
4947
  manifestFound = true;
3471
4948
  }