@p4code/cli 0.1.4 → 0.1.6

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 (87) hide show
  1. package/dist/bin.mjs +1268 -169
  2. package/dist/client/assets/{DiffPanel-uBagt_9w.js → DiffPanel-CcuhH24b.js} +2 -2
  3. package/dist/client/assets/{FilePreviewPanel-Dgo3_--S.js → FilePreviewPanel-IupIzTGt.js} +2 -2
  4. package/dist/client/assets/{PreviewPanel-BzrobngD.js → PreviewPanel-CMRKi8Jr.js} +2 -2
  5. package/dist/client/assets/{angular-html-C9jMC3ZQ.js → angular-html-D4VEGoU_.js} +2 -2
  6. package/dist/client/assets/{angular-ts-CYoSZaKw.js → angular-ts-DDbN_Pcg.js} +2 -2
  7. package/dist/client/assets/{apl-CgWZs6Gn.js → apl-BH7XGSiX.js} +2 -2
  8. package/dist/client/assets/arrow-right-B-_6vDIF.js +2 -0
  9. package/dist/client/assets/{astro-CUIqu6Jz.js → astro-DKjLEfvq.js} +2 -2
  10. package/dist/client/assets/{blade-iplcV4Ix.js → blade-mO6atsi4.js} +2 -2
  11. package/dist/client/assets/{c-Dd6jN1w_.js → c-drphy09y.js} +2 -2
  12. package/dist/client/assets/{cobol-0ScsIx-z.js → cobol-Do365H-h.js} +2 -2
  13. package/dist/client/assets/{coffee-KS_79spe.js → coffee-CJ9y15YY.js} +2 -2
  14. package/dist/client/assets/{cpp-BllSLkvw.js → cpp-C1FVZ39w.js} +2 -2
  15. package/dist/client/assets/{crystal-DF2_oukQ.js → crystal-D1Y9mCOy.js} +2 -2
  16. package/dist/client/assets/{css-DBr167Oo.js → css-C1chQpfJ.js} +2 -2
  17. package/dist/client/assets/{edge-CRJ0aLAq.js → edge-Cir90YUi.js} +2 -2
  18. package/dist/client/assets/{elixir-DXl1oanW.js → elixir-D8U_xdUO.js} +2 -2
  19. package/dist/client/assets/{elm-CqKx609q.js → elm-DFyeiMH7.js} +2 -2
  20. package/dist/client/assets/{erb-MLJdAbE4.js → erb-C6TvSrSi.js} +2 -2
  21. package/dist/client/assets/{fileCommentAnnotations-BjkCgE8J.js → fileCommentAnnotations-BotL9n2H.js} +2 -2
  22. package/dist/client/assets/{git-rebase-M0aVykt3.js → git-rebase-C7PDtezn.js} +2 -2
  23. package/dist/client/assets/{glimmer-js-CXPK1DP8.js → glimmer-js-CXugsloP.js} +2 -2
  24. package/dist/client/assets/{glimmer-ts-wptUL2fp.js → glimmer-ts-IiHVp7il.js} +2 -2
  25. package/dist/client/assets/{glsl-BRKhgqUF.js → glsl-eBdXjOgx.js} +2 -2
  26. package/dist/client/assets/{graphql-1BUUHzT4.js → graphql-CBwWowq8.js} +2 -2
  27. package/dist/client/assets/{hack-BR9NLXvX.js → hack-DoKoRNA-.js} +2 -2
  28. package/dist/client/assets/{haml-BgBKuGz0.js → haml-B2cA0ELm.js} +2 -2
  29. package/dist/client/assets/{handlebars-BUs0Hgpn.js → handlebars-OQNujFnM.js} +2 -2
  30. package/dist/client/assets/{html-BWwNwdti.js → html-B9l55IY7.js} +2 -2
  31. package/dist/client/assets/{html-derivative-B2dGSGlK.js → html-derivative-C24d_8BY.js} +2 -2
  32. package/dist/client/assets/{http-NIyxdCuV.js → http-Ca2KxOLT.js} +2 -2
  33. package/dist/client/assets/{hurl-CH8mm6dw.js → hurl-BuLZo6wH.js} +2 -2
  34. package/dist/client/assets/index-B-nUm8w7.js +664 -0
  35. package/dist/client/assets/index-DfKhU-QO.css +1 -0
  36. package/dist/client/assets/{java-CQPmtuJ5.js → java-BEVFbCvA.js} +2 -2
  37. package/dist/client/assets/{javascript-DzjJJM-s.js → javascript-AWDSrR1H.js} +2 -2
  38. package/dist/client/assets/{jinja-4Qs6tVyC.js → jinja-CFK-VDc5.js} +2 -2
  39. package/dist/client/assets/{jison-CPG2kEI3.js → jison-CSmyyQ0V.js} +2 -2
  40. package/dist/client/assets/{json-DlY_RU7e.js → json-D0qpEJUc.js} +2 -2
  41. package/dist/client/assets/{jsx-Cd_ApdjY.js → jsx-smdyqdvA.js} +2 -2
  42. package/dist/client/assets/{julia-SX8cqtZg.js → julia-DWo_5QH9.js} +2 -2
  43. package/dist/client/assets/{just-Ao5BO2B0.js → just-74GGYpCk.js} +2 -2
  44. package/dist/client/assets/{latex-C46nB2Dk.js → latex-d8vfKbSv.js} +2 -2
  45. package/dist/client/assets/{liquid-BMcKK3Z1.js → liquid-CsWNZJPs.js} +2 -2
  46. package/dist/client/assets/{lua-DjG6qAWa.js → lua-x3z7TbKl.js} +2 -2
  47. package/dist/client/assets/{marko-3JoFYw3q.js → marko-CJyhwQKw.js} +2 -2
  48. package/dist/client/assets/{mdc-BpGP1uha.js → mdc-CtbrrOYL.js} +2 -2
  49. package/dist/client/assets/{nginx-HOkzmdTd.js → nginx-CY-uw8dq.js} +2 -2
  50. package/dist/client/assets/{nim-CeKNmMcr.js → nim-DqT4w7rK.js} +2 -2
  51. package/dist/client/assets/{perl-BWQkxnsH.js → perl-CE3_DH1L.js} +2 -2
  52. package/dist/client/assets/{php-Df4cK7Vr.js → php-Zxsq1XMP.js} +2 -2
  53. package/dist/client/assets/{pug-CoCjyAiE.js → pug-DnCImvlN.js} +2 -2
  54. package/dist/client/assets/{qml-DYi25PQ3.js → qml-DLfoubjW.js} +2 -2
  55. package/dist/client/assets/{r-BzJkuBsW.js → r-BtfLsgk8.js} +2 -2
  56. package/dist/client/assets/{razor-BhG_PBuW.js → razor-CzAGh9fe.js} +2 -2
  57. package/dist/client/assets/{regexp-BAM54YZk.js → regexp-Bw17HBbP.js} +2 -2
  58. package/dist/client/assets/{rst-DHl4hyZ2.js → rst-DhRr0i5h.js} +2 -2
  59. package/dist/client/assets/{ruby-gNx0WumM.js → ruby-BnQcHU-H.js} +2 -2
  60. package/dist/client/assets/{sas-CW83v0Dn.js → sas-mMUu1htl.js} +2 -2
  61. package/dist/client/assets/{scss-WgrcQHVQ.js → scss-0_lOQ1qp.js} +2 -2
  62. package/dist/client/assets/{shellscript-CKEnN-lX.js → shellscript-BAdG8zJQ.js} +2 -2
  63. package/dist/client/assets/{shellsession-BbcmvnCz.js → shellsession-upfFHc_2.js} +2 -2
  64. package/dist/client/assets/{soy-D1MIxKdU.js → soy-Dzncl5z6.js} +2 -2
  65. package/dist/client/assets/{sql-Bhxl_Mi7.js → sql-Ca2FgbAh.js} +2 -2
  66. package/dist/client/assets/{stata-C-LT_UBU.js → stata-D8IymSlL.js} +2 -2
  67. package/dist/client/assets/{surrealql-Ch_ceRvJ.js → surrealql-CZsXvHAi.js} +2 -2
  68. package/dist/client/assets/{svelte-DlhsOd0r.js → svelte-nM61Il-m.js} +2 -2
  69. package/dist/client/assets/{templ-CIHR3tEf.js → templ-DzjlBYVo.js} +2 -2
  70. package/dist/client/assets/{tex-DRWioNbs.js → tex-BPwIdn9G.js} +2 -2
  71. package/dist/client/assets/textarea-DhTMBBxg.js +46 -0
  72. package/dist/client/assets/{ts-tags-BvRYwyAy.js → ts-tags-BL9D6Ojm.js} +2 -2
  73. package/dist/client/assets/{tsx-BRkrE5rM.js → tsx-AKDVGVPx.js} +2 -2
  74. package/dist/client/assets/{twig-BlZvFnQN.js → twig-B79f_Jz8.js} +2 -2
  75. package/dist/client/assets/{typescript-Djd2wnjW.js → typescript-BsyojNKj.js} +2 -2
  76. package/dist/client/assets/{vue-DActsgCM.js → vue-BTqtDJT-.js} +2 -2
  77. package/dist/client/assets/{vue-html-Cqt121Xa.js → vue-html-B_UfyILk.js} +2 -2
  78. package/dist/client/assets/{vue-vine-CzSkk7Q4.js → vue-vine-B8EK-Iln.js} +2 -2
  79. package/dist/client/assets/{xml-DPi6y6-m.js → xml-Do-3lCfL.js} +2 -2
  80. package/dist/client/assets/{xsl-BAvs_EI4.js → xsl-CwgwYGXC.js} +2 -2
  81. package/dist/client/assets/{yaml-DFFGim1C.js → yaml-C4XlY-p5.js} +2 -2
  82. package/dist/client/index.html +3 -3
  83. package/package.json +1 -1
  84. package/dist/client/assets/arrow-right-BpqEKABW.js +0 -2
  85. package/dist/client/assets/index-C7UXI10J.css +0 -1
  86. package/dist/client/assets/index-D28oUrhs.js +0 -664
  87. package/dist/client/assets/textarea-CHMvJ0-S.js +0 -46
package/dist/bin.mjs CHANGED
@@ -121,7 +121,7 @@ const closeServer = (server) => {
121
121
  * NetService - Service tag for startup networking helpers.
122
122
  */
123
123
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
124
- const make$72 = () => {
124
+ const make$73 = () => {
125
125
  /**
126
126
  * Returns true when a TCP server can bind to {host, port}.
127
127
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -230,10 +230,10 @@ const make$72 = () => {
230
230
  })
231
231
  };
232
232
  };
233
- const layer$68 = Layer.sync(NetService, make$72);
233
+ const layer$69 = Layer.sync(NetService, make$73);
234
234
  //#endregion
235
235
  //#region package.json
236
- var version = "0.1.4";
236
+ var version = "0.1.6";
237
237
  //#endregion
238
238
  //#region src/config.ts
239
239
  /**
@@ -254,8 +254,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
254
254
  /** @deprecated Import and use `layerTest` from this module. */
255
255
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$2(cwd, baseDirOrPrefix);
256
256
  };
257
- const make$71 = (config) => ServerConfig$1.of(config);
258
- const layer$67 = (config) => Layer.succeed(ServerConfig$1, make$71(config));
257
+ const make$72 = (config) => ServerConfig$1.of(config);
258
+ const layer$68 = (config) => Layer.succeed(ServerConfig$1, make$72(config));
259
259
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
260
260
  const { join } = yield* Path.Path;
261
261
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -5634,11 +5634,22 @@ const ClaudeSettings = makeProviderSettingsSchema({
5634
5634
  placeholder: "e.g. --chrome",
5635
5635
  clearWhenEmpty: "omit"
5636
5636
  }
5637
+ })),
5638
+ /**
5639
+ * The SDK preset system prompt omits the interactive CLI's terminal-tone
5640
+ * sections, so the model tends to open a turn with a tool call instead of
5641
+ * saying what it is about to do. This appends that instruction back.
5642
+ */
5643
+ narrateBeforeTools: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true)), Schema$1.annotateKey({
5644
+ title: "Explain before tool calls",
5645
+ description: "Ask Claude to write one short sentence before its first tool call in a turn.",
5646
+ providerSettingsForm: { control: "switch" }
5637
5647
  }))
5638
5648
  }, { order: [
5639
5649
  "binaryPath",
5640
5650
  "homePath",
5641
- "launchArgs"
5651
+ "launchArgs",
5652
+ "narrateBeforeTools"
5642
5653
  ] });
5643
5654
  const CursorSettings = makeProviderSettingsSchema({
5644
5655
  enabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false)), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } })),
@@ -5720,6 +5731,7 @@ const SourceControlWritingStyleSettings = Schema$1.Struct({
5720
5731
  followChangeRequestTemplates: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true)))
5721
5732
  });
5722
5733
  const DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL = Duration.seconds(30);
5734
+ const TaskBoardSource = Schema$1.Literals(["local", "hub"]);
5723
5735
  const ServerSettings = Schema$1.Struct({
5724
5736
  enableAssistantStreaming: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
5725
5737
  enableProviderUpdateChecks: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
@@ -5733,6 +5745,42 @@ const ServerSettings = Schema$1.Struct({
5733
5745
  }))),
5734
5746
  sourceControlWritingStyle: SourceControlWritingStyleSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
5735
5747
  sourceControlWriterModelSelection: Schema$1.NullOr(ModelSelection).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
