@p4code/cli 0.4.11 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -240,7 +240,7 @@ const make$93 = () => {
240
240
  const layer$82 = Layer.sync(NetService, make$93);
241
241
  //#endregion
242
242
  //#region package.json
243
- var version = "0.4.11";
243
+ var version = "0.4.13";
244
244
  //#endregion
245
245
  //#region src/config.ts
246
246
  /**
@@ -23553,8 +23553,8 @@ const removeMemoryFile = Effect.fn("memoryFile.removeMemoryFile")(function* (inp
23553
23553
  * config directory — the one holding `.credentials.json` — instead of a managed
23554
23554
  * home the user would have to log in to again. See `docs/architecture/hub.md`.
23555
23555
  *
23556
- * Keyed by skills directory so a second provider home is additive rather than a
23557
- * migration.
23556
+ * Keyed by skills directory so ownership can move safely when the configured
23557
+ * provider path migrates to the provider-neutral shared root.
23558
23558
  *
23559
23559
  * @module sync/skillManifest
23560
23560
  */
@@ -23596,6 +23596,20 @@ const withEntriesFor = (manifest, skillsRoot, entries) => ({
23596
23596
  [skillsRoot]: [...entries].sort((left, right) => left.name.localeCompare(right.name))
23597
23597
  }
23598
23598
  });