5748
+ /**
5749
+ * Where this server's task board lives.
5750
+ *
5751
+ * `local` is this machine's own SQLite database, which is where every board
5752
+ * has been until now. `hub` puts the board on the linked hub, so two servers
5753
+ * read and write one board instead of two that silently diverge.
5754
+ *
5755
+ * A setting rather than "hub if one is linked", because a hub can be linked
5756
+ * for skills alone, and flipping a board's home is not something to have
5757
+ * happen as a side effect of connecting. It is also the way back: the local
5758
+ * rows are still there, untouched, when it is set to `local` again.
5759
+ *
5760
+ * **On `hub`, an unreachable hub means no board.** There is no local
5761
+ * fallback by decision: answering from a stale local copy would recreate the
5762
+ * per-machine divergence the hub exists to remove, and it would do it
5763
+ * invisibly.
5764
+ */
5765
+ taskBoardSource: TaskBoardSource.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
5766
+ /**
5767
+ * Skills the agents on this server must not see, by name.
5768
+ *
5769
+ * Server-scoped rather than per provider instance, because a skill is a
5770
+ * property of the machine's filesystem and every instance on it reads the
5771
+ * same directories.
5772
+ *
5773
+ * Deliberately **not** synced through the hub. A shared skill that is muted
5774
+ * on one machine is the useful combination — the laptop does not want the
5775
+ * skill that shells out to the Studio's build tooling — and syncing the mute
5776
+ * would make that inexpressible.
5777
+ *
5778
+ * What "disabled" means is bounded by what the provider can enforce: the
5779
+ * skill is hidden from the model's listing and from `/name`, and its files
5780
+ * stay on disk and remain readable by Read and Bash. It is a context filter,
5781
+ * not a sandbox, and nothing here should be described to a user as one.
5782
+ */
5783
+ disabledSkills: Schema$1.Array(TrimmedNonEmptyString).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
5736
5784
  providers: Schema$1.Struct({
5737
5785
  codex: CodexSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
5738
5786
  claudeAgent: ClaudeSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
@@ -5790,7 +5838,8 @@ const ClaudeSettingsPatch = Schema$1.Struct({
5790
5838
  binaryPath: Schema$1.optionalKey(TrimmedString),
5791
5839
  homePath: Schema$1.optionalKey(TrimmedString),
5792
5840
  customModels: Schema$1.optionalKey(Schema$1.Array(Schema$1.String)),
5793
- launchArgs: Schema$1.optionalKey(TrimmedString)
5841
+ launchArgs: Schema$1.optionalKey(TrimmedString),
5842
+ narrateBeforeTools: Schema$1.optionalKey(Schema$1.Boolean)
5794
5843
  });
5795
5844
  const CursorSettingsPatch = Schema$1.Struct({
5796
5845
  enabled: Schema$1.optionalKey(Schema$1.Boolean),
@@ -5824,6 +5873,8 @@ const ServerSettingsPatch = Schema$1.Struct({
5824
5873
  followChangeRequestTemplates: Schema$1.optionalKey(Schema$1.Boolean)
5825
5874
  })),
5826
5875
  sourceControlWriterModelSelection: Schema$1.optionalKey(Schema$1.NullOr(ModelSelection)),
5876
+ disabledSkills: Schema$1.optionalKey(Schema$1.Array(TrimmedNonEmptyString)),
5877
+ taskBoardSource: Schema$1.optionalKey(TaskBoardSource),
5827
5878
  observability: Schema$1.optionalKey(Schema$1.Struct({
5828
5879
  otlpTracesUrl: Schema$1.optionalKey(TrimmedString),
5829
5880
  otlpMetricsUrl: Schema$1.optionalKey(TrimmedString)
@@ -7317,6 +7368,22 @@ const TaskStreamEvent = Schema$1.Union([
7317
7368
  taskId: TaskId
7318
7369
  })
7319
7370
  ]);
7371
+ Schema$1.Struct({
7372
+ patch: TaskUpdateInput,
7373
+ /** The status the caller believes the task is in. */
7374
+ expectedStatus: TaskStatus
7375
+ });
7376
+ Schema$1.Union([
7377
+ Schema$1.Struct({
7378
+ outcome: Schema$1.Literal("updated"),
7379
+ task: Task
7380
+ }),
7381
+ Schema$1.Struct({ outcome: Schema$1.Literal("missing") }),
7382
+ Schema$1.Struct({
7383
+ outcome: Schema$1.Literal("conflict"),
7384
+ current: Task
7385
+ })
7386
+ ]);
7320
7387
  /**
7321
7388
  * Start an agent on a task.
7322
7389
  *
@@ -7935,6 +8002,81 @@ var McpRegistryError = class extends Schema$1.TaggedErrorClass()("McpRegistryErr
7935
8002
  }
7936
8003
  };
7937
8004
  //#endregion
8005
+ //#region ../../packages/contracts/src/skillRegistry.ts
8006
+ /**
8007
+ * Finding skills that are not on this machine yet.
8008
+ *
8009
+ * Managing the skills you already have is CRUD, and that is `agentAsset.ts`.
8010
+ * This is the other half: a catalogue to search and a repository to read a
8011
+ * skill out of, with the install itself going through the ordinary local-create
8012
+ * path so there is exactly one way a skill lands on disk.
8013
+ *
8014
+ * **Nothing here installs anything.** Search returns names; fetch returns the
8015
+ * files it *would* write, with the source they came from. The confirmation step
8016
+ * is a person looking at that list, which is the only reason it is two calls
8017
+ * rather than one — a `SKILL.md` is instructions handed to an agent that
8018
+ * already has a shell, so what it contains is worth seeing before it arrives.
8019
+ *
8020
+ * @module skillRegistry
8021
+ */
8022
+ /**
8023
+ * One catalogue hit.
8024
+ *
8025
+ * `source` is the repository, and it is not decoration: it is what a person
8026
+ * judges the install on, so it travels with every result rather than being
8027
+ * fetched later.
8028
+ */
8029
+ const RegistrySkill = Schema$1.Struct({
8030
+ /** `owner/repo/skill-name`, the catalogue's own identifier. */
8031
+ id: TrimmedNonEmptyString,
8032
+ name: TrimmedNonEmptyString,
8033
+ /** `owner/repo`. */
8034
+ source: TrimmedNonEmptyString,
8035
+ /** How many installs the catalogue has counted. Popularity, not endorsement. */
8036
+ installs: Schema$1.Int
8037
+ });
8038
+ const RegistrySkillSearchInput = Schema$1.Struct({ query: TrimmedNonEmptyString });
8039
+ /**
8040
+ * `unavailable` rather than a failure, matching the hub calls: a catalogue on
8041
+ * someone else's infrastructure being down is an ordinary condition, and a
8042
+ * panel has to say which of "nothing matched" and "could not ask" happened.
8043
+ */
8044
+ const RegistrySkillSearchResult = Schema$1.Struct({
8045
+ results: Schema$1.Array(RegistrySkill),
8046
+ unavailable: Schema$1.NullOr(Schema$1.String)
8047
+ });
8048
+ const RegistrySkillFetchInput = Schema$1.Struct({
8049
+ /** `owner/repo/skill-name`, from a search result or typed by hand. */
8050
+ id: TrimmedNonEmptyString });
8051
+ /** Why a file in the skill's directory is not in the fetched set. */
8052
+ const RegistrySkillSkippedFile = Schema$1.Struct({
8053
+ path: Schema$1.String,
8054
+ reason: Schema$1.Literals([
8055
+ "too-large",
8056
+ "too-many-files",
8057
+ "over-total-size"
8058
+ ])
8059
+ });
8060
+ /**
8061
+ * What installing this skill would write, and where it came from.
8062
+ *
8063
+ * `skipped` is part of the answer rather than a log line. A bound that silently
8064
+ * drops a script leaves a skill that is present, looks installed, and fails at
8065
+ * the moment it is used.
8066
+ */
8067
+ const RegistrySkillFetchResult = Schema$1.Struct({
8068
+ found: Schema$1.Boolean,
8069
+ id: TrimmedNonEmptyString,
8070
+ /** The directory name the skill would take on disk. */
8071
+ name: Schema$1.String,
8072
+ source: Schema$1.String,
8073
+ /** Where these files can be read in full, before trusting them. */
8074
+ sourceUrl: Schema$1.String,
8075
+ files: Schema$1.Array(AgentAssetFile),
8076
+ skipped: Schema$1.Array(RegistrySkillSkippedFile),
8077
+ unavailable: Schema$1.NullOr(Schema$1.String)
8078
+ });
8079
+ //#endregion
7938
8080
  //#region ../../packages/contracts/src/filesystem.ts
7939
8081
  const FILESYSTEM_PATH_MAX_LENGTH = 512;
7940
8082
  const FilesystemBrowseInput = Schema$1.Struct({
@@ -8197,6 +8339,8 @@ const WS_METHODS = {
8197
8339
  assetsDelete: "assets.delete",
8198
8340
  assetsCreateLocal: "assets.createLocal",
8199
8341
  assetsRemoveLocal: "assets.removeLocal",
8342
+ skillRegistrySearch: "skillRegistry.search",
8343
+ skillRegistryFetch: "skillRegistry.fetch",
8200
8344
  mcpList: "mcp.list",
8201
8345
  mcpSave: "mcp.save",
8202
8346
  mcpRemove: "mcp.remove",
@@ -8420,6 +8564,24 @@ const WsAssetsRemoveLocalRpc = Rpc.make(WS_METHODS.assetsRemoveLocal, {
8420
8564
  error: Schema$1.Union([EnvironmentAuthorizationError])
8421
8565
  });
8422
8566
  /**
8567
+ * Finding and reading skills that are not on this machine yet.
8568
+ *
8569
+ * Read-only, both of them. Installing is `assets.createLocal` with the files
8570
+ * `skillRegistry.fetch` returned, so a skill arriving from a catalogue goes
8571
+ * through exactly the same refusals as one typed into the New skill dialog, and
8572
+ * the step in between is a person looking at what would be written.
8573
+ */
8574
+ const WsSkillRegistrySearchRpc = Rpc.make(WS_METHODS.skillRegistrySearch, {
8575
+ payload: RegistrySkillSearchInput,
8576
+ success: RegistrySkillSearchResult,
8577
+ error: Schema$1.Union([EnvironmentAuthorizationError])
8578
+ });
8579
+ const WsSkillRegistryFetchRpc = Rpc.make(WS_METHODS.skillRegistryFetch, {
8580
+ payload: RegistrySkillFetchInput,
8581
+ success: RegistrySkillFetchResult,
8582
+ error: Schema$1.Union([EnvironmentAuthorizationError])
8583
+ });
8584
+ /**
8423
8585
  * Registering the user's own MCP servers.
8424
8586
  *
8425
8587
  * Every one of these answers with the whole list, for the same reason the sync
@@ -8829,7 +8991,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
8829
8991
  error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
8830
8992
  stream: true
8831
8993
  });
8832
- const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
8994
+ const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
8833
8995
  //#endregion
8834
8996
  //#region ../../packages/shared/src/oauthScope.ts
8835
8997
  const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
@@ -8975,7 +9137,7 @@ function deriveAuthClientMetadata(input) {
8975
9137
  //#endregion
8976
9138
  //#region src/auth/EnvironmentAuthPolicy.ts
8977
9139
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
8978
- const make$70 = Effect.gen(function* () {
9140
+ const make$71 = Effect.gen(function* () {
8979
9141
  const config = yield* ServerConfig$1;
8980
9142
  const isRemoteReachable = isRemoteReachableHost(config.host);
8981
9143
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -8993,7 +9155,7 @@ const make$70 = Effect.gen(function* () {
8993
9155
  };
8994
9156
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
8995
9157
  });
8996
- const layer$66 = Layer.effect(EnvironmentAuthPolicy, make$70);
9158
+ const layer$67 = Layer.effect(EnvironmentAuthPolicy, make$71);
8997
9159
  //#endregion
8998
9160
  //#region src/persistence/Errors.ts
8999
9161
  function summarizeSchemaIssue(issue) {
@@ -9174,7 +9336,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
9174
9336
  cause
9175
9337
  });
9176
9338
  }
9177
- const make$69 = Effect.gen(function* () {
9339
+ const make$70 = Effect.gen(function* () {
9178
9340
  const sql = yield* SqlClient.SqlClient;
9179
9341
  const createSessionRow = SqlSchema.void({
9180
9342
  Request: CreateAuthSessionInput,
@@ -9308,7 +9470,7 @@ const make$69 = Effect.gen(function* () {
9308
9470
  setLastConnectedAt
9309
9471
  };
9310
9472
  });
9311
- const layer$65 = Layer.effect(AuthSessionRepository, make$69);
9473
+ const layer$66 = Layer.effect(AuthSessionRepository, make$70);
9312
9474
  //#endregion
9313
9475
  //#region src/auth/ServerSecretStore.ts
9314
9476
  const secretStoreErrorContext = {
@@ -9375,7 +9537,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
9375
9537
  const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
9376
9538
  const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
9377
9539
  var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
9378
- const make$68 = Effect.gen(function* () {
9540
+ const make$69 = Effect.gen(function* () {
9379
9541
  const crypto = yield* Crypto.Crypto;
9380
9542
  const fileSystem = yield* FileSystem.FileSystem;
9381
9543
  const path = yield* Path.Path;
@@ -9445,7 +9607,7 @@ const make$68 = Effect.gen(function* () {
9445
9607
  remove
9446
9608
  });
9447
9609
  });
9448
- const layer$64 = Layer.effect(ServerSecretStore, make$68);
9610
+ const layer$65 = Layer.effect(ServerSecretStore, make$69);
9449
9611
  //#endregion
9450
9612
  //#region src/auth/SessionStore.ts
9451
9613
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -9683,7 +9845,7 @@ function toAuthClientSession(input) {
9683
9845
  current: false
9684
9846
  };
9685
9847
  }
9686
- const make$67 = Effect.gen(function* () {
9848
+ const make$68 = Effect.gen(function* () {
9687
9849
  const crypto = yield* Crypto.Crypto;
9688
9850
  const serverConfig = yield* ServerConfig$1;
9689
9851
  const secretStore = yield* ServerSecretStore;
@@ -9997,7 +10159,7 @@ const make$67 = Effect.gen(function* () {
9997
10159
  markDisconnected
9998
10160
  });
9999
10161
  });
10000
- const layer$63 = Layer.effect(SessionStore, make$67).pipe(Layer.provideMerge(layer$65));
10162
+ const layer$64 = Layer.effect(SessionStore, make$68).pipe(Layer.provideMerge(layer$66));
10001
10163
  //#endregion
10002
10164
  //#region src/persistence/AuthPairingLinks.ts
10003
10165
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -10058,7 +10220,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
10058
10220
  cause
10059
10221
  });
10060
10222
  }
10061
- const make$66 = Effect.gen(function* () {
10223
+ const make$67 = Effect.gen(function* () {
10062
10224
  const sql = yield* SqlClient.SqlClient;
10063
10225
  const createPairingLinkRow = SqlSchema.void({
10064
10226
  Request: CreateAuthPairingLinkInput,
@@ -10193,7 +10355,7 @@ const make$66 = Effect.gen(function* () {
10193
10355
  getByCredential
10194
10356
  };
10195
10357
  });
10196
- const layer$62 = Layer.effect(AuthPairingLinkRepository, make$66);
10358
+ const layer$63 = Layer.effect(AuthPairingLinkRepository, make$67);
10197
10359
  //#endregion
10198
10360
  //#region src/auth/PairingGrantStore.ts
10199
10361
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -10288,7 +10450,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
10288
10450
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
10289
10451
  const PAIRING_TOKEN_LENGTH = 12;
10290
10452
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
10291
- const make$65 = Effect.gen(function* () {
10453
+ const make$66 = Effect.gen(function* () {
10292
10454
  const crypto = yield* Crypto.Crypto;
10293
10455
  const config = yield* ServerConfig$1;
10294
10456
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -10486,7 +10648,7 @@ const make$65 = Effect.gen(function* () {
10486
10648
  consume
10487
10649
  });
10488
10650
  });
10489
- const layer$61 = Layer.effect(PairingGrantStore, make$65).pipe(Layer.provideMerge(layer$62));
10651
+ const layer$62 = Layer.effect(PairingGrantStore, make$66).pipe(Layer.provideMerge(layer$63));
10490
10652
  //#endregion
10491
10653
  //#region src/persistence/Migrations/001_OrchestrationEvents.ts
10492
10654
  var _001_OrchestrationEvents_default = Effect.gen(function* () {
@@ -12259,7 +12421,7 @@ function parseBearerToken(request) {
12259
12421
  const token = header.slice(7).trim();
12260
12422
  return token.length > 0 ? token : null;
12261
12423
  }
12262
- const make$64 = Effect.gen(function* () {
12424
+ const make$65 = Effect.gen(function* () {
12263
12425
  const policy = yield* EnvironmentAuthPolicy;
12264
12426
  const bootstrapCredentials = yield* PairingGrantStore;
12265
12427
  const sessions = yield* SessionStore;
@@ -12454,9 +12616,9 @@ const make$64 = Effect.gen(function* () {
12454
12616
  issueStartupPairingUrl
12455
12617
  });
12456
12618
  });
12457
- const layer$60 = Layer.effect(EnvironmentAuth, make$64).pipe(Layer.provideMerge(layer$61), Layer.provideMerge(layer$63), Layer.provideMerge(layer$66));
12458
- const storageLayer = Layer.mergeAll(layer$64, layerConfig);
12459
- const runtimeLayer = layer$60.pipe(Layer.provideMerge(storageLayer));
12619
+ const layer$61 = Layer.effect(EnvironmentAuth, make$65).pipe(Layer.provideMerge(layer$62), Layer.provideMerge(layer$64), Layer.provideMerge(layer$67));
12620
+ const storageLayer = Layer.mergeAll(layer$65, layerConfig);
12621
+ const runtimeLayer = layer$61.pipe(Layer.provideMerge(storageLayer));
12460
12622
  //#endregion
12461
12623
  //#region src/cliAuthFormat.ts
12462
12624
  const newline = "\n";
@@ -13211,7 +13373,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
13211
13373
  }
13212
13374
  return yield* Effect.gen(function* () {
13213
13375
  return yield* run(yield* EnvironmentAuth);
13214
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
13376
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$68(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
13215
13377
  });
13216
13378
  const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
13217
13379
  const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
@@ -13351,7 +13513,7 @@ const DEFAULT_LIMITS = {
13351
13513
  windowMillis: FAILURE_WINDOW_MS,
13352
13514
  blockMillis: BLOCK_DURATION_MS
13353
13515
  };
13354
- const make$63 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
13516
+ const make$64 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
13355
13517
  const state = yield* Ref.make(initialThrottleState);
13356
13518
  return HubAuthThrottle.of({
13357
13519
  shouldRefuse: Effect.gen(function* () {
@@ -13365,7 +13527,7 @@ const make$63 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
13365
13527
  })
13366
13528
  });
13367
13529
  });
13368
- const layer$59 = Layer.effect(HubAuthThrottle, make$63());
13530
+ const layer$60 = Layer.effect(HubAuthThrottle, make$64());
13369
13531
  //#endregion
13370
13532
  //#region src/hub/HubAuth.ts
13371
13533
  /**
@@ -13817,6 +13979,43 @@ const makeTaskRepository = Effect.gen(function* () {
13817
13979
  task
13818
13980
  })
13819
13981
  })));
13982
+ /**
13983
+ * The claim, and the reason it is a separate method rather than an option on
13984
+ * `patch`: only this one has an outcome a caller must branch on.
13985
+ *
13986
+ * The comparison happens *inside* the transaction alongside the write, which
13987
+ * is the only arrangement that makes it a compare-and-set. Reading first and
13988
+ * writing after would leave exactly the window it exists to close.
13989
+ */
13990
+ const patchIfStatus = (input, updatedAt) => sql.withTransaction(Effect.gen(function* () {
13991
+ const existing = yield* getTaskRow({ taskId: input.patch.taskId });
13992
+ if (Option.isNone(existing)) return { outcome: "missing" };
13993
+ if (existing.value.status !== input.expectedStatus) return {
13994
+ outcome: "conflict",
13995
+ current: existing.value
13996
+ };
13997
+ const updated = {
13998
+ ...existing.value,
13999
+ ...input.patch.projectId !== void 0 ? { projectId: input.patch.projectId } : {},
14000
+ ...input.patch.repositoryKey !== void 0 ? { repositoryKey: input.patch.repositoryKey } : {},
14001
+ ...input.patch.threadId !== void 0 ? { threadId: input.patch.threadId } : {},
14002
+ ...input.patch.title !== void 0 ? { title: input.patch.title } : {},
14003
+ ...input.patch.body !== void 0 ? { body: input.patch.body } : {},
14004
+ ...input.patch.status !== void 0 ? { status: input.patch.status } : {},
14005
+ ...input.patch.priority !== void 0 ? { priority: input.patch.priority } : {},
14006
+ ...input.patch.assignee !== void 0 ? { assignee: input.patch.assignee } : {},
14007
+ ...input.patch.labels !== void 0 ? { labels: input.patch.labels } : {},
14008
+ updatedAt
14009
+ };
14010
+ yield* upsertTaskRow(updated);
14011
+ return {
14012
+ outcome: "updated",
14013
+ task: updated
14014
+ };
14015
+ })).pipe(Effect.mapError(toPersistenceSqlError("TaskRepository.patchIfStatus:query")), Effect.tap((result) => result.outcome === "updated" ? publish({
14016
+ type: "upserted",
14017
+ task: result.task
14018
+ }) : Effect.void));
13820
14019
  const deleteById = (input) => deleteTaskRow(input).pipe(Effect.mapError(toPersistenceSqlError("TaskRepository.deleteById:query")), Effect.tap(() => publish({
13821
14020
  type: "deleted",
13822
14021
  taskId: input.taskId
@@ -13824,6 +14023,7 @@ const makeTaskRepository = Effect.gen(function* () {
13824
14023
  return {
13825
14024
  upsert,
13826
14025
  patch,
14026
+ patchIfStatus,
13827
14027
  getById,
13828
14028
  list,
13829
14029
  deleteById,
@@ -13944,13 +14144,25 @@ const updateTaskRoute = HttpRouter.add("PATCH", "/tasks/:taskId", respondToHubFa
13944
14144
  yield* authenticateHubRequest();
13945
14145
  const params = yield* HttpRouter.params;
13946
14146
  const taskId = TaskId.make(params.taskId ?? "");
13947
- const body = yield* readJsonBody();
14147
+ const { expectedStatus, ...patchBody } = (yield* readJsonBody()) ?? {};
13948
14148
  const input = yield* decodeOrInvalid(TaskUpdateInput, "Request body does not match TaskUpdateInput.")({
13949
- ...body,
14149
+ ...patchBody,
13950
14150
  taskId
13951
14151
  });
13952
14152
  const tasks = yield* TaskRepository;
13953
14153
  const timestamp = DateTime.formatIso(yield* DateTime.now);
14154
+ if (expectedStatus !== void 0) {
14155
+ const condition = yield* decodeOrInvalid(TaskStatus, "expectedStatus is not a task status.")(expectedStatus);
14156
+ const result = yield* tasks.patchIfStatus({
14157
+ patch: input,
14158
+ expectedStatus: condition
14159
+ }, timestamp);
14160
+ if (result.outcome === "missing") return notFound(taskId);
14161
+ return result.outcome === "conflict" ? HttpServerResponse.jsonUnsafe({
14162
+ error: "conflict",
14163
+ current: result.current
14164
+ }, { status: 409 }) : HttpServerResponse.jsonUnsafe(result.task);
14165
+ }
13954
14166
  const patched = yield* tasks.patch(input, timestamp);
13955
14167
  return Option.isNone(patched) ? notFound(taskId) : HttpServerResponse.jsonUnsafe(patched.value);
13956
14168
  })));
@@ -14056,7 +14268,7 @@ const deleteAssetRoute = HttpRouter.add("DELETE", "/assets/:kind/:name", respond
14056
14268
  const outcome = yield* (yield* AgentAssetRepository).deleteByName(requested);
14057
14269
  return outcome._tag === "conflict" ? assetConflict(Option.getOrNull(outcome.current)) : HttpServerResponse.empty({ status: 204 });
14058
14270
  })));
14059
- const layer$58 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
14271
+ const layer$59 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
14060
14272
  //#endregion
14061
14273
  //#region src/hub/Migrations/001_Tasks.ts
14062
14274
  /**
@@ -14271,7 +14483,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
14271
14483
  });
14272
14484
  if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
14273
14485
  }));
14274
- const makeHubLayer = HttpRouter.serve(layer$58).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$59), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
14486
+ const makeHubLayer = HttpRouter.serve(layer$59).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$60), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
14275
14487
  const runHub = Layer.launch(makeHubLayer);
14276
14488
  //#endregion
14277
14489
  //#region src/atomicWrite.ts
@@ -14395,7 +14607,7 @@ function stripDefaultServerSettings(current, defaults) {
14395
14607
  }
14396
14608
  return Object.is(current, defaults) ? void 0 : current;
14397
14609
  }
14398
- const make$62 = Effect.gen(function* () {
14610
+ const make$63 = Effect.gen(function* () {
14399
14611
  const { settingsPath } = yield* ServerConfig$1;
14400
14612
  const fs = yield* FileSystem.FileSystem;
14401
14613
  const pathService = yield* Path.Path;
@@ -14616,7 +14828,7 @@ const make$62 = Effect.gen(function* () {
14616
14828
  }
14617
14829
  };
14618
14830
  });
14619
- const layer$57 = Layer.effect(ServerSettingsService, make$62);
14831
+ const layer$58 = Layer.effect(ServerSettingsService, make$63);
14620
14832
  //#endregion
14621
14833
  //#region src/pathExpansion.ts
14622
14834
  /**
@@ -14638,6 +14850,86 @@ function expandHomePath$3(value) {
14638
14850
  return value;
14639
14851
  }
14640
14852
  //#endregion
14853
+ //#region src/provider/skillOverrides.ts
14854
+ /**
14855
+ * Turning "this skill is disabled" into something a provider enforces.
14856
+ *
14857
+ * **A skill has two names and either can reach it.** Discovery reports the
14858
+ * `SKILL.md` frontmatter `name` when there is one and the directory name
14859
+ * otherwise, while the sync panel lists directories — so the disabled set can
14860
+ * hold either form depending on which surface the toggle was pressed on, and
14861
+ * the CLI keys its own listing on the name it resolved. Matching on one form
14862
+ * only produces a toggle that works for most skills and silently does nothing
14863
+ * for the ones whose frontmatter disagrees with their directory, which is the
14864
+ * worst of the available failures: it looks like the feature works.
14865
+ *
14866
+ * So both forms are matched on the way in and both are emitted on the way out.
14867
+ *
14868
+ * **The lever is `Settings.skillOverrides`, not the SDK's `skills` option.**
14869
+ * `skills: string[]` is an allowlist: passing it enables *only* the listed
14870
+ * skills, so a single disabled skill would silently take every plugin-provided
14871
+ * skill with it, since those are not in our discovery at all. `skillOverrides`
14872
+ * is per skill and additive — an absent key means "on" — so nothing is affected
14873
+ * except what was named.
14874
+ *
14875
+ * @module provider/skillOverrides
14876
+ */
14877
+ /**
14878
+ * Hidden from the model's listing *and* from `/name`.
14879
+ *
14880
+ * The other values the setting accepts are partial hides (`name-only`,
14881
+ * `user-invocable-only`); a p4code toggle labelled "disabled" has to be the
14882
+ * complete one, or the label is a lie.
14883
+ */
14884
+ const SKILL_OVERRIDE_OFF = "off";
14885
+ /**
14886
+ * The directory a skill lives in, from its `SKILL.md` path.
14887
+ *
14888
+ * Path handling by hand rather than through `Path.Path`, because this stays a
14889
+ * pure function: it is called from discovery, from the adapter and from tests,
14890
+ * and the segment separator is the only platform fact it needs.
14891
+ */
14892
+ function skillDirectoryName(skillPath) {
14893
+ const segments = skillPath.split(/[/\\]/).filter((segment) => segment.length > 0);
14894
+ return segments.length >= 2 ? segments[segments.length - 2] ?? "" : "";
14895
+ }
14896
+ /** Every name a skill answers to, ignoring empties. */
14897
+ function namesOf(skill) {
14898
+ const directoryName = skillDirectoryName(skill.path);
14899
+ return [skill.name, directoryName].filter((name) => name.length > 0);
14900
+ }
14901
+ /**
14902
+ * Whether a discovered skill is in the disabled set, under either of its names.
14903
+ */
14904
+ function isSkillDisabled(skill, disabled) {
14905
+ const disabledSet = new Set(disabled);
14906
+ return namesOf(skill).some((name) => disabledSet.has(name));
14907
+ }
14908
+ /**
14909
+ * The `skillOverrides` map to hand a session, or `undefined` when nothing is
14910
+ * disabled.
14911
+ *
14912
+ * `undefined` rather than `{}` on purpose: an empty map is a setting the
14913
+ * session did not have before, and the point of this feature is that a server
14914
+ * with nothing disabled starts sessions byte-identical to the ones it started
14915
+ * yesterday.
14916
+ *
14917
+ * Names that match no discovered skill are still emitted. A skill can be
14918
+ * disabled and then removed from disk, or disabled in a project the current
14919
+ * session is not in, and dropping the entry would quietly re-enable it the
14920
+ * moment discovery misses it.
14921
+ */
14922
+ function buildSkillOverrides(disabled, discovered) {
14923
+ if (disabled.length === 0) return;
14924
+ const overrides = {};
14925
+ for (const name of disabled) if (name.length > 0) overrides[name] = SKILL_OVERRIDE_OFF;
14926
+ for (const skill of discovered) {
14927
+ if (!isSkillDisabled(skill, disabled)) continue;
14928
+ for (const name of namesOf(skill)) overrides[name] = SKILL_OVERRIDE_OFF;
14929
+ }
14930
+ return Object.keys(overrides).length === 0 ? void 0 : overrides;
14931
+ }
14932
+ //#endregion
14641
14933
  //#region src/provider/Drivers/ClaudeSkills.ts
14642
14934
  /**
14643
14935
  * ClaudeSkills — filesystem discovery of Claude Code skills for the `$` picker.
@@ -14709,7 +15001,7 @@ const resolveClaudeUserSkillsDir = Effect.fn("resolveClaudeUserSkillsDir")(funct
14709
15001
  * collisions the project-scoped skill wins, matching Claude Code's
14710
15002
  * most-specific-wins resolution.
14711
15003
  */
14712
- const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config, cwd, environment) {
15004
+ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config, cwd, environment, disabledSkills) {
14713
15005
  const fileSystem = yield* FileSystem.FileSystem;
14714
15006
  const path = yield* Path.Path;
14715
15007
  const configDirPath = yield* resolveClaudeConfigDirPath(config, environment ?? process.env, cwd);
@@ -14734,7 +15026,10 @@ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config
14734
15026
  skillsByName.set(name, {
14735
15027
  name,
14736
15028
  path: skillPath,
14737
- enabled: true,
15029
+ enabled: !isSkillDisabled({
15030
+ name,
15031
+ path: skillPath
15032
+ }, disabledSkills ?? []),
14738
15033
  scope: root.scope,
14739
15034
  ...frontmatter.kind === "parsed" && frontmatter.description ? { description: frontmatter.description } : {}
14740
15035
  });
@@ -14749,7 +15044,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
14749
15044
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
14750
15045
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
14751
15046
  const slotsOf = (registration) => registration.secrets ?? [];
14752
- const make$61 = Effect.gen(function* () {
15047
+ const make$62 = Effect.gen(function* () {
14753
15048
  const config = yield* ServerConfig$1;
14754
15049
  const secrets = yield* ServerSecretStore;
14755
15050
  const fileSystem = yield* FileSystem.FileSystem;
@@ -14861,7 +15156,7 @@ const make$61 = Effect.gen(function* () {
14861
15156
  }).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
14862
15157
  };
14863
15158
  });
14864
- const layer$56 = Layer.effect(McpRegistry, make$61);
15159
+ const layer$57 = Layer.effect(McpRegistry, make$62);
14865
15160
  //#endregion
14866
15161
  //#region src/sync/HubLink.ts
14867
15162
  /**
@@ -14958,7 +15253,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
14958
15253
  shareMode: input.shareMode
14959
15254
  });
14960
15255
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
14961
- const make$60 = Effect.fn("HubLink.make")(function* (environment) {
15256
+ const make$61 = Effect.fn("HubLink.make")(function* (environment) {
14962
15257
  const secrets = yield* ServerSecretStore;
14963
15258
  const env = environment ?? process.env;
14964
15259
  const fromEnv = fromEnvironment(env);
@@ -15024,7 +15319,7 @@ const make$60 = Effect.fn("HubLink.make")(function* (environment) {
15024
15319
  })
15025
15320
  };
15026
15321
  });
15027
- const layer$55 = Layer.effect(HubLink, make$60());
15322
+ const layer$56 = Layer.effect(HubLink, make$61());
15028
15323
  //#endregion
15029
15324
  //#region src/sync/HubAssetClient.ts
15030
15325
  /**
@@ -15039,7 +15334,7 @@ const layer$55 = Layer.effect(HubLink, make$60());
15039
15334
  *
15040
15335
  * @module sync/HubAssetClient
15041
15336
  */
15042
- const HUB_REQUEST_TIMEOUT_MS = 15e3;
15337
+ const HUB_REQUEST_TIMEOUT_MS$1 = 15e3;
15043
15338
  /**
15044
15339
  * The hub could not be reached, or answered something unusable.
15045
15340
  *
@@ -15052,12 +15347,12 @@ var HubAssetConflict = class extends Data.TaggedError("HubAssetConflict") {};
15052
15347
  /** No hub is configured on this server, so there is nothing to talk to. */
15053
15348
  var HubNotConfigured = class extends Data.TaggedError("HubNotConfigured") {};
15054
15349
  const AssetListPage = Schema$1.Struct({ assets: Schema$1.Array(AgentAsset) });
15055
- const ConflictBody = Schema$1.Struct({ current: Schema$1.NullOr(AgentAsset) });
15350
+ const ConflictBody$1 = Schema$1.Struct({ current: Schema$1.NullOr(AgentAsset) });
15056
15351
  const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
15057
- const decodeConflictBody = Schema$1.decodeUnknownEffect(ConflictBody);
15352
+ const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
15058
15353
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
15059
15354
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
15060
- const make$59 = Effect.gen(function* () {
15355
+ const make$60 = Effect.gen(function* () {
15061
15356
  const http = yield* HttpClient.HttpClient;
15062
15357
  const link = yield* HubLink;
15063
15358
  const requireSettings = Effect.gen(function* () {
@@ -15066,7 +15361,7 @@ const make$59 = Effect.gen(function* () {
15066
15361
  return state.settings.value;
15067
15362
  });
15068
15363
  const send = Effect.fn("HubAssetClient.send")(function* (operation, settings, request) {
15069
- return yield* http.execute(request.pipe(HttpClientRequest.setHeader("authorization", `Bearer ${settings.token}`), HttpClientRequest.setHeader("accept", "application/json"))).pipe(Effect.timeout(HUB_REQUEST_TIMEOUT_MS), Effect.mapError((cause) => new HubUnavailable({
15364
+ return yield* http.execute(request.pipe(HttpClientRequest.setHeader("authorization", `Bearer ${settings.token}`), HttpClientRequest.setHeader("accept", "application/json"))).pipe(Effect.timeout(HUB_REQUEST_TIMEOUT_MS$1), Effect.mapError((cause) => new HubUnavailable({
15070
15365
  operation,
15071
15366
  detail: `${cause._tag ?? "error"}`
15072
15367
  })));
@@ -15086,7 +15381,7 @@ const make$59 = Effect.gen(function* () {
15086
15381
  * which is what lets a caller show both sides instead of only saying no.
15087
15382
  */
15088
15383
  const conflictFrom = Effect.fn("HubAssetClient.conflictFrom")(function* (name, response) {
15089
- const body = yield* decodeBody("conflict", decodeConflictBody, response).pipe(Effect.orElseSucceed(() => ({ current: null })));
15384
+ const body = yield* decodeBody("conflict", decodeConflictBody$1, response).pipe(Effect.orElseSucceed(() => ({ current: null })));
15090
15385
  return new HubAssetConflict({
15091
15386
  name,
15092
15387
  current: body.current === null ? Option.none() : Option.some(body.current)
@@ -15139,7 +15434,7 @@ const make$59 = Effect.gen(function* () {
15139
15434
  remove
15140
15435
  };
15141
15436
  });
15142
- const layer$54 = Layer.effect(HubAssetClient, make$59);
15437
+ const layer$55 = Layer.effect(HubAssetClient, make$60);
15143
15438
  //#endregion
15144
15439
  //#region src/sync/mcpRegistrationFiles.ts
15145
15440
  /**
@@ -15878,7 +16173,7 @@ const EMPTY_REPORT = {
15878
16173
  unavailable: null
15879
16174
  };
15880
16175
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
15881
- const make$58 = Effect.gen(function* () {
16176
+ const make$59 = Effect.gen(function* () {
15882
16177
  const client = yield* HubAssetClient;
15883
16178
  const link = yield* HubLink;
15884
16179
  const settingsStore = yield* ServerSettingsService;
@@ -16575,10 +16870,10 @@ const make$58 = Effect.gen(function* () {
16575
16870
  removeLocal
16576
16871
  };
16577
16872
  });
16578
- const layer$53 = Layer.effect(AssetSync, make$58);
16873
+ const layer$54 = Layer.effect(AssetSync, make$59);
16579
16874
  //#endregion
16580
16875
  //#region src/cli/assets.ts
16581
- const SyncServicesLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(layer$64), Layer.provideMerge(FetchHttpClient.layer));
16876
+ const SyncServicesLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$58.pipe(Layer.provide(layer$65))), Layer.provideMerge(layer$65), Layer.provideMerge(FetchHttpClient.layer));
16582
16877
  const runWithSync = (flags, run) => Effect.gen(function* () {
16583
16878
  const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
16584
16879
  return yield* Effect.gen(function* () {
@@ -16586,7 +16881,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
16586
16881
  sync: yield* AssetSync,
16587
16882
  link: yield* HubLink
16588
16883
  });
16589
- }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
16884
+ }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$68(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
16590
16885
  });
16591
16886
  const STATE_LABEL = {
16592
16887
  synced: "synced",
@@ -21801,12 +22096,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
21801
22096
  stderrTruncated: stderr.truncated
21802
22097
  };
21803
22098
  });
21804
- const make$57 = Effect.fn("ProcessRunner.make")(function* () {
22099
+ const make$58 = Effect.fn("ProcessRunner.make")(function* () {
21805
22100
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
21806
22101
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
21807
22102
  return ProcessRunner.of({ run });
21808
22103
  });
21809
- const layer$52 = Layer.effect(ProcessRunner, make$57());
22104
+ const layer$53 = Layer.effect(ProcessRunner, make$58());
21810
22105
  //#endregion
21811
22106
  //#region src/project/RepositoryIdentityResolver.ts
21812
22107
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -21897,7 +22192,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
21897
22192
  rootPath: cacheKey
21898
22193
  }) : null;
21899
22194
  });
21900
- const make$56 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
22195
+ const make$57 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
21901
22196
  const processRunner = yield* ProcessRunner;
21902
22197
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
21903
22198
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -21912,7 +22207,7 @@ const make$56 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
21912
22207
  });
21913
22208
  return RepositoryIdentityResolver.of({ resolve });
21914
22209
  });
21915
- const layer$51 = Layer.effect(RepositoryIdentityResolver, make$56()).pipe(Layer.provide(layer$52));
22210
+ const layer$52 = Layer.effect(RepositoryIdentityResolver, make$57()).pipe(Layer.provide(layer$53));
21916
22211
  //#endregion
21917
22212
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
21918
22213
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -23667,7 +23962,7 @@ function mergeWithDefaultKeybindings(custom) {
23667
23962
  * Keybindings - Service tag for keybinding configuration operations.
23668
23963
  */
23669
23964
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
23670
- const make$55 = Effect.gen(function* () {
23965
+ const make$56 = Effect.gen(function* () {
23671
23966
  const { keybindingsConfigPath } = yield* ServerConfig$1;
23672
23967
  const fs = yield* FileSystem.FileSystem;
23673
23968
  const path = yield* Path.Path;
@@ -23928,7 +24223,7 @@ const make$55 = Effect.gen(function* () {
23928
24223
  }))
23929
24224
  };
23930
24225
  });
23931
- const layer$50 = Layer.effect(Keybindings, make$55);
24226
+ const layer$51 = Layer.effect(Keybindings, make$56);
23932
24227
  //#endregion
23933
24228
  //#region src/process/externalLauncher.ts
23934
24229
  /**
@@ -24155,7 +24450,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
24155
24450
  cause
24156
24451
  }));
24157
24452
  });
24158
- const make$54 = Effect.gen(function* () {
24453
+ const make$55 = Effect.gen(function* () {
24159
24454
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
24160
24455
  const fileSystem = yield* FileSystem.FileSystem;
24161
24456
  const path = yield* Path.Path;
@@ -24166,7 +24461,7 @@ const make$54 = Effect.gen(function* () {
24166
24461
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
24167
24462
  });
24168
24463
  });
24169
- const layer$49 = Layer.effect(ExternalLauncher, make$54);
24464
+ const layer$50 = Layer.effect(ExternalLauncher, make$55);
24170
24465
  //#endregion
24171
24466
  //#region src/orchestration/Services/OrchestrationReactor.ts
24172
24467
  /**
@@ -24184,7 +24479,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
24184
24479
  //#endregion
24185
24480
  //#region src/serverLifecycleEvents.ts
24186
24481
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
24187
- const make$53 = Effect.gen(function* () {
24482
+ const make$54 = Effect.gen(function* () {
24188
24483
  const pubsub = yield* PubSub.unbounded();
24189
24484
  const state = yield* Ref.make({
24190
24485
  sequence: 0,
@@ -24208,7 +24503,7 @@ const make$53 = Effect.gen(function* () {
24208
24503
  }
24209
24504
  };
24210
24505
  });
24211
- const layer$48 = Layer.effect(ServerLifecycleEvents, make$53);
24506
+ const layer$49 = Layer.effect(ServerLifecycleEvents, make$54);
24212
24507
  //#endregion
24213
24508
  //#region src/telemetry/Identify.ts
24214
24509
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -24381,7 +24676,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
24381
24676
  /** No-op layer for callers that intentionally disable telemetry. */
24382
24677
  static layerTest = Layer.succeed(AnalyticsService, inert);
24383
24678
  };
24384
- const make$52 = Effect.gen(function* () {
24679
+ const make$53 = Effect.gen(function* () {
24385
24680
  const telemetryConfig = yield* TelemetryEnvConfig;
24386
24681
  const posthogKey = telemetryConfig.posthogKey.trim();
24387
24682
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -24451,7 +24746,7 @@ const make$52 = Effect.gen(function* () {
24451
24746
  flush
24452
24747
  });
24453
24748
  });
24454
- const layer$47 = Layer.effect(AnalyticsService, make$52);
24749
+ const layer$48 = Layer.effect(AnalyticsService, make$53);
24455
24750
  AnalyticsService.layerTest;
24456
24751
  //#endregion
24457
24752
  //#region src/service/pinnedRuntime.ts
@@ -24798,7 +25093,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
24798
25093
  }
24799
25094
  };
24800
25095
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
24801
- const make$51 = Effect.fn("cloud.boot_service.make")(function* (input) {
25096
+ const make$52 = Effect.fn("cloud.boot_service.make")(function* (input) {
24802
25097
  const hostExecPath = yield* HostProcessExecutablePath;
24803
25098
  const hostArguments = yield* HostProcessArguments;
24804
25099
  const host = input.host ?? {
@@ -25017,7 +25312,7 @@ const make$51 = Effect.fn("cloud.boot_service.make")(function* (input) {
25017
25312
  logPath
25018
25313
  });
25019
25314
  });
25020
- const layer$46 = (input) => Layer.effect(BootService, make$51(input));
25315
+ const layer$47 = (input) => Layer.effect(BootService, make$52(input));
25021
25316
  //#endregion
25022
25317
  //#region src/service/selfUpdate.ts
25023
25318
  /**
@@ -25092,7 +25387,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
25092
25387
  return null;
25093
25388
  });
25094
25389
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
25095
- const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
25390
+ const make$51 = Effect.fn("cloud.server_self_update.make")(function* (options) {
25096
25391
  const serverConfig = yield* ServerConfig$1;
25097
25392
  const fs = yield* FileSystem.FileSystem;
25098
25393
  const path = yield* Path.Path;
@@ -25242,7 +25537,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
25242
25537
  });
25243
25538
  return ServerSelfUpdate.of({ update });
25244
25539
  });
25245
- const layer$45 = Layer.effect(ServerSelfUpdate, make$50()).pipe(Layer.provide(layer$52));
25540
+ const layer$46 = Layer.effect(ServerSelfUpdate, make$51()).pipe(Layer.provide(layer$53));
25246
25541
  //#endregion
25247
25542
  //#region src/environment/ServerEnvironmentLabel.ts
25248
25543
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -25374,7 +25669,7 @@ function platformArch(architecture) {
25374
25669
  default: return "other";
25375
25670
  }
25376
25671
  }
25377
- const make$49 = Effect.gen(function* () {
25672
+ const make$50 = Effect.gen(function* () {
25378
25673
  const fileSystem = yield* FileSystem.FileSystem;
25379
25674
  const path = yield* Path.Path;
25380
25675
  const serverConfig = yield* ServerConfig$1;
@@ -25437,7 +25732,7 @@ const make$49 = Effect.gen(function* () {
25437
25732
  * state. It intentionally has no fallback Layer.succeed value: callers must
25438
25733
  * provide the external platform services and a ServerConfig.
25439
25734
  */
25440
- const layer$44 = Layer.effect(ServerEnvironment, make$49).pipe(Layer.provide(layer$52));
25735
+ const layer$45 = Layer.effect(ServerEnvironment, make$50).pipe(Layer.provide(layer$53));
25441
25736
  //#endregion
25442
25737
  //#region src/provider/Services/ProviderSessionReaper.ts
25443
25738
  var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
@@ -26513,7 +26808,7 @@ const maybeOpenBrowser = (target) => Effect.gen(function* () {
26513
26808
  yield* (yield* ExternalLauncher).launchBrowser(target).pipe(Effect.catch(() => Effect.logInfo("browser auto-open unavailable", { hint: `Open ${target} in your browser.` })));
26514
26809
  });
26515
26810
  const runStartupPhase = (phase, effect) => effect.pipe(Effect.annotateSpans({ "startup.phase": phase }), Effect.withSpan(`server.startup.${phase}`));
26516
- const make$48 = Effect.gen(function* () {
26811
+ const make$49 = Effect.gen(function* () {
26517
26812
  const serverConfig = yield* ServerConfig$1;
26518
26813
  const keybindings = yield* Keybindings;
26519
26814
  const orchestrationReactor = yield* OrchestrationReactor;
@@ -26654,7 +26949,7 @@ const make$48 = Effect.gen(function* () {
26654
26949
  enqueueCommand: commandGate.enqueueCommand
26655
26950
  };
26656
26951
  });
26657
- const layer$43 = Layer.effect(ServerRuntimeStartup, make$48);
26952
+ const layer$44 = Layer.effect(ServerRuntimeStartup, make$49);
26658
26953
  //#endregion
26659
26954
  //#region src/serverRuntimeState.ts
26660
26955
  const PersistedServerRuntimeState = Schema$1.Struct({
@@ -26802,7 +27097,7 @@ function expandHomePath$2(input, path) {
26802
27097
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
26803
27098
  return input;
26804
27099
  }
26805
- const make$47 = Effect.gen(function* () {
27100
+ const make$48 = Effect.gen(function* () {
26806
27101
  const fileSystem = yield* FileSystem.FileSystem;
26807
27102
  const path = yield* Path.Path;
26808
27103
  const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
@@ -26859,7 +27154,7 @@ const make$47 = Effect.gen(function* () {
26859
27154
  resolveRelativePathWithinRoot
26860
27155
  });
26861
27156
  });
26862
- const layer$42 = Layer.effect(WorkspacePaths, make$47);
27157
+ const layer$43 = Layer.effect(WorkspacePaths, make$48);
26863
27158
  //#endregion
26864
27159
  //#region src/cli/project.ts
26865
27160
  const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
@@ -26965,7 +27260,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
26965
27260
  operation: "generateProjectCommandId",
26966
27261
  cause
26967
27262
  })));
26968
- const ProjectCliRuntimeLive = Layer.mergeAll(layer$42, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$51), Layer.provideMerge(layerConfig)));
27263
+ const ProjectCliRuntimeLive = Layer.mergeAll(layer$43, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$52), Layer.provideMerge(layerConfig)));
26969
27264
  const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
26970
27265
  const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
26971
27266
  scopes: AuthAdministrativeScopes,
@@ -27056,7 +27351,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
27056
27351
  });
27057
27352
  yield* Console.log(output);
27058
27353
  }));
27059
- const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
27354
+ const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$68(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
27060
27355
  return yield* Effect.gen(function* () {
27061
27356
  const snapshot = yield* getOfflineSnapshot();
27062
27357
  const orchestrationEngine = yield* OrchestrationEngineService;
@@ -27067,7 +27362,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
27067
27362
  });
27068
27363
  yield* Console.log(output);
27069
27364
  }).pipe(Effect.provide(offlineRuntimeLayer));
27070
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$42).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
27365
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$43).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$68(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
27071
27366
  });
27072
27367
  const projectAddCommand = Command.make("add", {
27073
27368
  ...projectLocationFlags,
@@ -27674,7 +27969,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
27674
27969
  filePath: error.filePath,
27675
27970
  errorTag: error._tag
27676
27971
  }));
27677
- const make$46 = Effect.gen(function* () {
27972
+ const make$47 = Effect.gen(function* () {
27678
27973
  const fileSystem = yield* FileSystem.FileSystem;
27679
27974
  const path = yield* Path.Path;
27680
27975
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -27695,7 +27990,7 @@ const make$46 = Effect.gen(function* () {
27695
27990
  });
27696
27991
  return P4ProjectFileLoader.of({ load });
27697
27992
  });
27698
- const layer$41 = Layer.effect(P4ProjectFileLoader, make$46);
27993
+ const layer$42 = Layer.effect(P4ProjectFileLoader, make$47);
27699
27994
  //#endregion
27700
27995
  //#region src/project/ProjectFaviconResolver.ts
27701
27996
  /**
@@ -27766,7 +28061,7 @@ function extractIconHref(source) {
27766
28061
  return null;
27767
28062
  }
27768
28063
  const optionOnNotFound$1 = (effect) => effect.pipe(Effect.map(Option.some), Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error) }));
27769
- const make$45 = Effect.gen(function* () {
28064
+ const make$46 = Effect.gen(function* () {
27770
28065
  const fileSystem = yield* FileSystem.FileSystem;
27771
28066
  const path = yield* Path.Path;
27772
28067
  const workspacePaths = yield* WorkspacePaths;
@@ -27835,7 +28130,7 @@ const make$45 = Effect.gen(function* () {
27835
28130
  });
27836
28131
  return ProjectFaviconResolver.of({ resolvePath });
27837
28132
  });
27838
- const layer$40 = Layer.effect(ProjectFaviconResolver, make$45);
28133
+ const layer$41 = Layer.effect(ProjectFaviconResolver, make$46);
27839
28134
  //#endregion
27840
28135
  //#region src/assets/AssetAccess.ts
27841
28136
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -28088,10 +28383,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
28088
28383
  //#endregion
28089
28384
  //#region src/observability/BrowserTraceCollector.ts
28090
28385
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
28091
- const make$44 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
28386
+ const make$45 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
28092
28387
  for (const record of records) sink.push(record);
28093
28388
  }) });
28094
- const layer$39 = (sink) => Layer.succeed(BrowserTraceCollector, make$44(sink));
28389
+ const layer$40 = (sink) => Layer.succeed(BrowserTraceCollector, make$45(sink));
28095
28390
  //#endregion
28096
28391
  //#region src/auth/http.ts
28097
28392
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -30305,7 +30600,7 @@ const classifyNonZeroExit = (command, stderr) => {
30305
30600
  if (command === "gh" && (normalized.includes("could not resolve to a pullrequest") || normalized.includes("repository.pullrequest") || normalized.includes("no pull requests found for branch") || normalized.includes("pull request not found")) || command === "glab" && (normalized.includes("merge request not found") || normalized.includes("not found") || normalized.includes("404")) || command === "az" && normalized.includes("pull request") && (normalized.includes("not found") || normalized.includes("does not exist"))) return "not-found";
30306
30601
  return "command-failed";
30307
30602
  };
30308
- const make$43 = Effect.gen(function* () {
30603
+ const make$44 = Effect.gen(function* () {
30309
30604
  const processRunner = yield* ProcessRunner;
30310
30605
  const run = Effect.fn("VcsProcess.run")(function* (input) {
30311
30606
  const baseError = {
@@ -30362,7 +30657,7 @@ const make$43 = Effect.gen(function* () {
30362
30657
  });
30363
30658
  return VcsProcess.of({ run });
30364
30659
  });
30365
- const layer$38 = Layer.effect(VcsProcess, make$43).pipe(Layer.provide(layer$52));
30660
+ const layer$39 = Layer.effect(VcsProcess, make$44).pipe(Layer.provide(layer$53));
30366
30661
  //#endregion
30367
30662
  //#region src/vcs/VcsDriver.ts
30368
30663
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -30813,12 +31108,12 @@ const makeVcsDriver = Effect.gen(function* () {
30813
31108
  const driver = yield* makeVcsDriverShape();
30814
31109
  return VcsDriver.of(driver);
30815
31110
  });
30816
- const make$42 = Effect.gen(function* () {
31111
+ const make$43 = Effect.gen(function* () {
30817
31112
  const git = yield* makeGitVcsDriverCore();
30818
31113
  return GitVcsDriver.of(git);
30819
31114
  });
30820
31115
  Layer.effect(VcsDriver, makeVcsDriver);
30821
- const layer$37 = Layer.effect(GitVcsDriver, make$42);
31116
+ const layer$38 = Layer.effect(GitVcsDriver, make$43);
30822
31117
  //#endregion
30823
31118
  //#region src/vcs/VcsProjectConfig.ts
30824
31119
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -30850,7 +31145,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
30850
31145
  configPath: error.configPath,
30851
31146
  errorTag: error._tag
30852
31147
  }));
30853
- const make$41 = Effect.gen(function* () {
31148
+ const make$42 = Effect.gen(function* () {
30854
31149
  const fileSystem = yield* FileSystem.FileSystem;
30855
31150
  const path = yield* Path.Path;
30856
31151
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -30891,7 +31186,7 @@ const make$41 = Effect.gen(function* () {
30891
31186
  });
30892
31187
  return VcsProjectConfig.of({ resolveKind });
30893
31188
  });
30894
- const layer$36 = Layer.effect(VcsProjectConfig, make$41);
31189
+ const layer$37 = Layer.effect(VcsProjectConfig, make$42);
30895
31190
  //#endregion
30896
31191
  //#region src/vcs/VcsDriverRegistry.ts
30897
31192
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -30911,7 +31206,7 @@ function parseDetectionCacheKey(key) {
30911
31206
  cwd: key.slice(separatorIndex + 1)
30912
31207
  };
30913
31208
  }
30914
- const make$40 = Effect.gen(function* () {
31209
+ const make$41 = Effect.gen(function* () {
30915
31210
  const projectConfig = yield* VcsProjectConfig;
30916
31211
  const git = yield* makeVcsDriver;
30917
31212
  const drivers = { git };
@@ -30968,7 +31263,7 @@ const make$40 = Effect.gen(function* () {
30968
31263
  resolve
30969
31264
  });
30970
31265
  });
30971
- const layer$35 = Layer.effect(VcsDriverRegistry, make$40).pipe(Layer.provide(layer$36));
31266
+ const layer$36 = Layer.effect(VcsDriverRegistry, make$41).pipe(Layer.provide(layer$37));
30972
31267
  //#endregion
30973
31268
  //#region src/checkpointing/CheckpointStore.ts
30974
31269
  /**
@@ -30988,7 +31283,7 @@ const layer$35 = Layer.effect(VcsDriverRegistry, make$40).pipe(Layer.provide(lay
30988
31283
  */
30989
31284
  /** Service tag for checkpoint persistence and restore operations. */
30990
31285
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
30991
- const make$39 = Effect.gen(function* () {
31286
+ const make$40 = Effect.gen(function* () {
30992
31287
  const vcsRegistry = yield* VcsDriverRegistry;
30993
31288
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
30994
31289
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -31027,7 +31322,7 @@ const make$39 = Effect.gen(function* () {
31027
31322
  deleteCheckpointRefs
31028
31323
  });
31029
31324
  });
31030
- const layer$34 = Layer.effect(CheckpointStore, make$39);
31325
+ const layer$35 = Layer.effect(CheckpointStore, make$40);
31031
31326
  //#endregion
31032
31327
  //#region src/checkpointing/CheckpointDiffQuery.ts
31033
31328
  /**
@@ -31049,7 +31344,7 @@ function buildTurnDiffResult(input, diff) {
31049
31344
  diff
31050
31345
  };
31051
31346
  }
31052
- const make$38 = Effect.gen(function* () {
31347
+ const make$39 = Effect.gen(function* () {
31053
31348
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
31054
31349
  const checkpointStore = yield* CheckpointStore;
31055
31350
  const getTurnDiff = Effect.fn("getTurnDiff")(function* (input) {
@@ -31185,7 +31480,7 @@ const make$38 = Effect.gen(function* () {
31185
31480
  getFullThreadDiff
31186
31481
  });
31187
31482
  });
31188
- const layer$33 = Layer.effect(CheckpointDiffQuery, make$38);
31483
+ const layer$34 = Layer.effect(CheckpointDiffQuery, make$39);
31189
31484
  //#endregion
31190
31485
  //#region src/orchestration/ActivityPayloadProjection.ts
31191
31486
  function asRecord$1(value) {
@@ -32038,7 +32333,7 @@ function makeUpdateState(input) {
32038
32333
  output: input.output ?? null
32039
32334
  };
32040
32335
  }
32041
- const make$37 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
32336
+ const make$38 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
32042
32337
  const providerRegistry = yield* ProviderRegistry;
32043
32338
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
32044
32339
  const httpClient = yield* HttpClient.HttpClient;
@@ -32153,7 +32448,7 @@ const make$37 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
32153
32448
  });
32154
32449
  return ProviderMaintenanceRunner.of({ updateProvider });
32155
32450
  });
32156
- const layer$32 = Layer.effect(ProviderMaintenanceRunner, make$37());
32451
+ const layer$33 = Layer.effect(ProviderMaintenanceRunner, make$38());
32157
32452
  //#endregion
32158
32453
  //#region src/provider/Services/ProviderInstanceRegistry.ts
32159
32454
  var ProviderInstanceRegistry = class extends Context.Service()("@p4code/cli/provider/Services/ProviderInstanceRegistry") {};
@@ -32177,11 +32472,228 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
32177
32472
  detail: "This provider does not report account usage."
32178
32473
  }))))
32179
32474
  });
32180
- const make$36 = Effect.gen(function* () {
32475
+ const make$37 = Effect.gen(function* () {
32181
32476
  const registry = yield* ProviderInstanceRegistry;
32182
32477
  return makeTextGenerationFromRegistry(registry);
32183
32478
  });
32184
- const layer$31 = Layer.effect(TextGeneration, make$36);
32479
+ const layer$32 = Layer.effect(TextGeneration, make$37);
32480
+ const SKILL_MANIFEST_FILENAME = "SKILL.md";
32481
+ /**
32482
+ * Split a catalogue id (`owner/repo/skill-name`) into its parts.
32483
+ *
32484
+ * The skill segment is everything after the repository, because a nested skill
32485
+ * id contains slashes and rejoining the tail is the only reading that survives
32486
+ * one.
32487
+ */
32488
+ function parseRegistrySkillId(id) {
32489
+ const segments = id.split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
32490
+ if (segments.length < 3) return null;
32491
+ const [owner, repo, ...rest] = segments;
32492
+ if (!owner || !repo || rest.length === 0) return null;
32493
+ return {
32494
+ owner,
32495
+ repo,
32496
+ skillId: rest.join("/")
32497
+ };
32498
+ }
32499
+ /**
32500
+ * Where the skill lives in the repository, from the repository's file list.
32501
+ *
32502
+ * Matched on `<...>/<skillId>/SKILL.md` rather than on any fixed layout,
32503
+ * because repositories disagree: some keep skills at the root, some under
32504
+ * `skills/`, some several levels down inside a plugin. The shallowest match
32505
+ * wins, which is what picks the skill itself over a copy vendored inside an
32506
+ * example or a test fixture.
32507
+ */
32508
+ function chooseSkillRoot(entries, skillId) {
32509
+ const leaf = skillId.split("/").pop() ?? skillId;
32510
+ const candidates = [];
32511
+ for (const entry of entries) {
32512
+ if (entry.type !== "blob" || !entry.path.endsWith(`/SKILL.md`)) {
32513
+ if (entry.type === "blob" && entry.path === "SKILL.md") candidates.push("");
32514
+ continue;
32515
+ }
32516
+ const root = entry.path.slice(0, -9);
32517
+ if ((root.split("/").pop() ?? root) === leaf || root === skillId || root.endsWith(`/${skillId}`)) candidates.push(root);
32518
+ }
32519
+ if (candidates.length === 0) return null;
32520
+ candidates.sort((left, right) => {
32521
+ const depth = left.split("/").length - right.split("/").length;
32522
+ return depth === 0 ? left.localeCompare(right) : depth;
32523
+ });
32524
+ return candidates[0] ?? null;
32525
+ }
32526
+ /**
32527
+ * The files under a skill root, bounded and with the manifest first.
32528
+ *
32529
+ * `SKILL.md` leads because it is the one file that must arrive: everything
32530
+ * downstream — the local create refusal, the panel preview, discovery — keys on
32531
+ * it, so if a bound is going to drop something it must not be that.
32532
+ */
32533
+ function selectSkillFiles(entries, root) {
32534
+ const prefix = root === "" ? "" : `${root}/`;
32535
+ const manifestPath = `${prefix}${SKILL_MANIFEST_FILENAME}`;
32536
+ const inRoot = entries.filter((entry) => entry.type === "blob" && entry.path.startsWith(prefix) && entry.path !== `${prefix}`);
32537
+ const ordered = [...inRoot.filter((entry) => entry.path === manifestPath), ...inRoot.filter((entry) => entry.path !== manifestPath).sort((left, right) => left.path.localeCompare(right.path))];
32538
+ const paths = [];
32539
+ const skipped = [];
32540
+ let totalBytes = 0;
32541
+ for (const entry of ordered) {
32542
+ if (entry.size !== void 0 && entry.size > 262144) {
32543
+ skipped.push({
32544
+ path: entry.path,
32545
+ reason: "too-large"
32546
+ });
32547
+ continue;
32548
+ }
32549
+ if (paths.length >= 40) {
32550
+ skipped.push({
32551
+ path: entry.path,
32552
+ reason: "too-many-files"
32553
+ });
32554
+ continue;
32555
+ }
32556
+ const size = entry.size ?? 0;
32557
+ if (totalBytes + size > 1048576) {
32558
+ skipped.push({
32559
+ path: entry.path,
32560
+ reason: "over-total-size"
32561
+ });
32562
+ continue;
32563
+ }
32564
+ totalBytes += size;
32565
+ paths.push(entry.path);
32566
+ }
32567
+ return {
32568
+ paths,
32569
+ skipped
32570
+ };
32571
+ }
32572
+ /**
32573
+ * The asset-relative path of a repository file, for writing on disk.
32574
+ *
32575
+ * A skill asset's files are named relative to its own directory, so the
32576
+ * repository prefix comes off here rather than at the call site — the sync's
32577
+ * file shape and the registry's are the same shape, and only this line knows
32578
+ * why they differ.
32579
+ */
32580
+ function toAssetFilePath(repositoryPath, root) {
32581
+ return root === "" ? repositoryPath : repositoryPath.slice(root.length + 1);
32582
+ }
32583
+ //#endregion
32584
+ //#region src/skills/SkillRegistry.ts
32585
+ const REGISTRY_SEARCH_URL = "https://www.skills.sh/api/search";
32586
+ const GITHUB_API_URL = "https://api.github.com";
32587
+ const GITHUB_RAW_URL = "https://raw.githubusercontent.com";
32588
+ const REQUEST_TIMEOUT_MS = 15e3;
32589
+ /** More than a person reads, few enough that the panel is a list and not a feed. */
32590
+ const MAX_SEARCH_RESULTS = 25;
32591
+ /**
32592
+ * The catalogue's search response, decoded loosely on purpose.
32593
+ *
32594
+ * Only the four fields we display are required. This is an undocumented public
32595
+ * endpoint on someone else's service; a field appearing or disappearing beside
32596
+ * these should not turn search into an error.
32597
+ */
32598
+ const SearchResponse = Schema$1.Struct({ skills: Schema$1.Array(Schema$1.Struct({
32599
+ id: Schema$1.String,
32600
+ name: Schema$1.String,
32601
+ source: Schema$1.String,
32602
+ installs: Schema$1.optional(Schema$1.Number)
32603
+ })) });
32604
+ const decodeSearchResponse = Schema$1.decodeUnknownEffect(SearchResponse);
32605
+ const TreeResponse = Schema$1.Struct({
32606
+ tree: Schema$1.Array(Schema$1.Struct({
32607
+ path: Schema$1.String,
32608
+ type: Schema$1.String,
32609
+ size: Schema$1.optional(Schema$1.Number)
32610
+ })),
32611
+ truncated: Schema$1.optional(Schema$1.Boolean)
32612
+ });
32613
+ const decodeTreeResponse = Schema$1.decodeUnknownEffect(TreeResponse);
32614
+ var SkillRegistry = class extends Context.Service()("@p4code/cli/skills/SkillRegistry") {};
32615
+ const emptyFetch = (id, unavailable) => ({
32616
+ found: false,
32617
+ id,
32618
+ name: "",
32619
+ source: "",
32620
+ sourceUrl: "",
32621
+ files: [],
32622
+ skipped: [],
32623
+ unavailable
32624
+ });
32625
+ const make$36 = Effect.gen(function* () {
32626
+ const http = yield* HttpClient.HttpClient;
32627
+ const request = Effect.fn("SkillRegistry.request")(function* (url) {
32628
+ return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
32629
+ });
32630
+ const search = (query) => Effect.gen(function* () {
32631
+ const response = yield* request(`${REGISTRY_SEARCH_URL}?q=${encodeURIComponent(query)}`).pipe(Effect.orElseSucceed(() => null));
32632
+ if (response === null) return {
32633
+ results: [],
32634
+ unavailable: "The skill catalogue could not be reached."
32635
+ };
32636
+ if (response.status !== 200) return {
32637
+ results: [],
32638
+ unavailable: `The skill catalogue answered ${response.status}.`
32639
+ };
32640
+ const body = yield* response.json.pipe(Effect.flatMap(decodeSearchResponse), Effect.orElseSucceed(() => null));
32641
+ if (body === null) return {
32642
+ results: [],
32643
+ unavailable: "The skill catalogue answered something unreadable."
32644
+ };
32645
+ return {
32646
+ results: body.skills.slice(0, MAX_SEARCH_RESULTS).map((skill) => ({
32647
+ id: skill.id,
32648
+ name: skill.name,
32649
+ source: skill.source,
32650
+ installs: Math.trunc(skill.installs ?? 0)
32651
+ })),
32652
+ unavailable: null
32653
+ };
32654
+ });
32655
+ const fetch = (id) => Effect.gen(function* () {
32656
+ const ref = parseRegistrySkillId(id);
32657
+ if (ref === null) return emptyFetch(id, "Expected owner/repo/skill-name.");
32658
+ const repository = `${ref.owner}/${ref.repo}`;
32659
+ const treeResponse = yield* request(`${GITHUB_API_URL}/repos/${repository}/git/trees/HEAD?recursive=1`).pipe(Effect.orElseSucceed(() => null));
32660
+ if (treeResponse === null) return emptyFetch(id, `${repository} could not be reached.`);
32661
+ if (treeResponse.status === 403 || treeResponse.status === 429) return emptyFetch(id, "GitHub is rate limiting this machine. Try again later.");
32662
+ if (treeResponse.status !== 200) return emptyFetch(id, `GitHub answered ${treeResponse.status} for ${repository}.`);
32663
+ const tree = yield* treeResponse.json.pipe(Effect.flatMap(decodeTreeResponse), Effect.orElseSucceed(() => null));
32664
+ if (tree === null) return emptyFetch(id, `GitHub answered something unreadable for ${repository}.`);
32665
+ const entries = tree.tree;
32666
+ const root = chooseSkillRoot(entries, ref.skillId);
32667
+ if (root === null) return emptyFetch(id, tree.truncated === true ? `${repository} is too large for GitHub to list in one response, and ${ref.skillId} was not in the part it returned.` : `${repository} has no ${ref.skillId}/SKILL.md.`);
32668
+ const selection = selectSkillFiles(entries, root);
32669
+ const files = [];
32670
+ for (const path of selection.paths) {
32671
+ const fileResponse = yield* request(`${GITHUB_RAW_URL}/${repository}/HEAD/${path.split("/").map(encodeURIComponent).join("/")}`).pipe(Effect.orElseSucceed(() => null));
32672
+ if (fileResponse === null || fileResponse.status !== 200) return emptyFetch(id, `${path} could not be downloaded.`);
32673
+ const content = yield* fileResponse.text.pipe(Effect.orElseSucceed(() => null));
32674
+ if (content === null) return emptyFetch(id, `${path} is not text, so this skill cannot be installed here.`);
32675
+ files.push({
32676
+ path: toAssetFilePath(path, root),
32677
+ content
32678
+ });
32679
+ }
32680
+ return {
32681
+ found: true,
32682
+ id,
32683
+ name: root === "" ? ref.repo : root.split("/").pop() ?? ref.skillId,
32684
+ source: repository,
32685
+ sourceUrl: root === "" ? `https://github.com/${repository}` : `https://github.com/${repository}/tree/HEAD/${root}`,
32686
+ files,
32687
+ skipped: selection.skipped,
32688
+ unavailable: null
32689
+ };
32690
+ });
32691
+ return {
32692
+ search,
32693
+ fetch
32694
+ };
32695
+ });
32696
+ const layer$31 = Layer.effect(SkillRegistry, make$36);
32185
32697
  //#endregion
32186
32698
  //#region ../../packages/shared/src/KeyedCoalescingWorker.ts
32187
32699
  const makeKeyedCoalescingWorker = (options) => Effect.gen(function* () {
@@ -32396,7 +32908,7 @@ const COMMON_DEV_PORTS = Object.freeze([
32396
32908
  8888,
32397
32909
  9e3
32398
32910
  ]);
32399
- const POLL_INTERVAL = Duration.seconds(3);
32911
+ const POLL_INTERVAL$1 = Duration.seconds(3);
32400
32912
  const LSOF_TIMEOUT_MS = 5e3;
32401
32913
  const WINDOWS_LISTENER_TIMEOUT_MS = 5e3;
32402
32914
  const terminalOwnerKey = (owner) => `${owner.threadId}\u0000${owner.terminalId}`;
@@ -32571,7 +33083,7 @@ const make$35 = Effect.gen(function* PortDiscoveryMake() {
32571
33083
  lastSnapshot: next
32572
33084
  }])) yield* broadcast(next);
32573
33085
  }, Effect.catchCause((cause) => Effect.logWarning("preview port scan failed", Cause.pretty(cause))));
32574
- yield* Effect.forkScoped(pollTick().pipe(Effect.repeat(Schedule.spaced(POLL_INTERVAL))));
33086
+ yield* Effect.forkScoped(pollTick().pipe(Effect.repeat(Schedule.spaced(POLL_INTERVAL$1))));
32575
33087
  const acquireRetention = Effect.fn("PortDiscovery.retain")(function* () {
32576
33088
  if (yield* Ref.modify(stateRef, (state) => [state.retainCount === 0, {
32577
33089
  ...state,
@@ -34271,7 +34783,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
34271
34783
  subscribeMetadata
34272
34784
  });
34273
34785
  });
34274
- const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$52));
34786
+ const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$53));
34275
34787
  //#endregion
34276
34788
  //#region src/mcp/McpInvocationContext.ts
34277
34789
  var McpInvocationContext = class extends Context.Service()("@p4code/cli/mcp/McpInvocationContext") {};
@@ -41454,6 +41966,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
41454
41966
  const hubLink = yield* HubLink;
41455
41967
  const assetSync = yield* AssetSync;
41456
41968
  const mcpRegistry = yield* McpRegistry;
41969
+ const skillRegistry = yield* SkillRegistry;
41457
41970
  const startup = yield* ServerRuntimeStartup;
41458
41971
  const workspaceEntries = yield* WorkspaceEntries;
41459
41972
  const workspaceFileSystem = yield* WorkspaceFileSystem;
@@ -42038,6 +42551,8 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
42038
42551
  status: yield* assetSync.status
42039
42552
  };
42040
42553
  }), { "rpc.aggregate": "skills" }),
42554
+ [WS_METHODS.skillRegistrySearch]: ({ query }) => observeRpcEffect$1(WS_METHODS.skillRegistrySearch, skillRegistry.search(query), { "rpc.aggregate": "skills" }),
42555
+ [WS_METHODS.skillRegistryFetch]: ({ id }) => observeRpcEffect$1(WS_METHODS.skillRegistryFetch, skillRegistry.fetch(id), { "rpc.aggregate": "skills" }),
42041
42556
  [WS_METHODS.mcpList]: (_input) => observeRpcEffect$1(WS_METHODS.mcpList, mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))), { "rpc.aggregate": "mcp" }),
42042
42557
  [WS_METHODS.mcpSave]: ({ registration }) => observeRpcEffect$1(WS_METHODS.mcpSave, mcpRegistry.save(registration).pipe(Effect.andThen(mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))))), { "rpc.aggregate": "mcp" }),
42043
42558
  [WS_METHODS.mcpRemove]: ({ name }) => observeRpcEffect$1(WS_METHODS.mcpRemove, mcpRegistry.remove(name).pipe(Effect.andThen(mcpRegistry.list.pipe(Effect.map((servers) => ({ servers }))))), { "rpc.aggregate": "mcp" }),
@@ -42219,7 +42734,7 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
42219
42734
  const serverAuth = yield* EnvironmentAuth;
42220
42735
  const sessions = yield* SessionStore;
42221
42736
  const session = yield* serverAuth.authenticateWebSocketUpgrade(request).pipe(Effect.catchIf(isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid(serverAuthCredentialReason(error))), Effect.catchIf(isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error)));
42222
- const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$32), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(TaskRepository, taskRepository)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(layer$5.pipe(Layer.provide(layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$37), Layer.provide(layer$35.pipe(Layer.provide(layer$36))))), Layer.provide(layer$38))))));
42737
+ const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$33), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(TaskRepository, taskRepository)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(layer$5.pipe(Layer.provide(layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$38), Layer.provide(layer$36.pipe(Layer.provide(layer$37))))), Layer.provide(layer$39))))));
42223
42738
  return yield* Effect.acquireUseRelease(sessions.markConnected(session.sessionId), () => rpcWebSocketHttpEffect, () => sessions.markDisconnected(session.sessionId));
42224
42739
  }).pipe(Effect.catchTags({
42225
42740
  EnvironmentAuthInvalidError: HttpServerRespondable.toResponse,
@@ -42227,6 +42742,281 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
42227
42742
  })));
42228
42743
  }));
42229
42744
  //#endregion
42745
+ //#region src/persistence/Layers/RemoteTasks.ts
42746
+ /**
42747
+ * The board, kept on the hub instead of in this machine's database.
42748
+ *
42749
+ * A drop-in `TaskRepository`: same interface, same events, so the MCP toolkit,
42750
+ * the websocket RPCs and the board itself are unchanged and do not know which
42751
+ * one they are talking to. That was the point of making it an interface.
42752
+ *
42753
+ * **An unreachable hub fails loudly, by decision.** There is no local fallback
42754
+ * and no write queue. A server that answered from a local copy while the hub
42755
+ * was down would recreate exactly the divergence the hub exists to remove —
42756
+ * two Macs with two boards, silently — and the failure would be invisible until
42757
+ * someone noticed a task missing on the other machine. So a hub that cannot be
42758
+ * reached means the board says so and nothing is written. The cost is real and
42759
+ * is the point: no board while the hub is down.
42760
+ *
42761
+ * **Changes are polled, because the hub has no push.** The interface promises
42762
+ * a change stream, and the honest implementation of that against a plain HTTP
42763
+ * API is a poll with a diff. A write made through *this* server publishes
42764
+ * immediately, so the local board is live; the poll is what carries the other
42765
+ * machine's writes, at up to `POLL_INTERVAL` of lag.
42766
+ *
42767
+ * @module persistence/Layers/RemoteTasks
42768
+ */
42769
+ const HUB_REQUEST_TIMEOUT_MS = 15e3;
42770
+ /**
42771
+ * How stale another machine's write may look here.
42772
+ *
42773
+ * Short enough that a board left open follows along, long enough that two
42774
+ * servers idling all day are not a steady stream of requests at a hosted
42775
+ * service. Only changes made elsewhere wait for it.
42776
+ */
42777
+ const POLL_INTERVAL = Duration.seconds(10);
42778
+ const TaskListPage = Schema$1.Struct({ tasks: Schema$1.Array(Task) });
42779
+ const ConflictBody = Schema$1.Struct({ current: Task });
42780
+ const decodeTaskListPage = Schema$1.decodeUnknownEffect(TaskListPage);
42781
+ const decodeTask = Schema$1.decodeUnknownEffect(Task);
42782
+ const decodeConflictBody = Schema$1.decodeUnknownEffect(ConflictBody);
42783
+ /**
42784
+ * Every failure here is reported as the interface's own error type.
42785
+ *
42786
+ * `PersistenceSqlError` names a store that would not answer, which is exactly
42787
+ * what this is from a caller's point of view — the detail says the store is the
42788
+ * hub. Inventing a second error type would mean touching every call site to
42789
+ * handle a case they already handle.
42790
+ */
42791
+ const hubUnavailable = (operation, detail) => new PersistenceSqlError({
42792
+ operation: `RemoteTaskRepository.${operation}`,
42793
+ detail: `The hub board is unavailable: ${detail}`,
42794
+ cause: null
42795
+ });
42796
+ /**
42797
+ * @param isActive Whether this server is currently reading its board from the
42798
+ * hub. The poll asks before every pass rather than being started once: a
42799
+ * machine with the board set to `local` must not be sending a request to a
42800
+ * hosted service every ten seconds for a board nobody is reading, and the
42801
+ * setting can change while the server runs.
42802
+ */
42803
+ const makeRemoteTaskRepository = Effect.fn("makeRemoteTaskRepository")(function* (isActive) {
42804
+ const http = yield* HttpClient.HttpClient;
42805
+ const link = yield* HubLink;
42806
+ const changes = yield* PubSub.unbounded();
42807
+ const publish = (event) => PubSub.publish(changes, event).pipe(Effect.asVoid);
42808
+ /** The board as of the last poll, for diffing the next one against. */
42809
+ const lastSeen = yield* Ref.make(null);
42810
+ const requireSettings = (operation) => Effect.gen(function* () {
42811
+ const state = yield* link.current;
42812
+ if (Option.isNone(state.settings)) return yield* hubUnavailable(operation, "no hub is configured on this server");
42813
+ return state.settings.value;
42814
+ });
42815
+ const send = (operation, settings, request) => http.execute(request.pipe(HttpClientRequest.setHeader("authorization", `Bearer ${settings.token}`), HttpClientRequest.setHeader("accept", "application/json"))).pipe(Effect.timeout(HUB_REQUEST_TIMEOUT_MS), Effect.mapError((cause) => hubUnavailable(operation, `${cause._tag ?? "request failed"}`)));
42816
+ const list = (filter) => Effect.gen(function* () {
42817
+ const settings = yield* requireSettings("list");
42818
+ const query = new URLSearchParams();
42819
+ for (const key of [
42820
+ "projectId",
42821
+ "repositoryKey",
42822
+ "threadId",
42823
+ "status",
42824
+ "assignee"
42825
+ ]) {
42826
+ const value = filter[key];
42827
+ if (value !== void 0) query.set(key, value);
42828
+ }
42829
+ const suffix = query.size === 0 ? "" : `?${query.toString()}`;
42830
+ const response = yield* send("list", settings, HttpClientRequest.get(`${settings.baseUrl}/tasks${suffix}`));
42831
+ if (response.status !== 200) return yield* hubUnavailable("list", `status ${response.status}`);
42832
+ return (yield* response.json.pipe(Effect.flatMap(decodeTaskListPage), Effect.mapError(() => hubUnavailable("list", "the response did not match the contract")))).tasks;
42833
+ });
42834
+ const getById = ({ taskId }) => Effect.gen(function* () {
42835
+ const settings = yield* requireSettings("getById");
42836
+ const response = yield* send("getById", settings, HttpClientRequest.get(`${settings.baseUrl}/tasks/${encodeURIComponent(taskId)}`));
42837
+ if (response.status === 404) return Option.none();
42838
+ if (response.status !== 200) return yield* hubUnavailable("getById", `status ${response.status}`);
42839
+ return Option.some(yield* response.json.pipe(Effect.flatMap(decodeTask), Effect.mapError(() => hubUnavailable("getById", "the response did not match the contract"))));
42840
+ });
42841
+ /**
42842
+ * Upsert is a create here, and that is a real difference from the local
42843
+ * store.
42844
+ *
42845
+ * The hub mints its own ids and timestamps, so a row written through this
42846
+ * comes back with the hub's version of both. The caller's `taskId` is not
42847
+ * carried: honouring it would need a hub route that takes one, and inventing
42848
+ * that quietly would let two servers mint colliding ids.
42849
+ */
42850
+ const upsert = (row) => Effect.gen(function* () {
42851
+ const settings = yield* requireSettings("upsert");
42852
+ const response = yield* send("upsert", settings, HttpClientRequest.bodyJsonUnsafe(HttpClientRequest.post(`${settings.baseUrl}/tasks`), {
42853
+ ...row.projectId === null ? {} : { projectId: row.projectId },
42854
+ ...row.repositoryKey === null ? {} : { repositoryKey: row.repositoryKey },
42855
+ ...row.threadId === null ? {} : { threadId: row.threadId },
42856
+ title: row.title,
42857
+ body: row.body,
42858
+ status: row.status,
42859
+ priority: row.priority,
42860
+ ...row.assignee === null ? {} : { assignee: row.assignee },
42861
+ labels: row.labels
42862
+ }));
42863
+ if (response.status !== 201 && response.status !== 200) return yield* hubUnavailable("upsert", `status ${response.status}`);
42864
+ const created = yield* response.json.pipe(Effect.flatMap(decodeTask), Effect.mapError(() => hubUnavailable("upsert", "the response did not match the contract")));
42865
+ yield* publish({
42866
+ type: "upserted",
42867
+ task: created
42868
+ });
42869
+ });
42870
+ const patch = (input) => Effect.gen(function* () {
42871
+ const settings = yield* requireSettings("patch");
42872
+ const { taskId, ...fields } = input;
42873
+ const response = yield* send("patch", settings, HttpClientRequest.bodyJsonUnsafe(HttpClientRequest.patch(`${settings.baseUrl}/tasks/${encodeURIComponent(taskId)}`), fields));
42874
+ if (response.status === 404) return Option.none();
42875
+ if (response.status !== 200) return yield* hubUnavailable("patch", `status ${response.status}`);
42876
+ const updated = yield* response.json.pipe(Effect.flatMap(decodeTask), Effect.mapError(() => hubUnavailable("patch", "the response did not match the contract")));
42877
+ yield* publish({
42878
+ type: "upserted",
42879
+ task: updated
42880
+ });
42881
+ return Option.some(updated);
42882
+ });
42883
+ const patchIfStatus = (input) => Effect.gen(function* () {
42884
+ const settings = yield* requireSettings("patchIfStatus");
42885
+ const { taskId, ...fields } = input.patch;
42886
+ const response = yield* send("patchIfStatus", settings, HttpClientRequest.bodyJsonUnsafe(HttpClientRequest.patch(`${settings.baseUrl}/tasks/${encodeURIComponent(taskId)}`), {
42887
+ ...fields,
42888
+ expectedStatus: input.expectedStatus
42889
+ }));
42890
+ if (response.status === 404) return { outcome: "missing" };
42891
+ if (response.status === 409) return {
42892
+ outcome: "conflict",
42893
+ current: (yield* response.json.pipe(Effect.flatMap(decodeConflictBody), Effect.mapError(() => hubUnavailable("patchIfStatus", "the conflict response did not match the contract")))).current
42894
+ };
42895
+ if (response.status !== 200) return yield* hubUnavailable("patchIfStatus", `status ${response.status}`);
42896
+ const updated = yield* response.json.pipe(Effect.flatMap(decodeTask), Effect.mapError(() => hubUnavailable("patchIfStatus", "the response did not match the contract")));
42897
+ yield* publish({
42898
+ type: "upserted",
42899
+ task: updated
42900
+ });
42901
+ return {
42902
+ outcome: "updated",
42903
+ task: updated
42904
+ };
42905
+ });
42906
+ const deleteById = ({ taskId }) => Effect.gen(function* () {
42907
+ const settings = yield* requireSettings("deleteById");
42908
+ const response = yield* send("deleteById", settings, HttpClientRequest.delete(`${settings.baseUrl}/tasks/${encodeURIComponent(taskId)}`));
42909
+ if (response.status !== 200 && response.status !== 204 && response.status !== 404) return yield* hubUnavailable("deleteById", `status ${response.status}`);
42910
+ yield* publish({
42911
+ type: "deleted",
42912
+ taskId
42913
+ });
42914
+ });
42915
+ /**
42916
+ * One poll: read the board, publish what differs from last time.
42917
+ *
42918
+ * Compared on `updatedAt` rather than deep equality, because that is the
42919
+ * field every hub write moves and it makes the diff O(n) with no schema
42920
+ * knowledge. A failed poll publishes nothing and leaves the last snapshot in
42921
+ * place — a hub blip should not empty every open board.
42922
+ */
42923
+ const pollOnce = Effect.gen(function* () {
42924
+ if (!(yield* isActive)) return;
42925
+ const current = yield* list({});
42926
+ const previous = yield* Ref.getAndSet(lastSeen, current);
42927
+ if (previous === null) return;
42928
+ const previousById = new Map(previous.map((task) => [task.taskId, task]));
42929
+ for (const task of current) {
42930
+ const before = previousById.get(task.taskId);
42931
+ if (before === void 0 || before.updatedAt !== task.updatedAt) yield* publish({
42932
+ type: "upserted",
42933
+ task
42934
+ });
42935
+ previousById.delete(task.taskId);
42936
+ }
42937
+ for (const taskId of previousById.keys()) yield* publish({
42938
+ type: "deleted",
42939
+ taskId
42940
+ });
42941
+ }).pipe(Effect.ignoreCause({ log: true }));
42942
+ yield* Effect.forever(pollOnce.pipe(Effect.andThen(Effect.sleep(POLL_INTERVAL)))).pipe(Effect.forkScoped);
42943
+ return {
42944
+ upsert,
42945
+ patch,
42946
+ patchIfStatus,
42947
+ getById,
42948
+ list,
42949
+ deleteById,
42950
+ streamChanges: Stream.fromPubSub(changes),
42951
+ streamWithSnapshot: Stream.unwrap(Effect.gen(function* () {
42952
+ const subscription = yield* PubSub.subscribe(changes);
42953
+ const snapshot = yield* list({});
42954
+ yield* Ref.set(lastSeen, snapshot);
42955
+ return Stream.concat(Stream.succeed({
42956
+ type: "snapshot",
42957
+ tasks: snapshot
42958
+ }), Stream.fromSubscription(subscription));
42959
+ }))
42960
+ };
42961
+ });
42962
+ Layer.effect(TaskRepository, makeRemoteTaskRepository(Effect.succeed(true)));
42963
+ //#endregion
42964
+ //#region src/persistence/Layers/TaskBoardSource.ts
42965
+ /**
42966
+ * Which board a call goes to: this machine's database, or the hub's.
42967
+ *
42968
+ * Both repositories are built, and every call reads the setting and delegates.
42969
+ * That is deliberate rather than picking one at startup: `taskBoardSource` is
42970
+ * an ordinary setting a person changes in a panel, and a setting that only
42971
+ * takes effect after a restart is the same hazard the hub link already
42972
+ * documents — you change it, nothing happens, and there is nothing on screen
42973
+ * saying why.
42974
+ *
42975
+ * **Subscriptions pick their source when they subscribe.** A stream cannot
42976
+ * change its mind mid-flight without inventing a "your board moved" event that
42977
+ * nothing knows how to render, so an open board keeps reading the source it
42978
+ * opened against and picks up the change when it reconnects. Switching is rare;
42979
+ * a wrong row on screen is not worth a new event type.
42980
+ *
42981
+ * @module persistence/Layers/TaskBoardSource
42982
+ */
42983
+ const makeRoutedTaskRepository = Effect.gen(function* () {
42984
+ const local = yield* makeTaskRepository;
42985
+ const usingHub = (yield* ServerSettingsService).getSettings.pipe(Effect.map((current) => current.taskBoardSource === "hub"), Effect.orElseSucceed(() => false));
42986
+ const remote = yield* makeRemoteTaskRepository(usingHub);
42987
+ /**
42988
+ * `local` on a settings read failure, and that direction is chosen.
42989
+ *
42990
+ * The alternative is refusing every board call because a settings file could
42991
+ * not be read, which turns one broken file into a dead board. Falling back to
42992
+ * the machine's own database is the answer that still works and cannot lose a
42993
+ * hub write, because a write that never reached the hub was never accepted.
42994
+ */
42995
+ const pick = usingHub.pipe(Effect.map((hub) => hub ? remote : local));
42996
+ return {
42997
+ upsert: (row) => pick.pipe(Effect.flatMap((repository) => repository.upsert(row))),
42998
+ patch: (input, updatedAt) => pick.pipe(Effect.flatMap((repository) => repository.patch(input, updatedAt))),
42999
+ patchIfStatus: (input, updatedAt) => pick.pipe(Effect.flatMap((repository) => repository.patchIfStatus(input, updatedAt))),
43000
+ getById: (input) => pick.pipe(Effect.flatMap((repository) => repository.getById(input))),
43001
+ list: (filter) => pick.pipe(Effect.flatMap((repository) => repository.list(filter))),
43002
+ deleteById: (input) => pick.pipe(Effect.flatMap((repository) => repository.deleteById(input))),
43003
+ get streamChanges() {
43004
+ return Stream.unwrap(pick.pipe(Effect.map((repository) => repository.streamChanges)));
43005
+ },
43006
+ get streamWithSnapshot() {
43007
+ return Stream.unwrap(pick.pipe(Effect.map((repository) => repository.streamWithSnapshot)));
43008
+ }
43009
+ };
43010
+ });
43011
+ /**
43012
+ * The board, wherever it lives.
43013
+ *
43014
+ * Replaces `TaskRepositoryLive` at the one place the route tree provides it, so
43015
+ * the MCP toolkit and the websocket handlers keep sharing a single instance —
43016
+ * which is what makes a task an agent files appear on an open board.
43017
+ */
43018
+ const RoutedTaskRepositoryLive = Layer.effect(TaskRepository, makeRoutedTaskRepository);
43019
+ //#endregion
42230
43020
  //#region src/persistence/ProviderSessionRuntime.ts