23599
+ const moveEntriesFor = (manifest, sourceRoot, targetRoot) => {
23600
+ if (sourceRoot === targetRoot || manifest.directories[sourceRoot] === void 0) return manifest;
23601
+ const { [sourceRoot]: sourceEntries, ...directories } = manifest.directories;
23602
+ const targetEntries = directories[targetRoot] ?? [];
23603
+ const merged = new Map(targetEntries.map((entry) => [entry.name, entry]));
23604
+ for (const entry of sourceEntries ?? []) merged.set(entry.name, entry);
23605
+ return {
23606
+ version: 1,
23607
+ directories: {
23608
+ ...directories,
23609
+ [targetRoot]: [...merged.values()].sort((left, right) => left.name.localeCompare(right.name))
23610
+ }
23611
+ };
23612
+ };
23599
23613
  const writeSkillManifest = Effect.fn("skillManifest.write")(function* (manifestPath, manifest) {
23600
23614
  yield* writeFileStringAtomically({
23601
23615
  filePath: manifestPath,
@@ -23804,6 +23818,170 @@ function planSkillSync(input) {
23804
23818
  };
23805
23819
  }
23806
23820
  //#endregion
23821
+ //#region src/sync/sharedSkillRoot.ts
23822
+ const SHARED_SKILL_ROOT_REFUSAL = "shared-root-migration-blocked";
23823
+ const inspectLayout = Effect.fn("sharedSkillRoot.inspectLayout")(function* (input) {
23824
+ const fileSystem = yield* FileSystem.FileSystem;
23825
+ const path = yield* Path.Path;
23826
+ const targetRoot = path.resolve(input.sharedSkillsRoot ?? path.join(NodeOS.homedir(), ".agents", "skills"));
23827
+ const claudeRoot = path.resolve(input.claudeSkillsRoot);
23828
+ const manifest = yield* readSkillManifest(input.manifestPath);
23829
+ if (claudeRoot === targetRoot) return {
23830
+ claudeRoot,
23831
+ targetRoot,
23832
+ manifest,
23833
+ entries: [],
23834
+ candidateSkillNames: [],
23835
+ ignoredEntries: [],
23836
+ refused: [],
23837
+ linked: true,
23838
+ exists: yield* fileSystem.exists(targetRoot)
23839
+ };
23840
+ const linkedTarget = yield* fileSystem.readLink(claudeRoot).pipe(Effect.map((target) => path.resolve(path.dirname(claudeRoot), target)), Effect.option);
23841
+ if (linkedTarget._tag === "Some") return {
23842
+ claudeRoot,
23843
+ targetRoot,
23844
+ manifest,
23845
+ entries: [],
23846
+ candidateSkillNames: [],
23847
+ ignoredEntries: [],
23848
+ refused: linkedTarget.value === targetRoot ? [] : [{
23849
+ name: claudeRoot,
23850
+ reason: SHARED_SKILL_ROOT_REFUSAL
23851
+ }],
23852
+ linked: linkedTarget.value === targetRoot,
23853
+ exists: true
23854
+ };
23855
+ const exists = yield* fileSystem.exists(claudeRoot);
23856
+ if (exists) {
23857
+ const sourceDevice = (yield* fileSystem.stat(claudeRoot)).dev;
23858
+ let targetProbe = targetRoot;
23859
+ while (!(yield* fileSystem.exists(targetProbe))) {
23860
+ const parent = path.dirname(targetProbe);
23861
+ if (parent === targetProbe) break;
23862
+ targetProbe = parent;
23863
+ }
23864
+ if (sourceDevice !== (yield* fileSystem.stat(targetProbe)).dev) return {
23865
+ claudeRoot,
23866
+ targetRoot,
23867
+ manifest,
23868
+ entries: [],
23869
+ candidateSkillNames: [],
23870
+ ignoredEntries: [],
23871
+ refused: [{
23872
+ name: claudeRoot,
23873
+ reason: SHARED_SKILL_ROOT_REFUSAL
23874
+ }],
23875
+ linked: false,
23876
+ exists
23877
+ };
23878
+ }
23879
+ const entries = exists ? yield* fileSystem.readDirectory(claudeRoot) : [];
23880
+ const managedNames = new Set(entriesFor(manifest, claudeRoot).map((entry) => entry.name));
23881
+ const candidateSkillNames = [];
23882
+ const ignoredEntries = [];
23883
+ const strayEntries = [];
23884
+ for (const entry of entries) {
23885
+ if (isIgnoredEntry(entry)) {
23886
+ ignoredEntries.push(entry);
23887
+ continue;
23888
+ }
23889
+ const entryPath = path.join(claudeRoot, entry);
23890
+ if ((yield* fileSystem.stat(entryPath)).type === "Directory" && (yield* fileSystem.exists(path.join(entryPath, "SKILL.md")))) candidateSkillNames.push(entry);
23891
+ else strayEntries.push(entry);
23892
+ }
23893
+ return {
23894
+ claudeRoot,
23895
+ targetRoot,
23896
+ manifest,
23897
+ entries,
23898
+ candidateSkillNames,
23899
+ ignoredEntries,
23900
+ refused: [...candidateSkillNames.filter((entry) => !managedNames.has(entry)), ...strayEntries].map((name) => ({
23901
+ name,
23902
+ reason: SHARED_SKILL_ROOT_REFUSAL
23903
+ })),
23904
+ linked: false,
23905
+ exists
23906
+ };
23907
+ });
23908
+ /** Read-only root resolution used by status and previews. */
23909
+ const inspectSharedSkillRoot = Effect.fn("sharedSkillRoot.inspect")(function* (input) {
23910
+ const layout = yield* inspectLayout(input);
23911
+ if (layout.refused.length > 0) return {
23912
+ root: null,
23913
+ refused: layout.refused
23914
+ };
23915
+ return {
23916
+ root: layout.linked || !layout.exists ? layout.targetRoot : layout.claudeRoot,
23917
+ refused: []
23918
+ };
23919
+ });
23920
+ /**
23921
+ * Make Claude's configured skill path an alias of the provider-neutral user
23922
+ * skill root. Existing unmanaged content is never moved or replaced.
23923
+ */
23924
+ const prepareSharedSkillRoot = Effect.fn("sharedSkillRoot.prepare")(function* (input) {
23925
+ const fileSystem = yield* FileSystem.FileSystem;
23926
+ const path = yield* Path.Path;
23927
+ const layout = yield* inspectLayout(input);
23928
+ const { candidateSkillNames, claudeRoot, ignoredEntries, manifest, targetRoot } = layout;
23929
+ if (layout.refused.length > 0) return {
23930
+ root: null,
23931
+ refused: layout.refused
23932
+ };
23933
+ if (layout.linked) {
23934
+ yield* fileSystem.makeDirectory(targetRoot, { recursive: true });
23935
+ const migrated = moveEntriesFor(manifest, claudeRoot, targetRoot);
23936
+ if (migrated !== manifest) yield* writeSkillManifest(input.manifestPath, migrated);
23937
+ return {
23938
+ root: targetRoot,
23939
+ refused: []
23940
+ };
23941
+ }
23942
+ if (layout.exists) if (!(yield* fileSystem.exists(targetRoot))) {
23943
+ yield* fileSystem.makeDirectory(path.dirname(targetRoot), { recursive: true });
23944
+ yield* fileSystem.rename(claudeRoot, targetRoot);
23945
+ } else {
23946
+ const backupRoot = `${claudeRoot}.p4code-migrated`;
23947
+ if (ignoredEntries.length > 0 && (yield* fileSystem.exists(backupRoot))) return {
23948
+ root: null,
23949
+ refused: [{
23950
+ name: backupRoot,
23951
+ reason: SHARED_SKILL_ROOT_REFUSAL
23952
+ }]
23953
+ };
23954
+ const targetEntries = new Set(yield* fileSystem.readDirectory(targetRoot));
23955
+ const collisions = candidateSkillNames.filter((entry) => targetEntries.has(entry));
23956
+ if (collisions.length > 0) return {
23957
+ root: null,
23958
+ refused: collisions.map((name) => ({
23959
+ name,
23960
+ reason: SHARED_SKILL_ROOT_REFUSAL
23961
+ }))
23962
+ };
23963
+ for (const entry of candidateSkillNames) yield* fileSystem.rename(path.join(claudeRoot, entry), path.join(targetRoot, entry));
23964
+ }
23965
+ else {
23966
+ yield* fileSystem.makeDirectory(targetRoot, { recursive: true });
23967
+ yield* fileSystem.makeDirectory(path.dirname(claudeRoot), { recursive: true });
23968
+ }
23969
+ const stagedLink = `${claudeRoot}.p4code-link`;
23970
+ yield* fileSystem.remove(stagedLink, { force: true });
23971
+ yield* fileSystem.symlink(targetRoot, stagedLink);
23972
+ if (layout.exists && (yield* fileSystem.exists(claudeRoot))) if (ignoredEntries.length > 0) {
23973
+ const backupRoot = `${claudeRoot}.p4code-migrated`;
23974
+ yield* fileSystem.rename(claudeRoot, backupRoot);
23975
+ } else yield* fileSystem.remove(claudeRoot, { recursive: true });
23976
+ yield* fileSystem.rename(stagedLink, claudeRoot);
23977
+ const migrated = moveEntriesFor(manifest, claudeRoot, targetRoot);
23978
+ if (migrated !== manifest) yield* writeSkillManifest(input.manifestPath, migrated);
23979
+ return {
23980
+ root: targetRoot,
23981
+ refused: []
23982
+ };
23983
+ });
23984
+ //#endregion
23807
23985
  //#region src/sync/AssetSync.ts
23808
23986
  /**
23809
23987
  * Syncing user-scope agent assets between this machine and the hub.
@@ -23864,10 +24042,30 @@ const make$77 = Effect.gen(function* () {
23864
24042
  const claudeHome = Effect.fn("AssetSync.claudeHome")(function* () {
23865
24043
  return { homePath: (yield* settingsStore.getSettings.pipe(Effect.orElseSucceed(() => void 0)))?.providers.claudeAgent.homePath ?? "" };
23866
24044
  });
24045
+ const inspectSkillRoot = Effect.fn("AssetSync.inspectSkillRoot")(function* () {
24046
+ const claudeSkillsRoot = yield* claudeHome().pipe(Effect.flatMap(resolveClaudeUserSkillsDir));
24047
+ return yield* inspectSharedSkillRoot({
24048
+ claudeSkillsRoot,
24049
+ manifestPath
24050
+ }).pipe(Effect.catchCause((cause) => Effect.logWarning("shared skill root inspection failed", { cause }).pipe(Effect.as({
24051
+ root: null,
24052
+ refused: [{
24053
+ name: claudeSkillsRoot,
24054
+ reason: SHARED_SKILL_ROOT_REFUSAL
24055
+ }]
24056
+ }))));
24057
+ });
23867
24058
  const drivers = [
23868
24059
  {
23869
24060
  kind: "skill",
23870
- resolveRoot: claudeHome().pipe(Effect.flatMap(resolveClaudeUserSkillsDir)),
24061
+ prepareRoot: Effect.gen(function* () {
24062
+ yield* prepareSharedSkillRoot({
24063
+ claudeSkillsRoot: yield* claudeHome().pipe(Effect.flatMap(resolveClaudeUserSkillsDir)),
24064
+ manifestPath
24065
+ });
24066
+ }).pipe(Effect.catchCause((cause) => Effect.logWarning("shared skill root migration failed", { cause }))),
24067
+ resolveState: inspectSkillRoot(),
24068
+ resolveRoot: inspectSkillRoot().pipe(Effect.map((result) => result.root)),
23871
24069
  read: (root) => readSkillDirectory(root),
23872
24070
  write: ({ root, name, files }) => writeSkillDirectory({
23873
24071
  skillsRoot: root,
@@ -23925,6 +24123,19 @@ const make$77 = Effect.gen(function* () {
23925
24123
  }
23926
24124
  ];
23927
24125
  const driverFor = (kind) => drivers.find((driver) => driver.kind === kind);
24126
+ const resolveDriverState = Effect.fn("AssetSync.resolveDriverState")(function* (driver) {
24127
+ if (driver.resolveState) return yield* driver.resolveState;
24128
+ return {
24129
+ root: yield* driver.resolveRoot,
24130
+ refused: []
24131
+ };
24132
+ });
24133
+ const resolveAllDriverStates = Effect.fn("AssetSync.resolveAllDriverStates")(function* () {
24134
+ return yield* Effect.forEach(drivers, (driver) => resolveDriverState(driver).pipe(Effect.map((state) => ({
24135
+ driver,
24136
+ ...state
24137
+ }))), { concurrency: "unbounded" });
24138
+ });
23928
24139
  const isConfigured = link.current.pipe(Effect.map((state) => Option.isSome(state.settings)));
23929
24140
  /**
23930
24141
  * The three views for one kind, gathered once.
@@ -23932,14 +24143,15 @@ const make$77 = Effect.gen(function* () {
23932
24143
  * `status` and `run` both need exactly this and disagreeing about it is how a
23933
24144
  * panel comes to show something the next sync does not do.
23934
24145
  */
23935
- const viewsFor = Effect.fn("AssetSync.viewsFor")(function* (driver, hubAssets) {
23936
- const root = yield* driver.resolveRoot;
24146
+ const viewsFor = Effect.fn("AssetSync.viewsFor")(function* (driver, hubAssets, resolved) {
24147
+ const state = resolved ?? (yield* resolveDriverState(driver));
24148
+ const root = state.root;
23937
24149
  const hub = hubAssets.filter((asset) => asset.kind === driver.kind);
23938
24150
  if (root === null) return {
23939
24151
  root,
23940
24152
  hub,
23941
24153
  local: [],
23942
- refused: [],
24154
+ refused: state.refused,
23943
24155
  managed: []
23944
24156
  };
23945
24157
  const local = yield* driver.read(root);
@@ -23952,7 +24164,11 @@ const make$77 = Effect.gen(function* () {
23952
24164
  managed: entriesFor(manifest, root)
23953
24165
  };
23954
24166
  });
23955
- const rootsOf = Effect.fn("AssetSync.rootsOf")(function* () {
24167
+ const rootsOf = Effect.fn("AssetSync.rootsOf")(function* (states) {
24168
+ if (states) return states.map(({ driver, root }) => ({
24169
+ kind: driver.kind,
24170
+ path: root
24171
+ }));
23956
24172
  const roots = [];
23957
24173
  for (const driver of drivers) roots.push({
23958
24174
  kind: driver.kind,
@@ -23962,7 +24178,9 @@ const make$77 = Effect.gen(function* () {
23962
24178
  });
23963
24179
  const run = Effect.gen(function* () {
23964
24180
  if (!(yield* isConfigured)) return EMPTY_REPORT;
23965
- const roots = yield* rootsOf();
24181
+ for (const driver of drivers) if (driver.prepareRoot) yield* driver.prepareRoot;
24182
+ const resolvedDrivers = yield* resolveAllDriverStates();
24183
+ const roots = yield* rootsOf(resolvedDrivers);
23966
24184
  const hubAssets = yield* client.list().pipe(Effect.option);
23967
24185
  if (Option.isNone(hubAssets)) return {
23968
24186
  ...EMPTY_REPORT,
@@ -23977,8 +24195,9 @@ const make$77 = Effect.gen(function* () {
23977
24195
  const conflicts = [];
23978
24196
  const refused = [];
23979
24197
  const shareMode = (yield* link.current).shareMode;
23980
- for (const driver of drivers) {
23981
- const views = yield* viewsFor(driver, hubAssets.value);
24198
+ for (const resolved of resolvedDrivers) {
24199
+ const driver = resolved.driver;
24200
+ const views = yield* viewsFor(driver, hubAssets.value, resolved);
23982
24201
  const kind = driver.kind;
23983
24202
  for (const entry of views.refused) refused.push({
23984
24203
  kind,
@@ -24128,7 +24347,8 @@ const make$77 = Effect.gen(function* () {
24128
24347
  */
24129
24348
  const assetStatus = Effect.gen(function* () {
24130
24349
  const state = yield* link.current;
24131
- const roots = yield* rootsOf();
24350
+ const resolvedDrivers = yield* resolveAllDriverStates();
24351
+ const roots = yield* rootsOf(resolvedDrivers);
24132
24352
  const base = {
24133
24353
  syncMode: state.syncMode,
24134
24354
  shareMode: state.shareMode,
@@ -24138,9 +24358,10 @@ const make$77 = Effect.gen(function* () {
24138
24358
  const hubAssets = Option.isNone(state.settings) ? Option.none() : yield* client.list().pipe(Effect.option);
24139
24359
  const entries = [];
24140
24360
  const refused = [];
24141
- for (const driver of drivers) {
24361
+ for (const resolved of resolvedDrivers) {
24362
+ const driver = resolved.driver;
24142
24363
  const kind = driver.kind;
24143
- const views = yield* viewsFor(driver, Option.getOrElse(hubAssets, () => []));
24364
+ const views = yield* viewsFor(driver, Option.getOrElse(hubAssets, () => []), resolved);
24144
24365
  for (const entry of views.refused) refused.push({
24145
24366
  kind,
24146
24367
  name: entry.name,
@@ -24468,6 +24689,7 @@ const make$77 = Effect.gen(function* () {
24468
24689
  path: null,
24469
24690
  detail: `Unknown asset kind '${input.kind}'.`
24470
24691
  };
24692
+ if (driver.prepareRoot) yield* driver.prepareRoot;
24471
24693
  const root = yield* driver.resolveRoot;
24472
24694
  if (root === null) return {
24473
24695
  outcome: "no-root",
@@ -28644,7 +28866,7 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
28644
28866
  updatedAt: engagement.occurredAt
28645
28867
  }
28646
28868
  });
28647
- return events.map((event) => {
28869
+ const enrichedEvents = events.map((event) => {
28648
28870
  if (event.type === "thread.created") return {
28649
28871
  ...event,
28650
28872
  payload: {
@@ -28687,6 +28909,7 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
28687
28909
  default: return event;
28688
28910
  }
28689
28911
  });
28912
+ return enrichedEvents.length === 1 ? enrichedEvents[0] : enrichedEvents;
28690
28913
  });
28691
28914
  //#endregion
28692
28915
  //#region src/orchestration/Services/ProjectionPipeline.ts
@@ -32546,8 +32769,11 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32546
32769
  settled_at AS "settledAt",
32547
32770
  lifecycle AS "lifecycle",
32548
32771
  lifecycle_reason AS "lifecycleReason",
32549
- lifecycle_changed_at AS "lifecycleChangedAt",
32550
- last_engaged_at AS "lastEngagedAt",
32772
+ COALESCE(lifecycle_changed_at, archived_at, settled_at, created_at) AS "lifecycleChangedAt",
32773
+ COALESCE(last_engaged_at, MAX(created_at, COALESCE(latest_user_message_at, created_at),
32774
+ COALESCE((SELECT MAX(message.updated_at) FROM projection_thread_messages message
32775
+ WHERE message.thread_id = projection_threads.thread_id
32776
+ AND message.role IN ('user', 'assistant')), created_at))) AS "lastEngagedAt",
32551
32777
  snoozed_until AS "snoozedUntil",
32552
32778
  snoozed_at AS "snoozedAt",
32553
32779
  pinned_at AS "pinnedAt",
@@ -32587,8 +32813,11 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32587
32813
  settled_at AS "settledAt",
32588
32814
  lifecycle AS "lifecycle",
32589
32815
  lifecycle_reason AS "lifecycleReason",
32590
- lifecycle_changed_at AS "lifecycleChangedAt",
32591
- last_engaged_at AS "lastEngagedAt",
32816
+ COALESCE(lifecycle_changed_at, archived_at, settled_at, created_at) AS "lifecycleChangedAt",
32817
+ COALESCE(last_engaged_at, MAX(created_at, COALESCE(latest_user_message_at, created_at),
32818
+ COALESCE((SELECT MAX(message.updated_at) FROM projection_thread_messages message
32819
+ WHERE message.thread_id = projection_threads.thread_id
32820
+ AND message.role IN ('user', 'assistant')), created_at))) AS "lastEngagedAt",
32592
32821
  snoozed_until AS "snoozedUntil",
32593
32822
  snoozed_at AS "snoozedAt",
32594
32823
  pinned_at AS "pinnedAt",
@@ -32630,8 +32859,11 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
32630
32859
  settled_at AS "settledAt",
32631
32860
  lifecycle AS "lifecycle",
32632
32861
  lifecycle_reason AS "lifecycleReason",
32633
- lifecycle_changed_at AS "lifecycleChangedAt",
32634
- last_engaged_at AS "lastEngagedAt",
32862
+ COALESCE(lifecycle_changed_at, archived_at, settled_at, created_at) AS "lifecycleChangedAt",
32863
+ COALESCE(last_engaged_at, MAX(created_at, COALESCE(latest_user_message_at, created_at),
32864
+ COALESCE((SELECT MAX(message.updated_at) FROM projection_thread_messages message
32865
+ WHERE message.thread_id = projection_threads.thread_id
32866
+ AND message.role IN ('user', 'assistant')), created_at))) AS "lastEngagedAt",
32635
32867
  snoozed_until AS "snoozedUntil",
32636
32868
  snoozed_at AS "snoozedAt",
32637
32869
  pinned_at AS "pinnedAt",
@@ -33098,8 +33330,11 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
33098
33330
  settled_at AS "settledAt",
33099
33331
  lifecycle AS "lifecycle",
33100
33332
  lifecycle_reason AS "lifecycleReason",
33101
- lifecycle_changed_at AS "lifecycleChangedAt",
33102
- last_engaged_at AS "lastEngagedAt",
33333
+ COALESCE(lifecycle_changed_at, archived_at, settled_at, created_at) AS "lifecycleChangedAt",
33334
+ COALESCE(last_engaged_at, MAX(created_at, COALESCE(latest_user_message_at, created_at),
33335
+ COALESCE((SELECT MAX(message.updated_at) FROM projection_thread_messages message
33336
+ WHERE message.thread_id = projection_threads.thread_id
33337
+ AND message.role IN ('user', 'assistant')), created_at))) AS "lastEngagedAt",
33103
33338
  snoozed_until AS "snoozedUntil",
33104
33339
  snoozed_at AS "snoozedAt",
33105
33340
  pinned_at AS "pinnedAt",
@@ -99965,7 +100200,7 @@ const CURSOR_PRESENTATION = {
99965
100200
  badgeLabel: "Early Access",
99966
100201
  showInteractionModeToggle: true
99967
100202
  };
99968
- const EMPTY_CAPABILITIES$2 = createModelCapabilities({ optionDescriptors: [] });
100203
+ const EMPTY_CAPABILITIES$1 = createModelCapabilities({ optionDescriptors: [] });
99969
100204
  const CURSOR_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
99970
100205
  const CURSOR_PARAMETERIZED_MODEL_PICKER_MIN_VERSION_DATE = 20260408;
99971
100206
  const CURSOR_CLI_INSTALLATION_DOCS_URL = "https://cursor.com/docs/cli/installation";
@@ -100073,7 +100308,7 @@ function getBooleanCurrentValue(option) {
100073
100308
  if (normalized === "false") return false;
100074
100309
  }
100075
100310
  function buildCursorCapabilitiesFromConfigOptions(configOptions) {
100076
- if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$2;
100311
+ if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$1;
100077
100312
  const reasoningConfig = findCursorEffortConfigOption(configOptions);
100078
100313
  const reasoningEffortLevels = reasoningConfig?.type === "select" ? flattenSessionConfigSelectOptions(reasoningConfig).flatMap((entry) => {
100079
100314
  const normalizedValue = normalizeCursorReasoningValue(entry.value);
@@ -100242,7 +100477,7 @@ const discoverCursorModelsViaListAvailableModels = (cursorSettings, environment)
100242
100477
  }), environment);
100243
100478
  const discoverCursorModelsViaAcp = (cursorSettings, environment) => discoverCursorModelsViaListAvailableModels(cursorSettings, environment);
100244
100479
  function getCursorFallbackModels(cursorSettings) {
100245
- return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$2);
100480
+ return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$1);
100246
100481
  }
100247
100482
  /** Timeout for `agent about` — it's slower than a simple `--version` probe. */
100248
100483
  const ABOUT_TIMEOUT_MS = 8e3;
@@ -100278,7 +100513,7 @@ function buildCursorProviderSnapshot(input) {
100278
100513
  presentation: CURSOR_PRESENTATION,
100279
100514
  enabled: input.cursorSettings.enabled,
100280
100515
  checkedAt: input.checkedAt,
100281
- models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$2),
100516
+ models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$1),
100282
100517
  probe: {
100283
100518
  installed: true,
100284
100519
  version: input.parsed.version,
@@ -103111,14 +103346,14 @@ const GROK_PRESENTATION = {
103111
103346
  showInteractionModeToggle: false,
103112
103347
  requiresNewThreadForModelChange: true
103113
103348
  };
103114
- const EMPTY_CAPABILITIES$1 = createModelCapabilities({ optionDescriptors: [] });
103349
+ const EMPTY_CAPABILITIES = createModelCapabilities({ optionDescriptors: [] });
103115
103350
  const VERSION_PROBE_TIMEOUT_MS$1 = 4e3;
103116
103351
  const GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
103117
103352
  const GROK_BUILT_IN_MODELS = [{
103118
103353
  slug: "grok-build",
103119
103354
  name: "Grok Build",
103120
103355
  isCustom: false,
103121
- capabilities: EMPTY_CAPABILITIES$1
103356
+ capabilities: EMPTY_CAPABILITIES
103122
103357
  }];
103123
103358
  function buildInitialGrokProviderSnapshot(grokSettings) {
103124
103359
  return Effect.gen(function* () {
@@ -103153,7 +103388,7 @@ function buildInitialGrokProviderSnapshot(grokSettings) {
103153
103388
  });
103154
103389
  }
103155
103390
  function grokModelsFromSettings(customModels, builtInModels = GROK_BUILT_IN_MODELS) {
103156
- return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES$1);
103391
+ return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);
103157
103392
  }
103158
103393
  function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
103159
103394
  if (!modelState || modelState.availableModels.length === 0) return [];
@@ -103166,7 +103401,7 @@ function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
103166
103401
  slug,
103167
103402
  name: model.name.trim() || slug,
103168
103403
  isCustom: false,
103169
- capabilities: EMPTY_CAPABILITIES$1
103404
+ capabilities: EMPTY_CAPABILITIES
103170
103405
  };
103171
103406
  }).filter((model) => model !== void 0);
103172
103407
  }
@@ -103406,20 +103641,52 @@ const GrokDriver = {
103406
103641
  })
103407
103642
  };
103408
103643
  /**
103644
+ * Reasoning levels `muse exec --reasoning-effort` accepts, in the CLI's own
103645
+ * order (`muse exec --help`, Muse Code 1.1.1). `high` is the CLI default, so
103646
+ * it is the default here too.
103647
+ */
103648
+ const MUSE_REASONING_EFFORTS = [
103649
+ "none",
103650
+ "minimal",
103651
+ "low",
103652
+ "medium",
103653
+ "high",
103654
+ "xhigh",
103655
+ "max",
103656
+ "ultra"
103657
+ ];
103658
+ const MUSE_DEFAULT_REASONING_EFFORT = "high";
103659
+ /**
103660
+ * Keep a selection Muse cannot parse out of the argv. `reasoningEffort` is a
103661
+ * shared option id — Codex publishes one too — so a selection left over from
103662
+ * another provider can reach this driver, and an unknown level makes
103663
+ * `muse exec` reject the whole invocation.
103664
+ */
103665
+ function museReasoningEffort(value) {
103666
+ return value !== void 0 && MUSE_REASONING_EFFORTS.includes(value) ? value : void 0;
103667
+ }
103668
+ /**
103409
103669
  * Build the argument vector for one turn.
103410
103670
  *
103411
- * Approval policy is the load-bearing decision here. A headless Muse run has
103412
- * no channel to answer an approval prompt the CLI only offers
103413
- * `--disable-approval` (keep the OS sandbox) and `--yolo` (drop approval and
103414
- * the sandbox, and trust the workspace). P4Code owns the user-facing runtime
103415
- * mode, so it maps onto those two postures:
103671
+ * Approval policy is the load-bearing decision here. Muse Code 1.1.1 does
103672
+ * express approval as a policy`--approval-mode untrusted|on-request|never`,
103673
+ * plus named `--permission-profile` documents but a headless `exec` run
103674
+ * still has no channel to answer a prompt: approvals are served over MSP
103675
+ * (`muse serve`), not on the `exec` stream. So the only two postures `exec`
103676
+ * can honour end to end are `--disable-approval` (keep the OS sandbox) and
103677
+ * `--yolo` (drop approval and the sandbox, and trust the workspace). P4Code
103678
+ * owns the user-facing runtime mode, so it maps onto those two:
103416
103679
  *
103417
103680
  * - `full-access` → `--yolo`
103418
103681
  * - everything else → `--disable-approval --trust-workspace`, leaving the
103419
103682
  * sandbox to contain what runs.
103420
103683
  *
103421
- * `approval-required` cannot be honoured; the adapter emits a
103422
- * `config.warning` for it rather than silently pretending otherwise.
103684
+ * `approval-required` cannot be honoured over `exec`; the adapter emits a
103685
+ * `config.warning` for it rather than silently pretending otherwise. Serving
103686
+ * a real approval prompt means moving the driver onto MSP.
103687
+ *
103688
+ * Flags here were verified against Muse Code 1.1.1-R2514.1 (`muse exec
103689
+ * --help`).
103423
103690
  */
103424
103691
  function buildMuseExecArgs(input) {
103425
103692
  const args = [
@@ -103463,10 +103730,15 @@ function museApprovalsUnsupported(runtimeMode) {
103463
103730
  * MuseExecEvents — pure translation of `muse exec --json` JSONL records into
103464
103731
  * canonical `ProviderRuntimeEvent`s.
103465
103732
  *
103466
- * Muse Code has no ACP or app-server surface (verified against CLI
103467
- * 0.1.0-R708.1): the only scriptable path is `muse exec --json`, which runs
103468
- * one prompt to completion and prints one JSON object per line. Multi-turn
103469
- * threads are rebuilt by re-invoking with `--session-id`.
103733
+ * P4Code drives Muse through `muse exec --json`, which runs one prompt to
103734
+ * completion and prints one JSON object per line; multi-turn threads are
103735
+ * rebuilt by re-invoking with `--session-id`. Muse Code 1.1.1 also serves a
103736
+ * session host over stdio (`muse serve`, the MSP wire protocol, exported by
103737
+ * `muse schema`) with approval, user-input and steering methods that `exec`
103738
+ * has no channel for. Moving onto MSP is the way to lift those limits; the
103739
+ * mapping below stays on the `exec` stream until then.
103740
+ *
103741
+ * Record shapes here were re-verified against CLI 1.1.1-R2514.1.
103470
103742
  *
103471
103743
  * Everything here is a pure function over a caller-owned state record so the
103472
103744
  * mapping can be tested against captured CLI output without spawning a
@@ -103760,21 +104032,19 @@ function toolCallIdFromIdempotencyKey(key) {
103760
104032
  const callId = key.slice(5).trim();
103761
104033
  return callId.length > 0 ? callId : void 0;
103762
104034
  }
104035
+ /**
104036
+ * Muse's tool names, as the CLI itself reports them (Muse Code 1.1.1). Names
104037
+ * it does not have are not listed: an unknown tool still maps to
104038
+ * `dynamic_tool_call`, which is the right answer for the memory, goal, cron,
104039
+ * skill and todo tools that have no canonical item type here.
104040
+ */
103763
104041
  function itemTypeForToolName(toolName) {
103764
104042
  switch (toolName) {
103765
104043
  case "bash":
103766
- case "shell":
103767
- case "run_command":
103768
- case "unified_exec": return "command_execution";
104044
+ case "bash_input": return "command_execution";
103769
104045
  case "write_file":
103770
- case "edit_file":
103771
- case "multi_edit":
103772
- case "apply_patch":
103773
- case "delete_file":
103774
- case "move_file": return "file_change";
103775
- case "web_search":
103776
- case "web_fetch": return "web_search";
103777
- case "view_image": return "image_view";
104046
+ case "edit_file": return "file_change";
104047
+ case "web_search": return "web_search";
103778
104048
  default:
103779
104049
  if (toolName?.startsWith("subagent_")) return "collab_agent_tool_call";
103780
104050
  if (toolName?.startsWith("mcp__")) return "mcp_tool_call";
@@ -103990,11 +104260,12 @@ const makeMuseTextGeneration = (museSettings, environment = process.env) => Effe
103990
104260
  /**
103991
104261
  * MuseExecRuntime — one `muse exec --json` child process per turn.
103992
104262
  *
103993
- * Muse Code has no long-lived protocol surface: a turn is a process, and the
103994
- * thread is rebuilt from the CLI's own event-sourced session log when the
103995
- * next turn passes `--session-id`. This module owns spawning, line framing,
103996
- * interruption, and exit classification; it knows nothing about canonical
103997
- * runtime events.
104263
+ * On the `exec` path a turn is a process, and the thread is rebuilt from the
104264
+ * CLI's own event-sourced session log when the next turn passes
104265
+ * `--session-id`. (Muse Code 1.1.1 does have a long-lived surface —
104266
+ * `muse serve`, the MSP session host which this driver does not use yet.)
104267
+ * This module owns spawning, line framing, interruption, and exit
104268
+ * classification; it knows nothing about canonical runtime events.
103998
104269
  *
103999
104270
  * @module provider/muse/MuseExecRuntime
104000
104271
  */
@@ -104238,11 +104509,12 @@ function nonNegativeInt(value) {
104238
104509
  *
104239
104510
  * Consequences that are visible to users, and deliberate:
104240
104511
  *
104241
- * - **No interactive approvals.** Headless Muse has no channel to answer an
104242
- * approval prompt, so the adapter runs with approval disabled and lets
104243
- * Muse's OS sandbox do the containing. `respondToRequest` fails rather
104244
- * than pretending to route a decision, and `approval-required` threads get
104245
- * a `config.warning`.
104512
+ * - **No interactive approvals.** `muse exec` has no channel to answer an
104513
+ * approval prompt Muse Code 1.1.1 serves approvals over MSP
104514
+ * (`muse serve`), which this adapter does not speak — so the adapter runs
104515
+ * with approval disabled and lets Muse's OS sandbox do the containing.
104516
+ * `respondToRequest` fails rather than pretending to route a decision, and
104517
+ * `approval-required` threads get a `config.warning`.
104246
104518
  * - **No provider-side rollback.** Muse can fork a session interactively but
104247
104519
  * exposes nothing headless.
104248
104520
  * - **Tool arguments are not streamed.** They exist only in the on-disk
@@ -104382,10 +104654,12 @@ function makeMuseAdapter(museSettings, options) {
104382
104654
  }
104383
104655
  const turnId = TurnId.make(yield* randomUUIDv4);
104384
104656
  const model = input.modelSelection?.model ?? ctx.session.model;
104657
+ const reasoningEffort = input.modelSelection?.instanceId === boundInstanceId ? museReasoningEffort(getModelSelectionStringOptionValue(input.modelSelection, "reasoningEffort")) : void 0;
104385
104658
  const args = buildMuseExecArgs({
104386
104659
  sessionId: ctx.museSessionId,
104387
104660
  prompt,
104388
104661
  model,
104662
+ reasoningEffort,
104389
104663
  baseUrl: museSettings.baseUrl || void 0,
104390
104664
  runtimeMode: ctx.session.runtimeMode,
104391
104665
  imagePaths
@@ -104420,54 +104694,74 @@ function makeMuseAdapter(museSettings, options) {
104420
104694
  ...model ? { model } : {}
104421
104695
  };
104422
104696
  const turnScope = yield* Scope.make();
104423
- const run = yield* runMuseExec({
104697
+ let runStarted = false;
104698
+ let settled = false;
104699
+ let outcome;
104700
+ /**
104701
+ * Release everything the turn holds, exactly once, whatever ended it:
104702
+ * a clean exit, a typed failure, a defect, or an interrupt from a
104703
+ * stopped thread. Leaving `activeTurn` or a `running` session behind
104704
+ * would wedge the thread — every later `sendTurn` would refuse with
104705
+ * "a Muse turn is already running" until the server restarted — so
104706
+ * this runs from an `ensuring` finalizer rather than the happy path.
104707
+ */
104708
+ const finishTurn = Effect.suspend(() => {
104709
+ if (settled) return Effect.void;
104710
+ settled = true;
104711
+ return Effect.gen(function* () {
104712
+ ctx.activeTurn = void 0;
104713
+ yield* Scope.close(turnScope, Exit.void);
104714
+ if (runStarted && state.terminal === void 0) if (outcome === void 0 || outcome.kind === "interrupted") yield* publish({
104715
+ type: "turn.aborted",
104716
+ ...yield* makeEventStamp(),
104717
+ provider: PROVIDER$1,
104718
+ providerInstanceId: boundInstanceId,
104719
+ threadId: input.threadId,
104720
+ turnId,
104721
+ payload: { reason: "interrupted" }
104722
+ });
104723
+ else {
104724
+ const detail = outcome.kind === "failed" ? outcome.stderr || `\`muse exec\` exited with code ${outcome.exitCode}.` : "`muse exec` ended without a terminal event.";
104725
+ yield* publish({
104726
+ type: "turn.completed",
104727
+ ...yield* makeEventStamp(),
104728
+ provider: PROVIDER$1,
104729
+ providerInstanceId: boundInstanceId,
104730
+ threadId: input.threadId,
104731
+ turnId,
104732
+ payload: {
104733
+ state: "failed",
104734
+ errorMessage: detail
104735
+ }
104736
+ });
104737
+ }
104738
+ if (runStarted) yield* publishTokenUsage(ctx, turnId);
104739
+ const updatedAt = yield* nowIso;
104740
+ const { activeTurnId: _activeTurnId, ...readySession } = ctx.session;
104741
+ ctx.session = {
104742
+ ...readySession,
104743
+ status: "ready",
104744
+ updatedAt
104745
+ };
104746
+ });
104747
+ }).pipe(Effect.ignore);
104748
+ yield* Effect.acquireUseRelease(runMuseExec({
104424
104749
  threadId: input.threadId,
104425
104750
  binaryPath: museSettings.binaryPath || "muse",
104426
104751
  args,
104427
104752
  cwd: ctx.cwd,
104428
104753
  environment,
104429
104754
  onLine: handleLine
104430
- }).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() => Scope.close(turnScope, Exit.void)));
104431
- ctx.activeTurn = {
104432
- turnId,
104433
- run,
104434
- state
104435
- };
104436
- const outcome = yield* run.awaitOutcome;
104437
- yield* Scope.close(turnScope, Exit.void);
104438
- ctx.activeTurn = void 0;
104439
- if (state.terminal === void 0) if (outcome.kind === "interrupted") yield* publish({
104440
- type: "turn.aborted",
104441
- ...yield* makeEventStamp(),
104442
- provider: PROVIDER$1,
104443
- providerInstanceId: boundInstanceId,
104444
- threadId: input.threadId,
104445
- turnId,
104446
- payload: { reason: "interrupted" }
104447
- });
104448
- else {
104449
- const detail = outcome.kind === "failed" ? outcome.stderr || `\`muse exec\` exited with code ${outcome.exitCode}.` : "`muse exec` ended without a terminal event.";
104450
- yield* publish({
104451
- type: "turn.completed",
104452
- ...yield* makeEventStamp(),
104453
- provider: PROVIDER$1,
104454
- providerInstanceId: boundInstanceId,
104455
- threadId: input.threadId,
104755
+ }).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() => finishTurn), Effect.tap((run) => Effect.sync(() => {
104756
+ runStarted = true;
104757
+ ctx.activeTurn = {
104456
104758
  turnId,
104457
- payload: {
104458
- state: "failed",
104459
- errorMessage: detail
104460
- }
104461
- });
104462
- }
104463
- yield* publishTokenUsage(ctx, turnId);
104464
- const updatedAt = yield* nowIso;
104465
- const { activeTurnId: _activeTurnId, ...readySession } = ctx.session;
104466
- ctx.session = {
104467
- ...readySession,
104468
- status: "ready",
104469
- updatedAt
104470
- };
104759
+ run,
104760
+ state
104761
+ };
104762
+ }))), (run) => Effect.gen(function* () {
104763
+ outcome = yield* run.awaitOutcome;
104764
+ }), () => finishTurn);
104471
104765
  return {
104472
104766
  threadId: input.threadId,
104473
104767
  turnId,
@@ -104596,7 +104890,17 @@ const MUSE_PRESENTATION = {
104596
104890
  requiresNewThreadForModelChange: false,
104597
104891
  supportedRuntimeModes: ["auto", "full-access"]
104598
104892
  };
104599
- const EMPTY_CAPABILITIES = createModelCapabilities({ optionDescriptors: [] });
104893
+ const MUSE_CAPABILITIES = createModelCapabilities({ optionDescriptors: [{
104894
+ id: "reasoningEffort",
104895
+ label: "Reasoning",
104896
+ type: "select",
104897
+ options: MUSE_REASONING_EFFORTS.map((effort) => ({
104898
+ id: effort,
104899
+ label: effort.charAt(0).toUpperCase() + effort.slice(1),
104900
+ ...effort === "high" ? { isDefault: true } : {}
104901
+ })),
104902
+ currentValue: MUSE_DEFAULT_REASONING_EFFORT
104903
+ }] });
104600
104904
  const VERSION_PROBE_TIMEOUT_MS = 4e3;
104601
104905
  /** Models Meta documents for Muse Code; used until the CLI caches a catalog. */
104602
104906
  const MUSE_BUILT_IN_MODELS = [
@@ -104604,30 +104908,30 @@ const MUSE_BUILT_IN_MODELS = [
104604
104908
  slug: "muse-spark-1.3",
104605
104909
  name: "Muse Spark 1.3",
104606
104910
  isCustom: false,
104607
- capabilities: EMPTY_CAPABILITIES
104911
+ capabilities: MUSE_CAPABILITIES
104608
104912
  },
104609
104913
  {
104610
104914
  slug: "muse-spark-1.3-contributor",
104611
104915
  name: "Muse Spark 1.3 (contributor)",
104612
104916
  isCustom: false,
104613
104917
  isDefault: true,
104614
- capabilities: EMPTY_CAPABILITIES
104918
+ capabilities: MUSE_CAPABILITIES
104615
104919
  },
104616
104920
  {
104617
104921
  slug: "muse-spark-1.2",
104618
104922
  name: "Muse Spark 1.2",
104619
104923
  isCustom: false,
104620
- capabilities: EMPTY_CAPABILITIES
104924
+ capabilities: MUSE_CAPABILITIES
104621
104925
  },
104622
104926
  {
104623
104927
  slug: "muse-spark-1.2-contributor",
104624
104928
  name: "Muse Spark 1.2 (contributor)",
104625
104929
  isCustom: false,
104626
- capabilities: EMPTY_CAPABILITIES
104930
+ capabilities: MUSE_CAPABILITIES
104627
104931
  }
104628
104932
  ];
104629
104933
  function museModelsFromSettings(customModels, builtInModels = MUSE_BUILT_IN_MODELS) {
104630
- return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);
104934
+ return providerModelsFromSettings(builtInModels, customModels ?? [], MUSE_CAPABILITIES);
104631
104935
  }
104632
104936
  const discoverMuseModels = Effect.fn("discoverMuseModels")(function* (environment) {
104633
104937
  return (yield* readMuseModelCatalog(environment)).map((model) => ({
@@ -104635,7 +104939,7 @@ const discoverMuseModels = Effect.fn("discoverMuseModels")(function* (environmen
104635
104939
  name: model.name,
104636
104940
  isCustom: false,
104637
104941
  ...model.isDefault ? { isDefault: true } : {},
104638
- capabilities: EMPTY_CAPABILITIES
104942
+ capabilities: MUSE_CAPABILITIES
104639
104943
  }));
104640
104944
  });
104641
104945
  /**