42231
43021
  /**
42232
43022
  * ProviderSessionRuntimeRepository - Repository interface for provider runtime sessions.
@@ -44246,7 +45036,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
44246
45036
  };
44247
45037
  });
44248
45038
  var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
44249
- const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$68));
45039
+ const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$69));
44250
45040
  //#endregion
44251
45041
  //#region src/textGeneration/TextGenerationUtils.ts
44252
45042
  const isTextGenerationError$2 = Schema$1.is(TextGenerationError);
@@ -45218,7 +46008,7 @@ const runClaudeCommand = Effect.fn("runClaudeCommand")(function* (claudeSettings
45218
46008
  });
45219
46009
  return yield* spawnAndCollect(claudeSettings.binaryPath, command);
45220
46010
  });
45221
- const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")(function* (claudeSettings, resolveCapabilities, environment, cwd) {
46011
+ const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")(function* (claudeSettings, resolveCapabilities, environment, cwd, disabledSkills) {
45222
46012
  const resolvedEnvironment = environment ?? process.env;
45223
46013
  const checkedAt = DateTime.formatIso(yield* DateTime.now);
45224
46014
  const allModels = providerModelsFromSettings(BUILT_IN_MODELS, claudeSettings.customModels, DEFAULT_CLAUDE_MODEL_CAPABILITIES);
@@ -45292,7 +46082,7 @@ const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")(functio
45292
46082
  const models = providerModelsFromSettings(getBuiltInClaudeModelsForVersion(parsedVersion), claudeSettings.customModels, DEFAULT_CLAUDE_MODEL_CAPABILITIES);
45293
46083
  const versionUpgradeMessage = supportsClaudeOpus5(parsedVersion) ? void 0 : supportsClaudeFable5(parsedVersion) ? formatClaudeOpus5UpgradeMessage(parsedVersion) : supportsClaudeOpus48(parsedVersion) ? formatClaudeFable5UpgradeMessage(parsedVersion) : supportsClaudeOpus47(parsedVersion) ? formatClaudeOpus48UpgradeMessage(parsedVersion) : formatClaudeOpus47UpgradeMessage(parsedVersion);
45294
46084
  const capabilities = resolveCapabilities ? yield* resolveCapabilities(claudeSettings).pipe(Effect.orElseSucceed(() => void 0)) : void 0;
45295
- const skills = yield* discoverClaudeSkills(claudeSettings, cwd, resolvedEnvironment);
46085
+ const skills = yield* discoverClaudeSkills(claudeSettings, cwd, resolvedEnvironment, disabledSkills);
45296
46086
  const dedupedSlashCommands = dedupeSlashCommands(capabilities?.slashCommands ?? []);
45297
46087
  if (!capabilities) return buildServerProvider({
45298
46088
  presentation: CLAUDE_PRESENTATION,
@@ -45693,6 +46483,214 @@ function parseCliArgs(args, options) {
45693
46483
  };
45694
46484
  }
45695
46485
  //#endregion
46486
+ //#region ../../packages/shared/src/toolCategory.ts
46487
+ const TOOL_CATEGORY_TITLES = {
46488
+ file_read: "File read",
46489
+ file_search: "File search",
46490
+ file_change: "File change",
46491
+ command: "Command run",
46492
+ mcp_tool: "MCP tool call",
46493
+ subagent: "Subagent task",
46494
+ web_search: "Web search",
46495
+ image_view: "Image view",
46496
+ tool: "Tool call"
46497
+ };
46498
+ /** Exact tool names, lowercased, matched before the substring heuristics below. */
46499
+ const FILE_READ_TOOL_NAMES = /* @__PURE__ */ new Set([
46500
+ "read",
46501
+ "readfile",
46502
+ "read_file",
46503
+ "view",
46504
+ "viewfile",
46505
+ "view_file",
46506
+ "notebookread",
46507
+ "notebook_read",
46508
+ "openfile",
46509
+ "open_file"
46510
+ ]);
46511
+ const FILE_SEARCH_TOOL_NAMES = /* @__PURE__ */ new Set([
46512
+ "grep",
46513
+ "glob",
46514
+ "search",
46515
+ "filesearch",
46516
+ "file_search",
46517
+ "codebasesearch",
46518
+ "codebase_search",
46519
+ "listdir",
46520
+ "list_dir",
46521
+ "list_directory",
46522
+ "ls"
46523
+ ]);
46524
+ const FILE_CHANGE_TOOL_NAMES = /* @__PURE__ */ new Set([
46525
+ "edit",
46526
+ "write",
46527
+ "multiedit",
46528
+ "multi_edit",
46529
+ "notebookedit",
46530
+ "notebook_edit",
46531
+ "applypatch",
46532
+ "apply_patch",
46533
+ "patch",
46534
+ "createfile",
46535
+ "create_file",
46536
+ "strreplace",
46537
+ "str_replace",
46538
+ "deletefile",
46539
+ "delete_file"
46540
+ ]);
46541
+ const COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set([
46542
+ "bash",
46543
+ "shell",
46544
+ "terminal",
46545
+ "exec",
46546
+ "execute",
46547
+ "execcommand",
46548
+ "exec_command",
46549
+ "runcommand",
46550
+ "run_command",
46551
+ "local_shell"
46552
+ ]);
46553
+ const SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
46554
+ "task",
46555
+ "agent",
46556
+ "subagent",
46557
+ "sub_agent"
46558
+ ]);
46559
+ /** Shell builtins that say nothing about what the command as a whole does. */
46560
+ const NEUTRAL_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46561
+ "cd",
46562
+ "echo",
46563
+ "printf",
46564
+ "pwd",
46565
+ "true",
46566
+ "false",
46567
+ "time"
46568
+ ]);
46569
+ /** `sed`/`awk` are stream editors (`sed -i` rewrites files), so they stay commands. */
46570
+ const FILE_READ_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46571
+ "cat",
46572
+ "head",
46573
+ "tail",
46574
+ "less",
46575
+ "more",
46576
+ "bat",
46577
+ "nl",
46578
+ "jq",
46579
+ "wc"
46580
+ ]);
46581
+ const FILE_SEARCH_SHELL_COMMANDS = /* @__PURE__ */ new Set([
46582
+ "grep",
46583
+ "egrep",
46584
+ "fgrep",
46585
+ "rg",
46586
+ "ag",
46587
+ "ack",
46588
+ "find",
46589
+ "fd",
46590
+ "ls",
46591
+ "tree",
46592
+ "which"
46593
+ ]);
46594
+ const SHELL_SEGMENT_SEPARATOR = /\|\||&&|;|\||\n/u;
46595
+ const LEADING_ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=\S*\s+/u;
46596
+ const REDIRECT_TARGET = /\d?>>?\s*(?<target>[^\s|;&]+)/gu;
46597
+ /** Redirect targets that discard output instead of writing a file. */
46598
+ const DISCARDED_REDIRECT_TARGETS = /* @__PURE__ */ new Set([
46599
+ "/dev/null",
46600
+ "&1",
46601
+ "&2"
46602
+ ]);
46603
+ function toolCategoryTitle(category) {
46604
+ return TOOL_CATEGORY_TITLES[category];
46605
+ }
46606
+ function readCommandInput(toolInput) {
46607
+ const raw = toolInput?.command ?? toolInput?.cmd;
46608
+ if (typeof raw !== "string") return;
46609
+ const trimmed = raw.trim();
46610
+ return trimmed.length > 0 ? trimmed : void 0;
46611
+ }
46612
+ /** `cat x > y` reads and writes; only the write matters for the row heading. */
46613
+ function redirectsToFile(segment) {
46614
+ for (const match of segment.matchAll(REDIRECT_TARGET)) {
46615
+ const target = match.groups?.target;
46616
+ if (target && !DISCARDED_REDIRECT_TARGETS.has(target)) return true;
46617
+ }
46618
+ return false;
46619
+ }
46620
+ function classifyShellSegment(segment) {
46621
+ if (redirectsToFile(segment)) return "command";
46622
+ const head = segment.trim().replace(LEADING_ENV_ASSIGNMENT, "").split(/\s+/u)[0]?.replace(/^.*\//u, "").toLowerCase();
46623
+ if (!head || NEUTRAL_SHELL_COMMANDS.has(head)) return;
46624
+ if (FILE_READ_SHELL_COMMANDS.has(head)) return "file_read";
46625
+ if (FILE_SEARCH_SHELL_COMMANDS.has(head)) return "file_search";
46626
+ return "command";
46627
+ }
46628
+ /**
46629
+ * A shell call only counts as a read or a search when every non-neutral segment
46630
+ * agrees; anything unrecognized (or write-ish) keeps the whole call a command.
46631
+ * Quoted separators split segments too, which can only downgrade to "command".
46632
+ */
46633
+ function classifyShellCommand(command) {
46634
+ const categories = command.split(SHELL_SEGMENT_SEPARATOR).map(classifyShellSegment).filter((category) => category !== void 0);
46635
+ if (categories.length === 0 || categories.includes("command")) return "command";
46636
+ return categories.includes("file_search") ? "file_search" : "file_read";
46637
+ }
46638
+ function classifyToolCategory(input) {
46639
+ const normalized = input.toolName.trim().toLowerCase();
46640
+ if (normalized.startsWith("mcp__") || normalized.includes("mcp")) return "mcp_tool";
46641
+ if (SUBAGENT_TOOL_NAMES.has(normalized) || normalized.includes("agent")) return "subagent";
46642
+ if (FILE_READ_TOOL_NAMES.has(normalized)) return "file_read";
46643
+ if (FILE_SEARCH_TOOL_NAMES.has(normalized)) return "file_search";
46644
+ if (FILE_CHANGE_TOOL_NAMES.has(normalized)) return "file_change";
46645
+ if (COMMAND_TOOL_NAMES.has(normalized) || normalized.includes("bash") || normalized.includes("shell") || normalized.includes("terminal") || normalized.includes("command")) {
46646
+ const command = readCommandInput(input.toolInput);
46647
+ return command ? classifyShellCommand(command) : "command";
46648
+ }
46649
+ if (normalized.includes("websearch") || normalized.includes("web_search")) return "web_search";
46650
+ if (normalized.includes("edit") || normalized.includes("write") || normalized.includes("patch") || normalized.includes("replace") || normalized.includes("create") || normalized.includes("delete")) return "file_change";
46651
+ if (normalized.includes("read") || normalized.includes("view")) return "file_read";
46652
+ if (normalized.includes("grep") || normalized.includes("glob")) return "file_search";
46653
+ if (normalized.includes("image")) return "image_view";
46654
+ return "tool";
46655
+ }
46656
+ //#endregion
46657
+ //#region src/provider/askUserQuestionAnswers.ts
46658
+ /**
46659
+ * What separates the options of one multi-select answer.
46660
+ *
46661
+ * Comma-and-space rather than a bare comma: the SDK renders this string into a
46662
+ * tool result a model reads, and option labels are prose ("Fail loudly, no
46663
+ * fallback"), so the space is what keeps two joined labels from reading as one.
46664
+ */
46665
+ const MULTI_SELECT_SEPARATOR = ", ";
46666
+ function formatAnswerValue(value) {
46667
+ if (typeof value === "string") {
46668
+ const trimmed = value.trim();
46669
+ return trimmed.length > 0 ? trimmed : null;
46670
+ }
46671
+ if (Array.isArray(value)) {
46672
+ const parts = value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
46673
+ return parts.length > 0 ? parts.join(MULTI_SELECT_SEPARATOR) : null;
46674
+ }
46675
+ return null;
46676
+ }
46677
+ /**
46678
+ * The answers map to hand back to the SDK.
46679
+ *
46680
+ * Questions whose answer is empty, or is neither a string nor a list of them,
46681
+ * are dropped rather than stringified. `String(value)` on an object would put
46682
+ * `[object Object]` in front of the model as though it were the user's choice,
46683
+ * and an absent key is at least a fact the SDK can see.
46684
+ */
46685
+ function formatAskUserQuestionAnswers(answers) {
46686
+ const formatted = {};
46687
+ for (const [question, value] of Object.entries(answers)) {
46688
+ const answer = formatAnswerValue(value);
46689
+ if (answer !== null) formatted[question] = answer;
46690
+ }
46691
+ return formatted;
46692
+ }
46693
+ //#endregion
45696
46694
  //#region src/provider/Layers/ClaudeAdapter.ts
45697
46695
  /**
45698
46696
  * ClaudeAdapterLive - Scoped live implementation for the Claude Agent provider adapter.
@@ -46183,32 +47181,62 @@ function planStepsFromClaudeTasks(tasks) {
46183
47181
  };
46184
47182
  });
46185
47183
  }
47184
+ const TOOL_SUMMARY_MAX_LENGTH = 400;
47185
+ const TOOL_PROMPT_SUMMARY_MAX_LENGTH = 200;
47186
+ const TOOL_PATH_INPUT_KEYS = [
47187
+ "file_path",
47188
+ "filePath",
47189
+ "notebook_path",
47190
+ "path"
47191
+ ];
47192
+ function readTrimmedStringInput(input, keys) {
47193
+ for (const key of keys) {
47194
+ const value = input[key];
47195
+ if (typeof value === "string" && value.trim().length > 0) return value.trim();
47196
+ }
47197
+ }
47198
+ /**
47199
+ * Read/Grep/Glob carry no `command`, so without this they fall through to the
47200
+ * raw-JSON fallback and the work log shows `Read: {"file_path":...,"offset":80}`.
47201
+ */
47202
+ function summarizeFileToolRequest(toolName, input) {
47203
+ const pattern = readTrimmedStringInput(input, ["pattern", "query"]);
47204
+ const path = readTrimmedStringInput(input, TOOL_PATH_INPUT_KEYS);
47205
+ if (pattern) return path ? `${toolName}: ${pattern} in ${path}` : `${toolName}: ${pattern}`;
47206
+ if (!path) return;
47207
+ const offset = input.offset;
47208
+ const limit = input.limit;
47209
+ if (typeof offset === "number" && Number.isFinite(offset)) return `${toolName}: ${path}:${offset}${typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? `-${offset + limit - 1}` : ""}`;
47210
+ return `${toolName}: ${path}`;
47211
+ }
46186
47212
  function summarizeToolRequest(toolName, input) {
46187
47213
  const commandValue = input.command ?? input.cmd;
46188
47214
  const command = typeof commandValue === "string" ? commandValue : void 0;
46189
- if (command && command.trim().length > 0) return `${toolName}: ${command.trim().slice(0, 400)}`;
47215
+ if (command && command.trim().length > 0) return `${toolName}: ${command.trim().slice(0, TOOL_SUMMARY_MAX_LENGTH)}`;
46190
47216
  if (classifyToolItemType(toolName) === "collab_agent_tool_call") {
46191
47217
  const description = typeof input.description === "string" ? input.description.trim() : void 0;
46192
47218
  const prompt = typeof input.prompt === "string" ? input.prompt.trim() : void 0;
46193
47219
  const subagentType = typeof input.subagent_type === "string" ? input.subagent_type.trim() : void 0;
46194
- const label = description || (prompt ? prompt.slice(0, 200) : void 0);
47220
+ const label = description || (prompt ? prompt.slice(0, TOOL_PROMPT_SUMMARY_MAX_LENGTH) : void 0);
46195
47221
  if (label) return subagentType ? `${subagentType}: ${label}` : label;
46196
47222
  }
47223
+ const fileSummary = summarizeFileToolRequest(toolName, input);
47224
+ if (fileSummary) return fileSummary.slice(0, TOOL_SUMMARY_MAX_LENGTH);
46197
47225
  const serialized = encodeJsonStringForDiagnostics$2(input) ?? "[unserializable input]";
46198
- if (serialized.length <= 400) return `${toolName}: ${serialized}`;
46199
- return `${toolName}: ${serialized.slice(0, 397)}...`;
47226
+ if (serialized.length <= TOOL_SUMMARY_MAX_LENGTH) return `${toolName}: ${serialized}`;
47227
+ return `${toolName}: ${serialized.slice(0, TOOL_SUMMARY_MAX_LENGTH - 3)}...`;
46200
47228
  }
46201
- function titleForTool(itemType) {
46202
- switch (itemType) {
46203
- case "command_execution": return "Command run";
46204
- case "file_change": return "File change";
46205
- case "mcp_tool_call": return "MCP tool call";
46206
- case "collab_agent_tool_call": return "Subagent task";
46207
- case "web_search": return "Web search";
46208
- case "image_view": return "Image view";
46209
- case "dynamic_tool_call": return "Tool call";
46210
- default: return "Item";
46211
- }
47229
+ /**
47230
+ * Heading for a tool row. Derived from the tool name plus its input so a `Read`
47231
+ * reads as "File read" instead of a bare "Tool call", and a `Bash` that only
47232
+ * inspects a file (`sed -n 1,20p src/x.ts`) reads as "File read" instead of
47233
+ * "Command run". Recomputed as streamed input arrives.
47234
+ */
47235
+ function titleForTool(toolName, toolInput) {
47236
+ return toolCategoryTitle(classifyToolCategory({
47237
+ toolName,
47238
+ toolInput
47239
+ }));
46212
47240
  }
46213
47241
  const SUPPORTED_CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
46214
47242
  "image/gif",
@@ -46216,6 +47244,12 @@ const SUPPORTED_CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
46216
47244
  "image/png",
46217
47245
  "image/webp"
46218
47246
  ]);
47247
+ /**
47248
+ * Appended to the preset system prompt when `narrateBeforeTools` is on. The SDK
47249
+ * preset drops the interactive CLI's terminal-tone sections, so without this the
47250
+ * model opens a turn with a tool call rather than a line saying what it is doing.
47251
+ */
47252
+ const NARRATE_BEFORE_TOOLS_PROMPT = "Before your first tool call in a turn, write one short sentence saying what you are about to do. Keep it to a single line and skip it when you are answering without tools.";
46219
47253
  const CLAUDE_SETTING_SOURCES = [
46220
47254
  "user",
46221
47255
  "project",
@@ -46237,6 +47271,23 @@ function buildUserMessage(input) {
46237
47271
  }
46238
47272
  };
46239
47273
  }
47274
+ function userMessageContentBlocks(message) {
47275
+ const content = message.message.content;
47276
+ if (typeof content === "string") return content.length > 0 ? [{
47277
+ type: "text",
47278
+ text: content
47279
+ }] : [];
47280
+ return Array.isArray(content) ? content : [];
47281
+ }
47282
+ /**
47283
+ * Messages typed while a turn runs are delivered as one prompt, not one turn
47284
+ * each: a user who sends three corrections and then hits Stop means them to be
47285
+ * read together, and replaying them as separate turns made the agent act on the
47286
+ * first before seeing the rest.
47287
+ */
47288
+ function mergeUserMessages(previous, next) {
47289
+ return buildUserMessage({ sdkContent: [...userMessageContentBlocks(previous), ...userMessageContentBlocks(next)] });
47290
+ }
46240
47291
  function buildClaudeImageContentBlock(input) {
46241
47292
  return {
46242
47293
  type: "image",
@@ -46876,6 +47927,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46876
47927
  title: tool.title,
46877
47928
  ...tool.detail ? { detail: tool.detail } : {},
46878
47929
  data: {
47930
+ toolCallId: tool.itemId,
46879
47931
  toolName: tool.toolName,
46880
47932
  input: tool.input
46881
47933
  }
@@ -46923,6 +47975,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46923
47975
  });
46924
47976
  const updatedAt = yield* nowIso;
46925
47977
  context.turnState = void 0;
47978
+ context.steeredMessages.clear();
46926
47979
  context.session = {
46927
47980
  ...context.session,
46928
47981
  status: "ready",
@@ -46969,6 +48022,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46969
48022
  const updatedAt = yield* nowIso;
46970
48023
  context.turnState = turnState;
46971
48024
  context.interruptRequested = false;
48025
+ context.steeredMessages.clear();
46972
48026
  context.session = {
46973
48027
  ...context.session,
46974
48028
  status: "running",
@@ -46988,25 +48042,51 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
46988
48042
  });
46989
48043
  });
46990
48044
  /**
46991
- * Start the next queued sendTurn, if the session has gone idle.
48045
+ * Start the queued sendTurn, if the session has gone idle.
46992
48046
  *
46993
- * Runs after every turn completion. A message sent during a running turn
46994
- * waits here for the agent loop to end; pressing Stop ends the loop early,
46995
- * and the same drain is what makes Stop mean "and run my message now".
46996
- * One request per drain, so each queued message gets its own turn boundary -
46997
- * the next one starts when this turn's result arrives.
48047
+ * Runs after every turn completion. What waits here are the steers Stop took
48048
+ * back from the CLI before they ran (see `reclaimSteeredMessages`); they are
48049
+ * already merged into one request, so they start as a single turn the moment
48050
+ * the interrupted loop ends.
46998
48051
  */
46999
48052
  const drainPendingTurns = Effect.fn("drainPendingTurns")(function* (context) {
47000
48053
  if (context.stopped || context.turnState !== void 0) return;
47001
- const pending = context.pendingTurnRequests.shift();
48054
+ const pending = context.pendingTurnRequest;
47002
48055
  if (pending === void 0) return;
47003
- yield* applyTurnSessionOptions(context, pending.modelSelection, pending.interactionMode).pipe(Effect.catchCause((cause) => Effect.logWarning("claude pending turn session options failed", { cause })));
47004
- yield* beginTurn(context, pending.turnId, pending.modelSelection?.model);
48056
+ context.pendingTurnRequest = void 0;
48057
+ yield* beginTurn(context, pending.turnId, context.session.model);
47005
48058
  yield* Queue.offer(context.promptQueue, {
47006
48059
  type: "message",
47007
48060
  message: pending.message
47008
48061
  }).pipe(Effect.ignore);
47009
48062
  });
48063
+ /**
48064
+ * Take back the steers the CLI has not started yet, so Stop still runs them.
48065
+ *
48066
+ * A steer sits in the CLI's async-message queue until the agent loop reaches
48067
+ * its next boundary. `cancelAsyncMessage` answers true only while it is still
48068
+ * waiting there: those never reached the model and become the next turn
48069
+ * together, while an already-dequeued one is part of the turn being stopped
48070
+ * and must not run a second time.
48071
+ */
48072
+ const reclaimSteeredMessages = Effect.fn("reclaimSteeredMessages")(function* (context) {
48073
+ const steered = Array.from(context.steeredMessages);
48074
+ context.steeredMessages.clear();
48075
+ const cancelAsyncMessage = context.query.cancelAsyncMessage;
48076
+ if (steered.length === 0 || cancelAsyncMessage === void 0) return;
48077
+ const reclaimed = [];
48078
+ for (const [messageUuid, message] of steered) if (yield* Effect.tryPromise({
48079
+ try: () => cancelAsyncMessage(messageUuid),
48080
+ catch: (cause) => toRequestError$1(context.session.threadId, "turn/cancelAsyncMessage", cause)
48081
+ }).pipe(Effect.catchCause((cause) => Effect.logWarning("claude steer cancel failed", { cause }).pipe(Effect.as(false))))) reclaimed.push(message);
48082
+ if (reclaimed.length === 0) return;
48083
+ const merged = reclaimed.reduce((left, right) => mergeUserMessages(left, right));
48084
+ const pending = context.pendingTurnRequest;
48085
+ context.pendingTurnRequest = {
48086
+ turnId: pending?.turnId ?? TurnId.make(yield* randomUUIDv4),
48087
+ message: pending ? mergeUserMessages(pending.message, merged) : merged
48088
+ };
48089
+ });
47010
48090
  const handleStreamEvent = Effect.fn("handleStreamEvent")(function* (context, message) {
47011
48091
  if (message.type !== "stream_event") return;
47012
48092
  const { event } = message;
@@ -47057,11 +48137,13 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47057
48137
  const partialInputJson = tool.partialInputJson + event.delta.partial_json;
47058
48138
  const parsedInput = tryParseJsonRecord(partialInputJson);
47059
48139
  const detail = parsedInput ? summarizeToolRequest(tool.toolName, parsedInput) : tool.detail;
48140
+ const title = parsedInput ? titleForTool(tool.toolName, parsedInput) : tool.title;
47060
48141
  let nextTool = {
47061
48142
  ...tool,
47062
48143
  partialInputJson,
47063
48144
  ...parsedInput ? { input: parsedInput } : {},
47064
- ...detail ? { detail } : {}
48145
+ ...detail ? { detail } : {},
48146
+ title
47065
48147
  };
47066
48148
  const nextFingerprint = parsedInput && Object.keys(parsedInput).length > 0 ? toolInputFingerprint(parsedInput) : void 0;
47067
48149
  context.inFlightTools.set(event.index, nextTool);
@@ -47086,6 +48168,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47086
48168
  title: nextTool.title,
47087
48169
  ...nextTool.detail ? { detail: nextTool.detail } : {},
47088
48170
  data: {
48171
+ toolCallId: nextTool.itemId,
47089
48172
  toolName: nextTool.toolName,
47090
48173
  input: nextTool.input
47091
48174
  }
@@ -47133,7 +48216,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47133
48216
  itemId,
47134
48217
  itemType,
47135
48218
  toolName,
47136
- title: titleForTool(itemType),
48219
+ title: titleForTool(toolName, toolInput),
47137
48220
  detail,
47138
48221
  input: toolInput,
47139
48222
  partialInputJson: "",
@@ -47155,6 +48238,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47155
48238
  title: tool.title,
47156
48239
  ...tool.detail ? { detail: tool.detail } : {},
47157
48240
  data: {
48241
+ toolCallId: tool.itemId,
47158
48242
  toolName: tool.toolName,
47159
48243
  input: toolInput
47160
48244
  }
@@ -47192,6 +48276,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47192
48276
  const itemStatus = toolResult.isError ? "failed" : "completed";
47193
48277
  const toolUseResult = readClaudeToolUseResult(message);
47194
48278
  const toolData = {
48279
+ toolCallId: tool.itemId,
47195
48280
  toolName: tool.toolName,
47196
48281
  input: tool.input,
47197
48282
  result: toolResult.block
@@ -47852,7 +48937,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47852
48937
  behavior: "allow",
47853
48938
  updatedInput: {
47854
48939
  questions: toolInput.questions,
47855
- answers
48940
+ answers: formatAskUserQuestionAnswers(answers)
47856
48941
  }
47857
48942
  };
47858
48943
  });
@@ -47981,10 +49066,13 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47981
49066
  auto: "auto",
47982
49067
  "full-access": "bypassPermissions"
47983
49068
  }[input.runtimeMode];
49069
+ const disabledSkills = options?.resolveDisabledSkills === void 0 ? [] : yield* options.resolveDisabledSkills;
49070
+ const skillOverrides = disabledSkills.length === 0 ? void 0 : buildSkillOverrides(disabledSkills, yield* discoverClaudeSkills(claudeSettings, input.cwd, claudeEnvironment).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path)));
47984
49071
  const settings = {
47985
49072
  ...typeof thinking === "boolean" ? { alwaysThinkingEnabled: thinking } : {},
47986
49073
  ...fastMode ? { fastMode: true } : {},
47987
- ...ultracode ? { ultracode: true } : {}
49074
+ ...ultracode ? { ultracode: true } : {},
49075
+ ...skillOverrides ? { skillOverrides } : {}
47988
49076
  };
47989
49077
  const mcpSession = readMcpProviderSession(input.threadId);
47990
49078
  const externalMcpServers = options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers;
@@ -47994,7 +49082,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
47994
49082
  pathToClaudeCodeExecutable: claudeBinaryPath,
47995
49083
  systemPrompt: {
47996
49084
  type: "preset",
47997
- preset: "claude_code"
49085
+ preset: "claude_code",
49086
+ ...claudeSettings.narrateBeforeTools ? { append: NARRATE_BEFORE_TOOLS_PROMPT } : {}
47998
49087
  },
47999
49088
  settingSources: [...CLAUDE_SETTING_SOURCES],
48000
49089
  ...effectiveEffort ? { effort: effectiveEffort } : {},
@@ -48036,6 +49125,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
48036
49125
  "claude.query.include_partial_messages": true,
48037
49126
  "claude.query.additional_directories": input.cwd ? [input.cwd] : [],
48038
49127
  "claude.query.setting_sources": [...CLAUDE_SETTING_SOURCES],
49128
+ "claude.query.narrate_before_tools": claudeSettings.narrateBeforeTools,
48039
49129
  "claude.query.settings_json": encodeJsonStringForDiagnostics$2(settings) ?? "",
48040
49130
  "claude.query.extra_args_json": encodeJsonStringForDiagnostics$2(extraArgs) ?? "",
48041
49131
  "claude.query.path_to_executable": claudeBinaryPath
@@ -48085,7 +49175,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
48085
49175
  inFlightTools,
48086
49176
  claudeTasks,
48087
49177
  turnState: void 0,
48088
- pendingTurnRequests: [],
49178
+ pendingTurnRequest: void 0,
49179
+ steeredMessages: /* @__PURE__ */ new Map(),
48089
49180
  lastKnownContextWindow: initialContextWindow,
48090
49181
  lastKnownTokenUsage: void 0,
48091
49182
  lastKnownTotalProcessedTokens: void 0,
@@ -48154,16 +49245,19 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
48154
49245
  boundInstanceId
48155
49246
  });
48156
49247
  if (context.turnState) {
48157
- const turnId = TurnId.make(yield* randomUUIDv4);
48158
- context.pendingTurnRequests.push({
48159
- turnId,
48160
- message,
48161
- modelSelection,
48162
- interactionMode: input.interactionMode
48163
- });
49248
+ const steerUuid = yield* randomUUIDv4;
49249
+ const steered = {
49250
+ ...message,
49251
+ uuid: steerUuid
49252
+ };
49253
+ context.steeredMessages.set(steerUuid, steered);
49254
+ yield* Queue.offer(context.promptQueue, {
49255
+ type: "message",
49256
+ message: steered
49257
+ }).pipe(Effect.mapError((cause) => toRequestError$1(input.threadId, "turn/start", cause)));
48164
49258
  return {
48165
49259
  threadId: context.session.threadId,
48166
- turnId,
49260
+ turnId: context.turnState.turnId,
48167
49261
  ...context.session.resumeCursor !== void 0 ? { resumeCursor: context.session.resumeCursor } : {}
48168
49262
  };
48169
49263
  }
@@ -48183,6 +49277,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
48183
49277
  const interruptTurn = Effect.fn("interruptTurn")(function* (threadId, _turnId) {
48184
49278
  const context = yield* requireSession(threadId);
48185
49279
  context.interruptRequested = true;
49280
+ yield* reclaimSteeredMessages(context);
48186
49281
  yield* Effect.tryPromise({
48187
49282
  try: () => context.query.interrupt(),
48188
49283
  catch: (cause) => toRequestError$1(threadId, "turn/interrupt", cause)
@@ -48346,7 +49441,8 @@ function mergeProviderInstanceEnvironment(environment, baseEnv = process.env) {
48346
49441
  function makeProviderSnapshotSettings(provider, settings) {
48347
49442
  return {
48348
49443
  provider,
48349
- enableProviderUpdateChecks: settings.enableProviderUpdateChecks
49444
+ enableProviderUpdateChecks: settings.enableProviderUpdateChecks,
49445
+ disabledSkills: settings.disabledSkills
48350
49446
  };
48351
49447
  }
48352
49448
  function haveProviderSnapshotSettingsChanged(previous, next) {
@@ -48438,10 +49534,13 @@ const ClaudeDriver = {
48438
49534
  accentColor,
48439
49535
  continuationGroupKey
48440
49536
  });
49537
+ const mcpRegistry = yield* McpRegistry;
49538
+ const resolveDisabledSkills = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => []));
48441
49539
  const adapter = yield* makeClaudeAdapter(effectiveConfig, {
48442
49540
  instanceId,
48443
49541
  environment: processEnv,
48444
- resolveMcpServers: (yield* McpRegistry).resolveForSession,
49542
+ resolveMcpServers: mcpRegistry.resolveForSession,
49543
+ resolveDisabledSkills,
48445
49544
  ...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
48446
49545
  });
48447
49546
  const textGeneration = yield* makeClaudeTextGeneration(effectiveConfig, processEnv);
@@ -48451,7 +49550,7 @@ const ClaudeDriver = {
48451
49550
  lookup: () => probeClaudeCapabilities(effectiveConfig, processEnv, cwd).pipe(Effect.provideService(Path.Path, path))
48452
49551
  });
48453
49552
  const capabilitiesCacheKey = yield* makeClaudeCapabilitiesCacheKey(effectiveConfig, cwd);
48454
- const checkProvider = checkClaudeProviderStatus(effectiveConfig, () => Cache.get(capabilitiesProbeCache, capabilitiesCacheKey), processEnv, cwd).pipe(Effect.map(stampIdentity), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path));
49553
+ const checkProvider = resolveDisabledSkills.pipe(Effect.flatMap((disabledSkills) => checkClaudeProviderStatus(effectiveConfig, () => Cache.get(capabilitiesProbeCache, capabilitiesCacheKey), processEnv, cwd, disabledSkills)), Effect.map(stampIdentity), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path));
48455
49554
  const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
48456
49555
  const snapshot = yield* makeManagedServerProvider({
48457
49556
  maintenanceCapabilities,
@@ -83338,7 +84437,7 @@ const ObservabilityLive = Layer.unwrap(Effect.gen(function* () {
83338
84437
  sink,
83339
84438
  ...delegate ? { delegate } : {}
83340
84439
  });
83341
- return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$39(sink));
84440
+ return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$40(sink));
83342
84441
  })).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(otlpSerializationLayer));
83343
84442
  const metricsLayer = config.otlpMetricsUrl === void 0 ? Layer.empty : OtlpMetrics.layer({
83344
84443
  url: config.otlpMetricsUrl,
@@ -83617,25 +84716,25 @@ const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReacto
83617
84716
  const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$3));
83618
84717
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
83619
84718
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));
83620
- const VcsDriverRegistryLayerLive = layer$35.pipe(Layer.provide(layer$36));
83621
- const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$37), Layer.provideMerge(VcsDriverRegistryLayerLive));
83622
- const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$37), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$31));
83623
- const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$37));
84719
+ const VcsDriverRegistryLayerLive = layer$36.pipe(Layer.provide(layer$37));
84720
+ const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$38), Layer.provideMerge(VcsDriverRegistryLayerLive));
84721
+ const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$38), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$32));
84722
+ const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$38));
83624
84723
  const GitWorkflowLayerLive = layer$12.pipe(Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(GitLayerLive));
83625
- const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$37), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
83626
- const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$37), Layer.provideMerge(VcsDriverRegistryLayerLive));
83627
- const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$36), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$10.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$11.pipe(Layer.provide(GitWorkflowLayerLive))));
83628
- const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$33), Layer.provideMerge(layer$34.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
83629
- const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$52));
84724
+ const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$38), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
84725
+ const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$38), Layer.provideMerge(VcsDriverRegistryLayerLive));
84726
+ const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$37), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$10.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$11.pipe(Layer.provide(GitWorkflowLayerLive))));
84727
+ const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$34), Layer.provideMerge(layer$35.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
84728
+ const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$53));
83630
84729
  const TerminalLayerLive = layer$29.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
83631
84730
  const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(PortScannerLayerLive));
83632
- const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$42));
83633
- const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$42), Layer.provide(WorkspaceEntriesLayerLive));
83634
- const WorkspaceLayerLive = Layer.mergeAll(layer$42, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
83635
- const ProjectFaviconResolverLayerLive = layer$40.pipe(Layer.provide(layer$42), Layer.provide(layer$41));
83636
- const AuthLayerLive = layer$60.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$64));
84731
+ const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$43));
84732
+ const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$43), Layer.provide(WorkspaceEntriesLayerLive));
84733
+ const WorkspaceLayerLive = Layer.mergeAll(layer$43, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
84734
+ const ProjectFaviconResolverLayerLive = layer$41.pipe(Layer.provide(layer$43), Layer.provide(layer$42));
84735
+ const AuthLayerLive = layer$61.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$65));
83637
84736
  const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
83638
- const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$50), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(layer$44), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$56), Layer.provideMerge(layer$64)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$47), Layer.provideMerge(layer$49), Layer.provideMerge(layer$48), Layer.provide(layer$68));
84737
+ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$58.pipe(Layer.provide(layer$65))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$52), Layer.provideMerge(layer$45), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(Layer.mergeAll(layer$57, layer$31)), Layer.provideMerge(layer$65)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$48), Layer.provideMerge(layer$50), Layer.provideMerge(layer$49), Layer.provide(layer$69));
83639
84738
  /**
83640
84739
  * Hub asset sync.
83641
84740
  *
@@ -83643,9 +84742,9 @@ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(Checkpo
83643
84742
  * not-configured shape, and every call reports that rather than failing — so
83644
84743
  * this costs a server with no hub nothing but a layer.
83645
84744
  */
83646
- const AssetSyncLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55));
83647
- const RuntimeServicesLive = layer$43.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
83648
- const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(TaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$28), Layer.provide(layer$45), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
84745
+ const AssetSyncLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56));
84746
+ const RuntimeServicesLive = layer$44.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
84747
+ const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(RoutedTaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$28), Layer.provide(layer$46), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
83649
84748
  const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
83650
84749
  const config = yield* ServerConfig$1;
83651
84750
  yield* fixPath();
@@ -83688,7 +84787,7 @@ const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
83688
84787
  cause,
83689
84788
  servePort: configured.servePort
83690
84789
  }))) : Effect.void)) : Layer.empty;
83691
- return Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$38), Layer.provideMerge(PlatformServicesLive));
84790
+ return Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$39), Layer.provideMerge(PlatformServicesLive));
83692
84791
  }));
83693
84792
  const runServer = Layer.launch(makeServerLayer);
83694
84793
  //#endregion
@@ -83850,11 +84949,11 @@ const reconcileWithNewBinary = Effect.fn("cli.selfUpdate.reconcileWithNewBinary"
83850
84949
  //#endregion
83851
84950
  //#region src/cli/service.ts
83852
84951
  const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
83853
- const bootServiceLayer = (config) => layer$46({
84952
+ const bootServiceLayer = (config) => layer$47({
83854
84953
  baseDir: config.baseDir,
83855
84954
  logsDir: config.logsDir,
83856
84955
  cliVersion: version
83857
- }).pipe(Layer.provide(layer$52));
84956
+ }).pipe(Layer.provide(layer$53));
83858
84957
  /** Install, update, or repair the service using the CLI version running this command. */
83859
84958
  const reconcileService = Effect.fn("cli.service.reconcile")(function* () {
83860
84959
  const service = yield* BootService;
@@ -83901,7 +85000,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* (flags, run, pr
83901
85000
  const baseDirNotice = yield* describeNonDefaultBaseDir(config.baseDir);
83902
85001
  if (baseDirNotice !== null) yield* Console.error(baseDirNotice);
83903
85002
  if (prepare !== void 0) yield* prepare(config);
83904
- return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$52)));
85003
+ return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$53)));
83905
85004
  });
83906
85005
  /**
83907
85006
  * Options the installed unit carries, rather than ones this command consumes.
@@ -84055,7 +85154,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
84055
85154
  ]));
84056
85155
  //#endregion
84057
85156
  //#region src/bin.ts
84058
- const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$68, FetchHttpClient.layer);
85157
+ const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$69, FetchHttpClient.layer);
84059
85158
  /**
84060
85159
  * Prints an update reminder before dispatching, at most once a day.
84061
85160
  *