@p4code/cli 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +1408 -400
- package/dist/client/assets/{DiffPanel-BrM3QqVj.js → DiffPanel-CseUmWvE.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-DXk_4dj4.js → FilePreviewPanel-Dm7l5FFh.js} +2 -2
- package/dist/client/assets/{PreviewPanel-DfngnmUt.js → PreviewPanel-DOyykNL9.js} +2 -2
- package/dist/client/assets/{angular-html-veA1EpVJ.js → angular-html-YobKPLun.js} +2 -2
- package/dist/client/assets/{angular-ts-CjhObYqk.js → angular-ts-Ac3I6TFV.js} +2 -2
- package/dist/client/assets/{apl-BaBqmioI.js → apl-Dwl73GTS.js} +2 -2
- package/dist/client/assets/arrow-right-BqDszij1.js +2 -0
- package/dist/client/assets/{astro-aZep3bUH.js → astro-KBgldnxZ.js} +2 -2
- package/dist/client/assets/{blade-B-PS40R8.js → blade-DTU2NGhh.js} +2 -2
- package/dist/client/assets/{c-Da6G9SJL.js → c-COF4kWCp.js} +2 -2
- package/dist/client/assets/{cobol-MFD_Q4Xa.js → cobol-BA7JsdNr.js} +2 -2
- package/dist/client/assets/{coffee-DOKaGG2H.js → coffee-PFN_w4qL.js} +2 -2
- package/dist/client/assets/{cpp-iD8OoA0X.js → cpp-CpT6w-g5.js} +2 -2
- package/dist/client/assets/{crystal-BgCh46iL.js → crystal-BrlaRT0B.js} +2 -2
- package/dist/client/assets/{css-C0AvK1Fp.js → css-C2LMO3MX.js} +2 -2
- package/dist/client/assets/{edge-DWC_9CO2.js → edge-DBkQ-Ajd.js} +2 -2
- package/dist/client/assets/{elixir-5ahlHJpU.js → elixir-EbSNDEx0.js} +2 -2
- package/dist/client/assets/{elm-B4b4KSk0.js → elm-CSs0Wzy2.js} +2 -2
- package/dist/client/assets/{erb-CNRZFKcV.js → erb-BBmMQG9b.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-V3SOLaB4.js → fileCommentAnnotations-CifvTzWP.js} +2 -2
- package/dist/client/assets/{git-rebase-AbfQBerd.js → git-rebase-C09BpZDw.js} +2 -2
- package/dist/client/assets/{glimmer-js-_dx97vsR.js → glimmer-js-D4Bqn6Yg.js} +2 -2
- package/dist/client/assets/{glimmer-ts-BFmDDwi5.js → glimmer-ts-fLCHL8o6.js} +2 -2
- package/dist/client/assets/{glsl-CXroyNbj.js → glsl-17sV9oGm.js} +2 -2
- package/dist/client/assets/{graphql-RU5TF49j.js → graphql-BqFquulN.js} +2 -2
- package/dist/client/assets/{hack-S6OyQkI3.js → hack-D6Jpv-j9.js} +2 -2
- package/dist/client/assets/{haml-CclbQxsV.js → haml-D0eo8PEJ.js} +2 -2
- package/dist/client/assets/{handlebars-DFD506ku.js → handlebars-HG0XNkEg.js} +2 -2
- package/dist/client/assets/{html-BtV0fnqE.js → html-Bh3padJ5.js} +2 -2
- package/dist/client/assets/{html-derivative-oHJ5ggVN.js → html-derivative-DVHw4MNX.js} +2 -2
- package/dist/client/assets/{http-C8JeRqCn.js → http-CYCXaOA5.js} +2 -2
- package/dist/client/assets/{hurl-C1cew6hc.js → hurl-D4oK46oh.js} +2 -2
- package/dist/client/assets/{index-5pO5drbg.js → index-CV_dK79d.js} +217 -202
- package/dist/client/assets/{java-CrUEPeR7.js → java-nvFYZOCN.js} +2 -2
- package/dist/client/assets/{javascript-DJT7psEs.js → javascript-CMBxjf-M.js} +2 -2
- package/dist/client/assets/{jinja-CXxzh_AI.js → jinja-vsPdkFFj.js} +2 -2
- package/dist/client/assets/{jison-BEgWlYuP.js → jison-uky2pPNE.js} +2 -2
- package/dist/client/assets/{json-DYMLm7oI.js → json-DOSqa2F1.js} +2 -2
- package/dist/client/assets/{jsx-C2i-6RBR.js → jsx-Ci7CVNQe.js} +2 -2
- package/dist/client/assets/{julia-268B5DSC.js → julia-J2m7N3_R.js} +2 -2
- package/dist/client/assets/{just-DHlfSR_g.js → just-BDy3oM_N.js} +2 -2
- package/dist/client/assets/{latex-hZJ97DlU.js → latex-CMD49F7D.js} +2 -2
- package/dist/client/assets/{liquid-BXWKgP9a.js → liquid-Dq6k2YW6.js} +2 -2
- package/dist/client/assets/{lua-DZpMPVk1.js → lua-CohtP6J8.js} +2 -2
- package/dist/client/assets/{marko-C4krJn5M.js → marko-C4V2zw1t.js} +2 -2
- package/dist/client/assets/{mdc-DPMybKM9.js → mdc-q1ZKjwYt.js} +2 -2
- package/dist/client/assets/{nginx-DS7EZxTP.js → nginx-D2TdEIZ3.js} +2 -2
- package/dist/client/assets/{nim-DQ5EIgDG.js → nim-BtWKpYQs.js} +2 -2
- package/dist/client/assets/{perl-BYRfkLcm.js → perl-Ci4Pso9h.js} +2 -2
- package/dist/client/assets/{php-CRmJir-2.js → php-CMzpcAju.js} +2 -2
- package/dist/client/assets/{pug-ChlH277L.js → pug-D0sbFhP6.js} +2 -2
- package/dist/client/assets/{qml-D03lc83e.js → qml-BfWXHzNc.js} +2 -2
- package/dist/client/assets/{r-B0iWOCwM.js → r-BN4rQDnz.js} +2 -2
- package/dist/client/assets/{razor-jqTSj5Nf.js → razor-D5iw68yr.js} +2 -2
- package/dist/client/assets/{regexp-BtdW0BtX.js → regexp-Ba0WieCb.js} +2 -2
- package/dist/client/assets/{rst-ODlKAfCn.js → rst-DQqlRfnq.js} +2 -2
- package/dist/client/assets/{ruby-D3-MIdGC.js → ruby-CReh9Gta.js} +2 -2
- package/dist/client/assets/{sas-CHM8NNaC.js → sas-CJ-Cpd8L.js} +2 -2
- package/dist/client/assets/{scss-C5u0ylVF.js → scss-CZp-cH6d.js} +2 -2
- package/dist/client/assets/{shellscript-e38vxsyD.js → shellscript-hCSuhhJZ.js} +2 -2
- package/dist/client/assets/{shellsession-C2xN505I.js → shellsession-BnDNcCDQ.js} +2 -2
- package/dist/client/assets/{soy-Cm15Zvq3.js → soy-BXarcWJY.js} +2 -2
- package/dist/client/assets/{sql-BpFtjTfW.js → sql-ADQCXhYd.js} +2 -2
- package/dist/client/assets/{stata-BlPPmFYy.js → stata-DSS4IpqF.js} +2 -2
- package/dist/client/assets/{surrealql-BCULE51k.js → surrealql-DuYcftNm.js} +2 -2
- package/dist/client/assets/{svelte-BNRSv2pn.js → svelte-CAXOFInl.js} +2 -2
- package/dist/client/assets/{templ-DoGq1v8F.js → templ-BvTIEIah.js} +2 -2
- package/dist/client/assets/{tex--cpmM4NW.js → tex-DpnVRTMM.js} +2 -2
- package/dist/client/assets/textarea-etKSOtDN.js +46 -0
- package/dist/client/assets/{ts-tags-D5eCylEK.js → ts-tags-4wlHlovT.js} +2 -2
- package/dist/client/assets/{tsx-Bu8K9CNP.js → tsx-CHDgELdy.js} +2 -2
- package/dist/client/assets/{twig-BZHf1w2v.js → twig-DPmx2UHF.js} +2 -2
- package/dist/client/assets/{typescript-CdzN72UO.js → typescript-CHO8bW-H.js} +2 -2
- package/dist/client/assets/{vue-BZJs2XXT.js → vue-CM19I03n.js} +2 -2
- package/dist/client/assets/{vue-html-zt0y5fO9.js → vue-html-ginfkxGQ.js} +2 -2
- package/dist/client/assets/{vue-vine-SsQX7Kll.js → vue-vine-CGdZxUwf.js} +2 -2
- package/dist/client/assets/{xml-BKlTVdhA.js → xml-B8CY16OL.js} +2 -2
- package/dist/client/assets/{xsl-B00O3UQ-.js → xsl-COHh7n8v.js} +2 -2
- package/dist/client/assets/{yaml-Dr4fa503.js → yaml-DXx-djat.js} +2 -2
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/dist/client/assets/arrow-right-Cvlj0Cvu.js +0 -2
- package/dist/client/assets/textarea-M0r5voiR.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$
|
|
124
|
+
const make$76 = () => {
|
|
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$75 = () => {
|
|
|
230
230
|
})
|
|
231
231
|
};
|
|
232
232
|
};
|
|
233
|
-
const layer$
|
|
233
|
+
const layer$72 = Layer.sync(NetService, make$76);
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region package.json
|
|
236
|
-
var version = "0.1.
|
|
236
|
+
var version = "0.1.19";
|
|
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$
|
|
258
|
-
const layer$
|
|
257
|
+
const make$75 = (config) => ServerConfig$1.of(config);
|
|
258
|
+
const layer$71 = (config) => Layer.succeed(ServerConfig$1, make$75(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");
|
|
@@ -3430,6 +3430,36 @@ const PreviewAutomationNetworkEntry = Schema.Struct({
|
|
|
3430
3430
|
errorText: Schema.optional(Schema.String),
|
|
3431
3431
|
timestamp: Schema.String
|
|
3432
3432
|
});
|
|
3433
|
+
/** Value of an accessibility state such as `expanded`, `checked` or `level`. */
|
|
3434
|
+
const PreviewAccessibilityState = Schema.Union([
|
|
3435
|
+
Schema.String,
|
|
3436
|
+
Schema.Number,
|
|
3437
|
+
Schema.Boolean
|
|
3438
|
+
]);
|
|
3439
|
+
/**
|
|
3440
|
+
* A node of the compacted accessibility tree. `parentId` points at the nearest
|
|
3441
|
+
* emitted ancestor, so the hierarchy is reconstructible without child lists.
|
|
3442
|
+
*/
|
|
3443
|
+
const PreviewAccessibilityNode = Schema.Struct({
|
|
3444
|
+
id: Schema.String,
|
|
3445
|
+
parentId: Schema.optional(Schema.String),
|
|
3446
|
+
role: Schema.String,
|
|
3447
|
+
name: Schema.optional(Schema.String),
|
|
3448
|
+
description: Schema.optional(Schema.String),
|
|
3449
|
+
value: Schema.optional(Schema.String),
|
|
3450
|
+
states: Schema.optional(Schema.Record(Schema.String, PreviewAccessibilityState))
|
|
3451
|
+
});
|
|
3452
|
+
/**
|
|
3453
|
+
* Semantic nodes only: ignored nodes, raw text nodes and unnamed layout
|
|
3454
|
+
* wrappers are dropped, and the node list is capped. `totalNodes` counts the
|
|
3455
|
+
* nodes that survived filtering, so `truncated` distinguishes a clipped tree
|
|
3456
|
+
* from a small page.
|
|
3457
|
+
*/
|
|
3458
|
+
const PreviewAccessibilitySnapshot = Schema.Struct({
|
|
3459
|
+
nodes: Schema.Array(PreviewAccessibilityNode),
|
|
3460
|
+
totalNodes: Schema.Int,
|
|
3461
|
+
truncated: Schema.Boolean
|
|
3462
|
+
});
|
|
3433
3463
|
const PreviewAutomationActionEvent = Schema.Struct({
|
|
3434
3464
|
id: Schema.String,
|
|
3435
3465
|
action: Schema.String,
|
|
@@ -3449,7 +3479,7 @@ const PreviewAutomationSnapshot = Schema.Struct({
|
|
|
3449
3479
|
loading: Schema.Boolean,
|
|
3450
3480
|
visibleText: Schema.String,
|
|
3451
3481
|
interactiveElements: Schema.Array(PreviewAutomationElement),
|
|
3452
|
-
accessibilityTree:
|
|
3482
|
+
accessibilityTree: PreviewAccessibilitySnapshot,
|
|
3453
3483
|
consoleEntries: Schema.Array(PreviewAutomationConsoleEntry),
|
|
3454
3484
|
networkEntries: Schema.Array(PreviewAutomationNetworkEntry),
|
|
3455
3485
|
actionTimeline: Schema.Array(PreviewAutomationActionEvent),
|
|
@@ -4773,6 +4803,7 @@ Schema$1.Literals([
|
|
|
4773
4803
|
"config.warning",
|
|
4774
4804
|
"deprecation.notice",
|
|
4775
4805
|
"files.persisted",
|
|
4806
|
+
"code.published",
|
|
4776
4807
|
"runtime.warning",
|
|
4777
4808
|
"runtime.error"
|
|
4778
4809
|
]);
|
|
@@ -4821,6 +4852,7 @@ const ModelReroutedType = Schema$1.Literal("model.rerouted");
|
|
|
4821
4852
|
const ConfigWarningType = Schema$1.Literal("config.warning");
|
|
4822
4853
|
const DeprecationNoticeType = Schema$1.Literal("deprecation.notice");
|
|
4823
4854
|
const FilesPersistedType = Schema$1.Literal("files.persisted");
|
|
4855
|
+
const CodePublishedType = Schema$1.Literal("code.published");
|
|
4824
4856
|
const ToolDeniedType = Schema$1.Literal("tool.denied");
|
|
4825
4857
|
const RuntimeWarningType = Schema$1.Literal("runtime.warning");
|
|
4826
4858
|
const RuntimeErrorType = Schema$1.Literal("runtime.error");
|
|
@@ -5123,6 +5155,12 @@ const FilesPersistedPayload = Schema$1.Struct({
|
|
|
5123
5155
|
error: TrimmedNonEmptyStringSchema$1
|
|
5124
5156
|
})))
|
|
5125
5157
|
});
|
|
5158
|
+
const CodePublishedPayload = Schema$1.Struct({
|
|
5159
|
+
hostingProvider: TrimmedNonEmptyStringSchema$1,
|
|
5160
|
+
url: TrimmedNonEmptyStringSchema$1,
|
|
5161
|
+
repo: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
5162
|
+
identifier: Schema$1.optional(TrimmedNonEmptyStringSchema$1)
|
|
5163
|
+
});
|
|
5126
5164
|
const ToolDeniedPayload = Schema$1.Struct({
|
|
5127
5165
|
toolName: TrimmedNonEmptyStringSchema$1,
|
|
5128
5166
|
toolUseId: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
@@ -5363,6 +5401,11 @@ const ProviderRuntimeFilesPersistedEvent = Schema$1.Struct({
|
|
|
5363
5401
|
type: FilesPersistedType,
|
|
5364
5402
|
payload: FilesPersistedPayload
|
|
5365
5403
|
});
|
|
5404
|
+
const ProviderRuntimeCodePublishedEvent = Schema$1.Struct({
|
|
5405
|
+
...ProviderRuntimeEventBase.fields,
|
|
5406
|
+
type: CodePublishedType,
|
|
5407
|
+
payload: CodePublishedPayload
|
|
5408
|
+
});
|
|
5366
5409
|
const ProviderRuntimeToolDeniedEvent = Schema$1.Struct({
|
|
5367
5410
|
...ProviderRuntimeEventBase.fields,
|
|
5368
5411
|
type: ToolDeniedType,
|
|
@@ -5424,6 +5467,7 @@ Schema$1.Union([
|
|
|
5424
5467
|
ProviderRuntimeConfigWarningEvent,
|
|
5425
5468
|
ProviderRuntimeDeprecationNoticeEvent,
|
|
5426
5469
|
ProviderRuntimeFilesPersistedEvent,
|
|
5470
|
+
ProviderRuntimeCodePublishedEvent,
|
|
5427
5471
|
ProviderRuntimeToolDeniedEvent,
|
|
5428
5472
|
ProviderRuntimeWarningEvent,
|
|
5429
5473
|
ProviderRuntimeErrorEvent
|
|
@@ -5795,6 +5839,20 @@ const ServerSettings = Schema$1.Struct({
|
|
|
5795
5839
|
* back for sessions started by the Claude provider.
|
|
5796
5840
|
*/
|
|
5797
5841
|
enableToolCallNarration: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
5842
|
+
/**
|
|
5843
|
+
* Whether the model may spawn subagents without being asked to.
|
|
5844
|
+
*
|
|
5845
|
+
* On by default: a bounded search or review whose result is far smaller than
|
|
5846
|
+
* the reading it takes to produce is exactly the work a subagent is for, and
|
|
5847
|
+
* an agent that has to ask first will simply do it inline instead.
|
|
5848
|
+
*
|
|
5849
|
+
* Stated in the appended prompt whichever way it is set, rather than only
|
|
5850
|
+
* when off. The provider's own preset prompt carries an instruction not to
|
|
5851
|
+
* spawn subagents unless asked, and p4code requests that preset wholesale —
|
|
5852
|
+
* so there is nothing to withhold, and only a later, more specific
|
|
5853
|
+
* instruction can override an earlier general one.
|
|
5854
|
+
*/
|
|
5855
|
+
enableUnpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
5798
5856
|
automaticGitFetchInterval: Schema$1.DurationFromMillis.pipe(Schema$1.withDecodingDefault(Effect.succeed(Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL)))),
|
|
5799
5857
|
defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
|
|
5800
5858
|
newWorktreesStartFromOrigin: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
@@ -5929,6 +5987,7 @@ const ServerSettingsPatch = Schema$1.Struct({
|
|
|
5929
5987
|
enableAssistantStreaming: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5930
5988
|
enableProviderUpdateChecks: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5931
5989
|
enableToolCallNarration: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5990
|
+
enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5932
5991
|
automaticGitFetchInterval: Schema$1.optionalKey(Schema$1.DurationFromMillis),
|
|
5933
5992
|
defaultThreadEnvMode: Schema$1.optionalKey(ThreadEnvMode),
|
|
5934
5993
|
newWorktreesStartFromOrigin: Schema$1.optionalKey(Schema$1.Boolean),
|
|
@@ -7189,6 +7248,21 @@ const RepositoryKeyField = TrimmedNonEmptyString.pipe(Schema$1.annotateEncoded({
|
|
|
7189
7248
|
* placed by hand and falls back to the priority/recency order.
|
|
7190
7249
|
*/
|
|
7191
7250
|
const OrderKeyField = TrimmedNonEmptyString.pipe(Schema$1.annotateEncoded({ description: "Manual board position key. Lexicographic: smaller sorts higher in the column. Null means no manual position." }));
|
|
7251
|
+
/**
|
|
7252
|
+
* Where a task came from when it did not originate on this board.
|
|
7253
|
+
*
|
|
7254
|
+
* The identifier is the tracker's own, not ours: `MOBILE-12262` reads the same
|
|
7255
|
+
* in p4code as it does in Linear, in a commit message, and in conversation,
|
|
7256
|
+
* which is the whole point of carrying it. `url` is what the tracker itself
|
|
7257
|
+
* reported, never a guess assembled from the identifier - a Linear URL embeds
|
|
7258
|
+
* the workspace slug, so a constructed one would 404.
|
|
7259
|
+
*/
|
|
7260
|
+
const TaskExternalRef = Schema$1.Struct({
|
|
7261
|
+
source: Schema$1.Literals(["linear"]),
|
|
7262
|
+
identifier: TrimmedNonEmptyString,
|
|
7263
|
+
url: TrimmedNonEmptyString
|
|
7264
|
+
});
|
|
7265
|
+
const ExternalRefField = TaskExternalRef.pipe(Schema$1.annotateEncoded({ description: "The tracker issue this task mirrors, e.g. a Linear ticket. Null for a task that originated on this board." }));
|
|
7192
7266
|
const LabelsField = Schema$1.Array(TaskLabel).check(Schema$1.isMaxLength(TASK_LABELS_MAX_COUNT)).pipe(Schema$1.annotateEncoded({ description: "Free-form tags used to group and filter tasks." }));
|
|
7193
7267
|
const TaskIdField = TaskId.pipe(Schema$1.annotateEncoded({ description: "Id of the task to act on - the UUID or the readable id (e.g. P4-12)." }));
|
|
7194
7268
|
const Task = Schema$1.Struct({
|
|
@@ -7230,6 +7304,13 @@ const Task = Schema$1.Struct({
|
|
|
7230
7304
|
* to a history.
|
|
7231
7305
|
*/
|
|
7232
7306
|
threadId: Schema$1.NullOr(ThreadId),
|
|
7307
|
+
/**
|
|
7308
|
+
* The tracker issue this task mirrors, or `null` for a task that was filed
|
|
7309
|
+
* here. Nullable-with-default for the same version-skew reason as
|
|
7310
|
+
* `readableId` above: a row from a store without the migration decodes to
|
|
7311
|
+
* `null` rather than failing the whole board.
|
|
7312
|
+
*/
|
|
7313
|
+
externalRef: Schema$1.NullOr(TaskExternalRef).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
|
|
7233
7314
|
title: TrimmedNonEmptyString.check(Schema$1.isMaxLength(TASK_TITLE_MAX_LENGTH)),
|
|
7234
7315
|
body: Schema$1.String,
|
|
7235
7316
|
status: StoredStatusField,
|
|
@@ -7243,6 +7324,7 @@ const TaskCreateInput = Schema$1.Struct({
|
|
|
7243
7324
|
projectId: Schema$1.optional(ProjectIdField),
|
|
7244
7325
|
repositoryKey: Schema$1.optional(RepositoryKeyField),
|
|
7245
7326
|
threadId: Schema$1.optional(ThreadIdField),
|
|
7327
|
+
externalRef: Schema$1.optional(ExternalRefField),
|
|
7246
7328
|
title: TitleField,
|
|
7247
7329
|
body: Schema$1.optional(BodyField),
|
|
7248
7330
|
status: Schema$1.optional(StatusField),
|
|
@@ -7281,6 +7363,7 @@ const TaskUpdateInput = Schema$1.Struct({
|
|
|
7281
7363
|
projectId: Schema$1.optional(Schema$1.NullOr(ProjectIdField)),
|
|
7282
7364
|
repositoryKey: Schema$1.optional(Schema$1.NullOr(RepositoryKeyField)),
|
|
7283
7365
|
threadId: Schema$1.optional(Schema$1.NullOr(ThreadIdField)),
|
|
7366
|
+
externalRef: Schema$1.optional(Schema$1.NullOr(ExternalRefField)),
|
|
7284
7367
|
title: Schema$1.optional(TitleField),
|
|
7285
7368
|
body: Schema$1.optional(BodyField),
|
|
7286
7369
|
status: Schema$1.optional(StatusField),
|
|
@@ -7646,6 +7729,58 @@ var ProjectWriteFileError = class extends Schema$1.TaggedErrorClass()("ProjectWr
|
|
|
7646
7729
|
}
|
|
7647
7730
|
};
|
|
7648
7731
|
//#endregion
|
|
7732
|
+
//#region ../../packages/contracts/src/ticket.ts
|
|
7733
|
+
/**
|
|
7734
|
+
* Reading an issue out of an external tracker.
|
|
7735
|
+
*
|
|
7736
|
+
* One call, deliberately: p4code does not sync trackers, it recognizes a ticket
|
|
7737
|
+
* somebody pasted and asks the tracker what it is called. Everything durable
|
|
7738
|
+
* that comes back is stored on a task as its {@link TaskExternalRef}.
|
|
7739
|
+
*
|
|
7740
|
+
* The tracker is reached through the MCP server the user already registered for
|
|
7741
|
+
* it, so there is no second place to configure credentials and no p4code-owned
|
|
7742
|
+
* API key. That also bounds what this can ever answer: a tracker with no MCP
|
|
7743
|
+
* server registered, or one never signed in to on this machine, is reported as
|
|
7744
|
+
* unconfigured rather than silently returning nothing.
|
|
7745
|
+
*
|
|
7746
|
+
* @module contracts/ticket
|
|
7747
|
+
*/
|
|
7748
|
+
/**
|
|
7749
|
+
* A tracker reference as it was typed, before anything has confirmed it exists.
|
|
7750
|
+
*
|
|
7751
|
+
* Deliberately not the parsed `{ source, identifier }` pair: what a user pastes
|
|
7752
|
+
* is a URL as often as an id, and which tracker a bare `MOBILE-12262` belongs
|
|
7753
|
+
* to is a question only the resolver can answer.
|
|
7754
|
+
*/
|
|
7755
|
+
const TicketResolveInput = Schema$1.Struct({ reference: TrimmedNonEmptyString });
|
|
7756
|
+
const TicketResolveResult = Schema$1.Struct({
|
|
7757
|
+
externalRef: TaskExternalRef,
|
|
7758
|
+
/** The issue title as the tracker reports it, for the task that mirrors it. */
|
|
7759
|
+
title: TrimmedNonEmptyString
|
|
7760
|
+
});
|
|
7761
|
+
/**
|
|
7762
|
+
* Why a reference could not be turned into a ticket.
|
|
7763
|
+
*
|
|
7764
|
+
* The reason is separate from the detail because the client acts on it: an
|
|
7765
|
+
* unconfigured tracker is a thing the user can fix in Settings and is worth
|
|
7766
|
+
* saying so about, while `unavailable` is a retry and `not_found` usually means
|
|
7767
|
+
* a typo. `detail` is the sentence to show; the reason decides the shape.
|
|
7768
|
+
*/
|
|
7769
|
+
const TicketResolveFailureReason = Schema$1.Literals([
|
|
7770
|
+
"not_configured",
|
|
7771
|
+
"not_authorized",
|
|
7772
|
+
"not_found",
|
|
7773
|
+
"unavailable"
|
|
7774
|
+
]);
|
|
7775
|
+
var TicketResolveError = class extends Schema$1.TaggedErrorClass()("TicketResolveError", {
|
|
7776
|
+
reason: TicketResolveFailureReason,
|
|
7777
|
+
detail: Schema$1.String
|
|
7778
|
+
}) {
|
|
7779
|
+
get message() {
|
|
7780
|
+
return this.detail;
|
|
7781
|
+
}
|
|
7782
|
+
};
|
|
7783
|
+
//#endregion
|
|
7649
7784
|
//#region ../../packages/contracts/src/threadControl.ts
|
|
7650
7785
|
/**
|
|
7651
7786
|
* Thread control over MCP - the self-scoped toolkit an agent uses to act on
|
|
@@ -7820,11 +7955,17 @@ const AGENT_ASSET_TOTAL_MAX_BYTES = 4 * 1024 * 1024;
|
|
|
7820
7955
|
* directory rather than the provider's. The definition travels; the bearer
|
|
7821
7956
|
* tokens and API keys it references do not, and stay in each machine's own
|
|
7822
7957
|
* secret store.
|
|
7958
|
+
* - `agent` — a subagent definition, a single markdown file in
|
|
7959
|
+
* `<configDir>/agents`. The name is the filename, as for `memory`. This is the
|
|
7960
|
+
* only artifact that bounds what a subagent is handed and what it hands back,
|
|
7961
|
+
* because p4code observes subagent runs rather than constructing their
|
|
7962
|
+
* prompts — so the I/O contract can only ship as a file the provider reads.
|
|
7823
7963
|
*/
|
|
7824
7964
|
const AgentAssetKind = Schema$1.Literals([
|
|
7825
7965
|
"skill",
|
|
7826
7966
|
"memory",
|
|
7827
|
-
"mcp"
|
|
7967
|
+
"mcp",
|
|
7968
|
+
"agent"
|
|
7828
7969
|
]);
|
|
7829
7970
|
/**
|
|
7830
7971
|
* An asset's name, which for a skill is also its directory name.
|
|
@@ -8691,6 +8832,7 @@ const WS_METHODS = {
|
|
|
8691
8832
|
tasksUpdate: "tasks.update",
|
|
8692
8833
|
tasksDelete: "tasks.delete",
|
|
8693
8834
|
tasksStartThread: "tasks.startThread",
|
|
8835
|
+
ticketsResolve: "tickets.resolve",
|
|
8694
8836
|
hubGetSyncStatus: "hub.getSyncStatus",
|
|
8695
8837
|
hubConnect: "hub.connect",
|
|
8696
8838
|
hubDisconnect: "hub.disconnect",
|
|
@@ -8998,6 +9140,17 @@ const WsMcpOAuthDisconnectRpc = Rpc.make(WS_METHODS.mcpOAuthDisconnect, {
|
|
|
8998
9140
|
error: Schema$1.Union([McpRegistryError, EnvironmentAuthorizationError])
|
|
8999
9141
|
});
|
|
9000
9142
|
/**
|
|
9143
|
+
* Asks the tracker what a pasted reference is, without writing anything.
|
|
9144
|
+
*
|
|
9145
|
+
* Read-only on purpose: the caller decides whether the answer becomes a task.
|
|
9146
|
+
* A resolve that filed a row would make an accidental paste a board entry.
|
|
9147
|
+
*/
|
|
9148
|
+
const WsTicketsResolveRpc = Rpc.make(WS_METHODS.ticketsResolve, {
|
|
9149
|
+
payload: TicketResolveInput,
|
|
9150
|
+
success: TicketResolveResult,
|
|
9151
|
+
error: Schema$1.Union([TicketResolveError, EnvironmentAuthorizationError])
|
|
9152
|
+
});
|
|
9153
|
+
/**
|
|
9001
9154
|
* The board, then every change to it, for as long as the client stays
|
|
9002
9155
|
* subscribed. The leading snapshot is what makes this sufficient on its own —
|
|
9003
9156
|
* a client does not list first, so there is no window between the two in which
|
|
@@ -9375,7 +9528,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
9375
9528
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
9376
9529
|
stream: true
|
|
9377
9530
|
});
|
|
9378
|
-
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, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
9531
|
+
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, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTicketsResolveRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
9379
9532
|
//#endregion
|
|
9380
9533
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
9381
9534
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -9521,7 +9674,7 @@ function deriveAuthClientMetadata(input) {
|
|
|
9521
9674
|
//#endregion
|
|
9522
9675
|
//#region src/auth/EnvironmentAuthPolicy.ts
|
|
9523
9676
|
var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
|
|
9524
|
-
const make$
|
|
9677
|
+
const make$74 = Effect.gen(function* () {
|
|
9525
9678
|
const config = yield* ServerConfig$1;
|
|
9526
9679
|
const isRemoteReachable = isRemoteReachableHost(config.host);
|
|
9527
9680
|
const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
|
|
@@ -9539,7 +9692,7 @@ const make$73 = Effect.gen(function* () {
|
|
|
9539
9692
|
};
|
|
9540
9693
|
return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
|
|
9541
9694
|
});
|
|
9542
|
-
const layer$
|
|
9695
|
+
const layer$70 = Layer.effect(EnvironmentAuthPolicy, make$74);
|
|
9543
9696
|
//#endregion
|
|
9544
9697
|
//#region src/persistence/Errors.ts
|
|
9545
9698
|
function summarizeSchemaIssue(issue) {
|
|
@@ -9720,7 +9873,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
|
|
|
9720
9873
|
cause
|
|
9721
9874
|
});
|
|
9722
9875
|
}
|
|
9723
|
-
const make$
|
|
9876
|
+
const make$73 = Effect.gen(function* () {
|
|
9724
9877
|
const sql = yield* SqlClient.SqlClient;
|
|
9725
9878
|
const createSessionRow = SqlSchema.void({
|
|
9726
9879
|
Request: CreateAuthSessionInput,
|
|
@@ -9854,7 +10007,7 @@ const make$72 = Effect.gen(function* () {
|
|
|
9854
10007
|
setLastConnectedAt
|
|
9855
10008
|
};
|
|
9856
10009
|
});
|
|
9857
|
-
const layer$
|
|
10010
|
+
const layer$69 = Layer.effect(AuthSessionRepository, make$73);
|
|
9858
10011
|
//#endregion
|
|
9859
10012
|
//#region src/auth/ServerSecretStore.ts
|
|
9860
10013
|
const secretStoreErrorContext = {
|
|
@@ -9921,7 +10074,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
|
|
|
9921
10074
|
const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
|
|
9922
10075
|
const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
|
|
9923
10076
|
var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
|
|
9924
|
-
const make$
|
|
10077
|
+
const make$72 = Effect.gen(function* () {
|
|
9925
10078
|
const crypto = yield* Crypto.Crypto;
|
|
9926
10079
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
9927
10080
|
const path = yield* Path.Path;
|
|
@@ -9991,7 +10144,7 @@ const make$71 = Effect.gen(function* () {
|
|
|
9991
10144
|
remove
|
|
9992
10145
|
});
|
|
9993
10146
|
});
|
|
9994
|
-
const layer$
|
|
10147
|
+
const layer$68 = Layer.effect(ServerSecretStore, make$72);
|
|
9995
10148
|
//#endregion
|
|
9996
10149
|
//#region src/auth/SessionStore.ts
|
|
9997
10150
|
var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
|
|
@@ -10229,7 +10382,7 @@ function toAuthClientSession(input) {
|
|
|
10229
10382
|
current: false
|
|
10230
10383
|
};
|
|
10231
10384
|
}
|
|
10232
|
-
const make$
|
|
10385
|
+
const make$71 = Effect.gen(function* () {
|
|
10233
10386
|
const crypto = yield* Crypto.Crypto;
|
|
10234
10387
|
const serverConfig = yield* ServerConfig$1;
|
|
10235
10388
|
const secretStore = yield* ServerSecretStore;
|
|
@@ -10543,7 +10696,7 @@ const make$70 = Effect.gen(function* () {
|
|
|
10543
10696
|
markDisconnected
|
|
10544
10697
|
});
|
|
10545
10698
|
});
|
|
10546
|
-
const layer$
|
|
10699
|
+
const layer$67 = Layer.effect(SessionStore, make$71).pipe(Layer.provideMerge(layer$69));
|
|
10547
10700
|
//#endregion
|
|
10548
10701
|
//#region src/persistence/AuthPairingLinks.ts
|
|
10549
10702
|
const AuthPairingLinkRecord = Schema$1.Struct({
|
|
@@ -10604,7 +10757,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
|
|
|
10604
10757
|
cause
|
|
10605
10758
|
});
|
|
10606
10759
|
}
|
|
10607
|
-
const make$
|
|
10760
|
+
const make$70 = Effect.gen(function* () {
|
|
10608
10761
|
const sql = yield* SqlClient.SqlClient;
|
|
10609
10762
|
const createPairingLinkRow = SqlSchema.void({
|
|
10610
10763
|
Request: CreateAuthPairingLinkInput,
|
|
@@ -10739,7 +10892,7 @@ const make$69 = Effect.gen(function* () {
|
|
|
10739
10892
|
getByCredential
|
|
10740
10893
|
};
|
|
10741
10894
|
});
|
|
10742
|
-
const layer$
|
|
10895
|
+
const layer$66 = Layer.effect(AuthPairingLinkRepository, make$70);
|
|
10743
10896
|
//#endregion
|
|
10744
10897
|
//#region src/auth/PairingGrantStore.ts
|
|
10745
10898
|
var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
|
|
@@ -10834,7 +10987,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
|
|
|
10834
10987
|
const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
10835
10988
|
const PAIRING_TOKEN_LENGTH = 12;
|
|
10836
10989
|
const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
|
|
10837
|
-
const make$
|
|
10990
|
+
const make$69 = Effect.gen(function* () {
|
|
10838
10991
|
const crypto = yield* Crypto.Crypto;
|
|
10839
10992
|
const config = yield* ServerConfig$1;
|
|
10840
10993
|
const pairingLinks = yield* AuthPairingLinkRepository;
|
|
@@ -11032,7 +11185,7 @@ const make$68 = Effect.gen(function* () {
|
|
|
11032
11185
|
consume
|
|
11033
11186
|
});
|
|
11034
11187
|
});
|
|
11035
|
-
const layer$
|
|
11188
|
+
const layer$65 = Layer.effect(PairingGrantStore, make$69).pipe(Layer.provideMerge(layer$66));
|
|
11036
11189
|
//#endregion
|
|
11037
11190
|
//#region src/persistence/Migrations/001_OrchestrationEvents.ts
|
|
11038
11191
|
var _001_OrchestrationEvents_default = Effect.gen(function* () {
|
|
@@ -12485,6 +12638,28 @@ var _039_ProjectionThreadsCompressMode_default = Effect.gen(function* () {
|
|
|
12485
12638
|
`;
|
|
12486
12639
|
});
|
|
12487
12640
|
//#endregion
|
|
12641
|
+
//#region src/persistence/Migrations/040_TaskExternalRef.ts
|
|
12642
|
+
/**
|
|
12643
|
+
* The tracker issue a task mirrors, for tasks that did not start on this board.
|
|
12644
|
+
*
|
|
12645
|
+
* Stored as one JSON blob rather than three columns because nothing queries
|
|
12646
|
+
* inside it except the uniqueness check below, and a tracker's shape is not
|
|
12647
|
+
* ours to normalize - a second source will bring fields Linear does not have.
|
|
12648
|
+
*
|
|
12649
|
+
* The unique index is over the identifier alone, extracted from the JSON: it is
|
|
12650
|
+
* what stops a second paste of the same ticket from filing a duplicate task.
|
|
12651
|
+
* Partial, so the many rows with no external ref do not collide on NULL.
|
|
12652
|
+
*/
|
|
12653
|
+
var _040_TaskExternalRef_default = Effect.gen(function* () {
|
|
12654
|
+
const sql = yield* SqlClient.SqlClient;
|
|
12655
|
+
yield* sql`ALTER TABLE tasks ADD COLUMN external_ref_json TEXT`;
|
|
12656
|
+
yield* sql`
|
|
12657
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_external_ref_identifier
|
|
12658
|
+
ON tasks(json_extract(external_ref_json, '$.identifier'))
|
|
12659
|
+
WHERE external_ref_json IS NOT NULL
|
|
12660
|
+
`;
|
|
12661
|
+
});
|
|
12662
|
+
//#endregion
|
|
12488
12663
|
//#region src/persistence/Migrations.ts
|
|
12489
12664
|
/**
|
|
12490
12665
|
* MigrationsLive - Migration runner with inline loader
|
|
@@ -12700,6 +12875,11 @@ const migrationEntries = [
|
|
|
12700
12875
|
39,
|
|
12701
12876
|
"ProjectionThreadsCompressMode",
|
|
12702
12877
|
_039_ProjectionThreadsCompressMode_default
|
|
12878
|
+
],
|
|
12879
|
+
[
|
|
12880
|
+
40,
|
|
12881
|
+
"TaskExternalRef",
|
|
12882
|
+
_040_TaskExternalRef_default
|
|
12703
12883
|
]
|
|
12704
12884
|
];
|
|
12705
12885
|
const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
|
|
@@ -12887,7 +13067,7 @@ function parseBearerToken(request) {
|
|
|
12887
13067
|
const token = header.slice(7).trim();
|
|
12888
13068
|
return token.length > 0 ? token : null;
|
|
12889
13069
|
}
|
|
12890
|
-
const make$
|
|
13070
|
+
const make$68 = Effect.gen(function* () {
|
|
12891
13071
|
const policy = yield* EnvironmentAuthPolicy;
|
|
12892
13072
|
const bootstrapCredentials = yield* PairingGrantStore;
|
|
12893
13073
|
const sessions = yield* SessionStore;
|
|
@@ -13082,9 +13262,9 @@ const make$67 = Effect.gen(function* () {
|
|
|
13082
13262
|
issueStartupPairingUrl
|
|
13083
13263
|
});
|
|
13084
13264
|
});
|
|
13085
|
-
const layer$
|
|
13086
|
-
const storageLayer = Layer.mergeAll(layer$
|
|
13087
|
-
const runtimeLayer = layer$
|
|
13265
|
+
const layer$64 = Layer.effect(EnvironmentAuth, make$68).pipe(Layer.provideMerge(layer$65), Layer.provideMerge(layer$67), Layer.provideMerge(layer$70));
|
|
13266
|
+
const storageLayer = Layer.mergeAll(layer$68, layerConfig);
|
|
13267
|
+
const runtimeLayer = layer$64.pipe(Layer.provideMerge(storageLayer));
|
|
13088
13268
|
//#endregion
|
|
13089
13269
|
//#region src/cliAuthFormat.ts
|
|
13090
13270
|
const newline = "\n";
|
|
@@ -13839,7 +14019,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
|
|
|
13839
14019
|
}
|
|
13840
14020
|
return yield* Effect.gen(function* () {
|
|
13841
14021
|
return yield* run(yield* EnvironmentAuth);
|
|
13842
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$
|
|
14022
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
13843
14023
|
});
|
|
13844
14024
|
const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
|
|
13845
14025
|
const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
|
|
@@ -13979,7 +14159,7 @@ const DEFAULT_LIMITS = {
|
|
|
13979
14159
|
windowMillis: FAILURE_WINDOW_MS,
|
|
13980
14160
|
blockMillis: BLOCK_DURATION_MS
|
|
13981
14161
|
};
|
|
13982
|
-
const make$
|
|
14162
|
+
const make$67 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
|
|
13983
14163
|
const state = yield* Ref.make(initialThrottleState);
|
|
13984
14164
|
return HubAuthThrottle.of({
|
|
13985
14165
|
shouldRefuse: Effect.gen(function* () {
|
|
@@ -13993,7 +14173,7 @@ const make$66 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
|
|
|
13993
14173
|
})
|
|
13994
14174
|
});
|
|
13995
14175
|
});
|
|
13996
|
-
const layer$
|
|
14176
|
+
const layer$63 = Layer.effect(HubAuthThrottle, make$67());
|
|
13997
14177
|
//#endregion
|
|
13998
14178
|
//#region src/hub/HubAuth.ts
|
|
13999
14179
|
/**
|
|
@@ -14307,7 +14487,10 @@ const DeleteTaskInput = Schema$1.Struct({ taskId: TaskId });
|
|
|
14307
14487
|
var TaskRepository = class extends Context.Service()("@p4code/cli/persistence/Services/Tasks/TaskRepository") {};
|
|
14308
14488
|
//#endregion
|
|
14309
14489
|
//#region src/persistence/Layers/Tasks.ts
|
|
14310
|
-
const TaskDbRow = Task.mapFields(Struct.assign({
|
|
14490
|
+
const TaskDbRow = Task.mapFields(Struct.assign({
|
|
14491
|
+
labels: Schema$1.fromJsonString(Schema$1.Array(TaskLabel)),
|
|
14492
|
+
externalRef: Schema$1.NullOr(Schema$1.fromJsonString(TaskExternalRef))
|
|
14493
|
+
}));
|
|
14311
14494
|
/**
|
|
14312
14495
|
* The `list` filter with every field normalized to a bound `null` rather than
|
|
14313
14496
|
* an absent key. Each predicate is then `(:param IS NULL OR column = :param)`,
|
|
@@ -14327,6 +14510,7 @@ const TASK_COLUMNS = `
|
|
|
14327
14510
|
project_id AS "projectId",
|
|
14328
14511
|
repository_key AS "repositoryKey",
|
|
14329
14512
|
thread_id AS "threadId",
|
|
14513
|
+
external_ref_json AS "externalRef",
|
|
14330
14514
|
title,
|
|
14331
14515
|
body,
|
|
14332
14516
|
status,
|
|
@@ -14350,6 +14534,7 @@ const makeTaskRepository = Effect.gen(function* () {
|
|
|
14350
14534
|
project_id,
|
|
14351
14535
|
repository_key,
|
|
14352
14536
|
thread_id,
|
|
14537
|
+
external_ref_json,
|
|
14353
14538
|
title,
|
|
14354
14539
|
body,
|
|
14355
14540
|
status,
|
|
@@ -14366,6 +14551,7 @@ const makeTaskRepository = Effect.gen(function* () {
|
|
|
14366
14551
|
${row.projectId},
|
|
14367
14552
|
${row.repositoryKey},
|
|
14368
14553
|
${row.threadId},
|
|
14554
|
+
${row.externalRef === null ? null : JSON.stringify(row.externalRef)},
|
|
14369
14555
|
${row.title},
|
|
14370
14556
|
${row.body},
|
|
14371
14557
|
${row.status},
|
|
@@ -14382,6 +14568,7 @@ const makeTaskRepository = Effect.gen(function* () {
|
|
|
14382
14568
|
project_id = excluded.project_id,
|
|
14383
14569
|
repository_key = excluded.repository_key,
|
|
14384
14570
|
thread_id = excluded.thread_id,
|
|
14571
|
+
external_ref_json = excluded.external_ref_json,
|
|
14385
14572
|
title = excluded.title,
|
|
14386
14573
|
body = excluded.body,
|
|
14387
14574
|
status = excluded.status,
|
|
@@ -14480,6 +14667,7 @@ const makeTaskRepository = Effect.gen(function* () {
|
|
|
14480
14667
|
...input.projectId !== void 0 ? { projectId: input.projectId } : {},
|
|
14481
14668
|
...input.repositoryKey !== void 0 ? { repositoryKey: input.repositoryKey } : {},
|
|
14482
14669
|
...input.threadId !== void 0 ? { threadId: input.threadId } : {},
|
|
14670
|
+
...input.externalRef !== void 0 ? { externalRef: input.externalRef } : {},
|
|
14483
14671
|
...input.title !== void 0 ? { title: input.title } : {},
|
|
14484
14672
|
...input.body !== void 0 ? { body: input.body } : {},
|
|
14485
14673
|
...input.status !== void 0 ? { status: input.status } : {},
|
|
@@ -14651,6 +14839,7 @@ const createTaskRoute = HttpRouter.add("POST", "/tasks", respondToHubFailures(Ef
|
|
|
14651
14839
|
projectId: input.projectId ?? null,
|
|
14652
14840
|
repositoryKey: input.repositoryKey ?? null,
|
|
14653
14841
|
threadId: input.threadId ?? null,
|
|
14842
|
+
externalRef: input.externalRef ?? null,
|
|
14654
14843
|
title: input.title,
|
|
14655
14844
|
body: input.body ?? "",
|
|
14656
14845
|
status: input.status ?? "backlog",
|
|
@@ -14792,7 +14981,7 @@ const deleteAssetRoute = HttpRouter.add("DELETE", "/assets/:kind/:name", respond
|
|
|
14792
14981
|
const outcome = yield* (yield* AgentAssetRepository).deleteByName(requested);
|
|
14793
14982
|
return outcome._tag === "conflict" ? assetConflict(Option.getOrNull(outcome.current)) : HttpServerResponse.empty({ status: 204 });
|
|
14794
14983
|
})));
|
|
14795
|
-
const layer$
|
|
14984
|
+
const layer$62 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
|
|
14796
14985
|
//#endregion
|
|
14797
14986
|
//#region src/hub/Migrations/001_Tasks.ts
|
|
14798
14987
|
/**
|
|
@@ -14954,6 +15143,22 @@ var _005_TaskOrderKeys_default = Effect.gen(function* () {
|
|
|
14954
15143
|
yield* (yield* SqlClient.SqlClient)`ALTER TABLE tasks ADD COLUMN order_key TEXT`;
|
|
14955
15144
|
});
|
|
14956
15145
|
//#endregion
|
|
15146
|
+
//#region src/hub/Migrations/006_TaskExternalRef.ts
|
|
15147
|
+
/**
|
|
15148
|
+
* The tracker issue a task mirrors, mirroring the environment server's
|
|
15149
|
+
* migration 040. The hub carries the column so a task filed against a Linear
|
|
15150
|
+
* ticket on one machine still reads as that ticket on another.
|
|
15151
|
+
*/
|
|
15152
|
+
var _006_TaskExternalRef_default = Effect.gen(function* () {
|
|
15153
|
+
const sql = yield* SqlClient.SqlClient;
|
|
15154
|
+
yield* sql`ALTER TABLE tasks ADD COLUMN external_ref_json TEXT`;
|
|
15155
|
+
yield* sql`
|
|
15156
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_external_ref_identifier
|
|
15157
|
+
ON tasks(json_extract(external_ref_json, '$.identifier'))
|
|
15158
|
+
WHERE external_ref_json IS NOT NULL
|
|
15159
|
+
`;
|
|
15160
|
+
});
|
|
15161
|
+
//#endregion
|
|
14957
15162
|
//#region src/hub/Migrations.ts
|
|
14958
15163
|
/**
|
|
14959
15164
|
* Hub migrations.
|
|
@@ -14989,6 +15194,11 @@ const hubMigrationEntries = [
|
|
|
14989
15194
|
5,
|
|
14990
15195
|
"TaskOrderKeys",
|
|
14991
15196
|
_005_TaskOrderKeys_default
|
|
15197
|
+
],
|
|
15198
|
+
[
|
|
15199
|
+
6,
|
|
15200
|
+
"TaskExternalRef",
|
|
15201
|
+
_006_TaskExternalRef_default
|
|
14992
15202
|
]
|
|
14993
15203
|
];
|
|
14994
15204
|
const hubMigrationLoader = Migrator.fromRecord(Object.fromEntries(hubMigrationEntries.map(([id, name, migration]) => [`${id}_${name}`, migration])));
|
|
@@ -15071,7 +15281,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
|
|
|
15071
15281
|
});
|
|
15072
15282
|
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 });
|
|
15073
15283
|
}));
|
|
15074
|
-
const makeHubLayer = HttpRouter.serve(layer$
|
|
15284
|
+
const makeHubLayer = HttpRouter.serve(layer$62).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$63), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
15075
15285
|
const runHub = Layer.launch(makeHubLayer);
|
|
15076
15286
|
//#endregion
|
|
15077
15287
|
//#region src/atomicWrite.ts
|
|
@@ -15195,7 +15405,7 @@ function stripDefaultServerSettings(current, defaults) {
|
|
|
15195
15405
|
}
|
|
15196
15406
|
return Object.is(current, defaults) ? void 0 : current;
|
|
15197
15407
|
}
|
|
15198
|
-
const make$
|
|
15408
|
+
const make$66 = Effect.gen(function* () {
|
|
15199
15409
|
const { settingsPath } = yield* ServerConfig$1;
|
|
15200
15410
|
const fs = yield* FileSystem.FileSystem;
|
|
15201
15411
|
const pathService = yield* Path.Path;
|
|
@@ -15416,7 +15626,7 @@ const make$65 = Effect.gen(function* () {
|
|
|
15416
15626
|
}
|
|
15417
15627
|
};
|
|
15418
15628
|
});
|
|
15419
|
-
const layer$
|
|
15629
|
+
const layer$61 = Layer.effect(ServerSettingsService, make$66);
|
|
15420
15630
|
//#endregion
|
|
15421
15631
|
//#region src/pathExpansion.ts
|
|
15422
15632
|
/**
|
|
@@ -15532,7 +15742,15 @@ function buildSkillOverrides(disabled, discovered) {
|
|
|
15532
15742
|
* @module provider/Drivers/ClaudeSkills
|
|
15533
15743
|
*/
|
|
15534
15744
|
const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
15535
|
-
|
|
15745
|
+
/**
|
|
15746
|
+
* The `name`/`description` header a skill and a subagent definition both carry.
|
|
15747
|
+
*
|
|
15748
|
+
* Exported because agent definitions are the same header in a different file,
|
|
15749
|
+
* and the sync validates them before publishing one. A second copy of this
|
|
15750
|
+
* would be a second answer to "does this load in the provider", which is the
|
|
15751
|
+
* one question both callers are asking.
|
|
15752
|
+
*/
|
|
15753
|
+
function parseMarkdownFrontmatter(contents) {
|
|
15536
15754
|
const match = FRONTMATTER_PATTERN.exec(contents);
|
|
15537
15755
|
if (!match) return { kind: "missing" };
|
|
15538
15756
|
let parsed;
|
|
@@ -15583,6 +15801,19 @@ const resolveClaudeUserSkillsDir = Effect.fn("resolveClaudeUserSkillsDir")(funct
|
|
|
15583
15801
|
return path.join(configDirPath, "skills");
|
|
15584
15802
|
});
|
|
15585
15803
|
/**
|
|
15804
|
+
* The user-scope subagent directory the CLI would read.
|
|
15805
|
+
*
|
|
15806
|
+
* Same resolution as the skills directory beside it, for the same reason: the
|
|
15807
|
+
* sync materializes into exactly the directory the provider loads from, and
|
|
15808
|
+
* deriving it a second time is how the two come to disagree about where an
|
|
15809
|
+
* agent definition lives.
|
|
15810
|
+
*/
|
|
15811
|
+
const resolveClaudeUserAgentsDir = Effect.fn("resolveClaudeUserAgentsDir")(function* (config, environment, cwd) {
|
|
15812
|
+
const path = yield* Path.Path;
|
|
15813
|
+
const configDirPath = yield* resolveClaudeConfigDirPath(config, environment ?? process.env, cwd);
|
|
15814
|
+
return path.join(configDirPath, "agents");
|
|
15815
|
+
});
|
|
15816
|
+
/**
|
|
15586
15817
|
* Enumerate Claude Code skills from the user config dir and the workspace.
|
|
15587
15818
|
* Discovery is best-effort: unreadable roots and malformed skill entries are
|
|
15588
15819
|
* skipped so a broken skill never degrades the provider snapshot. On name
|
|
@@ -15607,7 +15838,7 @@ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config
|
|
|
15607
15838
|
const skillPath = path.join(root.directory, entry, "SKILL.md");
|
|
15608
15839
|
const contents = yield* fileSystem.readFileString(skillPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
15609
15840
|
if (contents === void 0) continue;
|
|
15610
|
-
const frontmatter =
|
|
15841
|
+
const frontmatter = parseMarkdownFrontmatter(contents);
|
|
15611
15842
|
if (frontmatter.kind === "malformed") continue;
|
|
15612
15843
|
const name = (frontmatter.kind === "parsed" ? frontmatter.name : void 0) ?? entry.trim();
|
|
15613
15844
|
if (!name) continue;
|
|
@@ -15737,7 +15968,7 @@ function claudeEntryFromRegistration(registration) {
|
|
|
15737
15968
|
};
|
|
15738
15969
|
}
|
|
15739
15970
|
var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
|
|
15740
|
-
const make$
|
|
15971
|
+
const make$65 = Effect.gen(function* () {
|
|
15741
15972
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
15742
15973
|
const path = yield* Path.Path;
|
|
15743
15974
|
const services = yield* Effect.context();
|
|
@@ -15802,7 +16033,7 @@ const make$64 = Effect.gen(function* () {
|
|
|
15802
16033
|
removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
|
|
15803
16034
|
};
|
|
15804
16035
|
});
|
|
15805
|
-
const layer$
|
|
16036
|
+
const layer$60 = Layer.effect(ClaudeMcpFiles, make$65);
|
|
15806
16037
|
Layer.succeed(ClaudeMcpFiles, {
|
|
15807
16038
|
readUser: Effect.succeed([]),
|
|
15808
16039
|
upsertUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
|
|
@@ -15938,7 +16169,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
|
|
|
15938
16169
|
const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
|
|
15939
16170
|
var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
|
|
15940
16171
|
const registryError = (detail) => new McpRegistryError({ detail });
|
|
15941
|
-
const make$
|
|
16172
|
+
const make$64 = Effect.gen(function* () {
|
|
15942
16173
|
const config = yield* ServerConfig$1;
|
|
15943
16174
|
const secrets = yield* ServerSecretStore;
|
|
15944
16175
|
const http = yield* HttpClient.HttpClient;
|
|
@@ -16258,7 +16489,7 @@ const make$63 = Effect.gen(function* () {
|
|
|
16258
16489
|
accessTokenFor
|
|
16259
16490
|
};
|
|
16260
16491
|
});
|
|
16261
|
-
const layer$
|
|
16492
|
+
const layer$59 = Layer.effect(McpOAuth, make$64);
|
|
16262
16493
|
Layer.succeed(McpOAuth, {
|
|
16263
16494
|
statusFor: () => Effect.succeed(Option.none()),
|
|
16264
16495
|
begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
|
|
@@ -16276,7 +16507,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
|
|
|
16276
16507
|
const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
|
|
16277
16508
|
var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
|
|
16278
16509
|
const slotsOf = (registration) => registration.secrets ?? [];
|
|
16279
|
-
const make$
|
|
16510
|
+
const make$63 = Effect.gen(function* () {
|
|
16280
16511
|
const config = yield* ServerConfig$1;
|
|
16281
16512
|
const secrets = yield* ServerSecretStore;
|
|
16282
16513
|
const oauth = yield* McpOAuth;
|
|
@@ -16427,7 +16658,289 @@ const make$62 = Effect.gen(function* () {
|
|
|
16427
16658
|
}).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
|
|
16428
16659
|
};
|
|
16429
16660
|
});
|
|
16430
|
-
const layer$
|
|
16661
|
+
const layer$58 = Layer.effect(McpRegistry, make$63);
|
|
16662
|
+
//#endregion
|
|
16663
|
+
//#region src/sync/skillDirectory.ts
|
|
16664
|
+
/**
|
|
16665
|
+
* Reading and writing skill directories on disk.
|
|
16666
|
+
*
|
|
16667
|
+
* Kept apart from the sync itself so the filesystem shape has its own tests,
|
|
16668
|
+
* and so the rules about what is refusable — binaries, oversized files, deep
|
|
16669
|
+
* trees — live in one place rather than being rediscovered at each call site.
|
|
16670
|
+
*
|
|
16671
|
+
* @module sync/skillDirectory
|
|
16672
|
+
*/
|
|
16673
|
+
/** Bounded so a stray symlink or a vendored `node_modules` cannot walk forever. */
|
|
16674
|
+
const MAX_SKILL_DEPTH = 4;
|
|
16675
|
+
/**
|
|
16676
|
+
* Entries that are never part of a skill, skipped rather than refused.
|
|
16677
|
+
*
|
|
16678
|
+
* The distinction matters. Refusing an asset because it cannot be represented
|
|
16679
|
+
* faithfully is right — a half-materialized skill loads and misbehaves. But a
|
|
16680
|
+
* `.DS_Store` is not part of the skill at all, and macOS drops one into any
|
|
16681
|
+
* directory that has been opened in Finder, so refusing on its account rejects
|
|
16682
|
+
* a perfectly good skill for a reason its author cannot see.
|
|
16683
|
+
*
|
|
16684
|
+
* These could not sync in any case: `AgentAssetFilePath` rejects a leading dot,
|
|
16685
|
+
* so a dotfile has no representable path even if it were text.
|
|
16686
|
+
*/
|
|
16687
|
+
const IGNORED_ENTRIES = /* @__PURE__ */ new Set([
|
|
16688
|
+
".DS_Store",
|
|
16689
|
+
"Thumbs.db",
|
|
16690
|
+
".git",
|
|
16691
|
+
".svn",
|
|
16692
|
+
".hg",
|
|
16693
|
+
"node_modules",
|
|
16694
|
+
"__pycache__",
|
|
16695
|
+
".venv",
|
|
16696
|
+
".pytest_cache",
|
|
16697
|
+
".ruff_cache",
|
|
16698
|
+
".mypy_cache"
|
|
16699
|
+
]);
|
|
16700
|
+
/**
|
|
16701
|
+
* Anything else with a leading dot is skipped too — an editor's `.idea`, a
|
|
16702
|
+
* `.gitignore` that belongs to the checkout rather than the skill. The rule is
|
|
16703
|
+
* the same one the path schema already enforces, applied earlier so it reads as
|
|
16704
|
+
* "not part of the skill" instead of "this skill is broken".
|
|
16705
|
+
*/
|
|
16706
|
+
const isIgnoredEntry = (entry) => IGNORED_ENTRIES.has(entry) || entry.startsWith(".");
|
|
16707
|
+
/**
|
|
16708
|
+
* A file is binary if decoding it yields a NUL.
|
|
16709
|
+
*
|
|
16710
|
+
* Cheap, and right for what is being asked: the question is not "is this valid
|
|
16711
|
+
* UTF-8" but "can this round-trip through a JSON text column", and a NUL is
|
|
16712
|
+
* what actually breaks that on the way back out of SQLite.
|
|
16713
|
+
*
|
|
16714
|
+
* Exported so the single-file kinds ask it the same way this one does, rather
|
|
16715
|
+
* than each keeping a copy that could drift into a different answer.
|
|
16716
|
+
*/
|
|
16717
|
+
const looksBinary$1 = (content) => content.includes("\0");
|
|
16718
|
+
/**
|
|
16719
|
+
* Read one directory as an asset's files, or say why it cannot be one.
|
|
16720
|
+
*
|
|
16721
|
+
* Exported because memory's `global-memory/` is the same shape by a different
|
|
16722
|
+
* name: a directory whose whole contents are the asset. Sharing this keeps the
|
|
16723
|
+
* refusal rules — depth, file count, binaries, size — identical for both,
|
|
16724
|
+
* rather than letting a second copy drift into accepting what the first
|
|
16725
|
+
* rejects.
|
|
16726
|
+
*/
|
|
16727
|
+
const readSkillFiles = Effect.fn("skillDirectory.readSkillFiles")(function* (root) {
|
|
16728
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16729
|
+
const path = yield* Path.Path;
|
|
16730
|
+
const files = [];
|
|
16731
|
+
const walk = Effect.fn("skillDirectory.walk")(function* (directory, prefix, depth) {
|
|
16732
|
+
if (depth > MAX_SKILL_DEPTH) return "too-deep";
|
|
16733
|
+
const entries = yield* fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
16734
|
+
for (const entry of [...entries].sort()) {
|
|
16735
|
+
if (isIgnoredEntry(entry)) continue;
|
|
16736
|
+
const absolute = path.join(directory, entry);
|
|
16737
|
+
const relative = prefix === "" ? entry : `${prefix}/${entry}`;
|
|
16738
|
+
const info = yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16739
|
+
if (info === void 0) return "unreadable";
|
|
16740
|
+
if (info.type === "Directory") {
|
|
16741
|
+
const nested = yield* walk(absolute, relative, depth + 1);
|
|
16742
|
+
if (nested !== null) return nested;
|
|
16743
|
+
continue;
|
|
16744
|
+
}
|
|
16745
|
+
if (info.type !== "File") continue;
|
|
16746
|
+
if (files.length >= 512) return "too-many-files";
|
|
16747
|
+
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16748
|
+
if (content === void 0) return "unreadable";
|
|
16749
|
+
if (looksBinary$1(content)) return "binary-file";
|
|
16750
|
+
if (content.length > 524288) return "file-too-large";
|
|
16751
|
+
files.push({
|
|
16752
|
+
path: relative,
|
|
16753
|
+
content
|
|
16754
|
+
});
|
|
16755
|
+
}
|
|
16756
|
+
return null;
|
|
16757
|
+
});
|
|
16758
|
+
const refusal = yield* walk(root, "", 1);
|
|
16759
|
+
if (refusal !== null) return { refusal };
|
|
16760
|
+
if (agentAssetTotalBytes(files) > 4194304) return { refusal: "asset-too-large" };
|
|
16761
|
+
return { files };
|
|
16762
|
+
});
|
|
16763
|
+
/**
|
|
16764
|
+
* Read every skill directory under a user-scope `skills/` root.
|
|
16765
|
+
*
|
|
16766
|
+
* A directory with no `SKILL.md` is not a skill and is skipped silently —
|
|
16767
|
+
* Claude Code would ignore it too, so reporting it would be noise about
|
|
16768
|
+
* something that was never a skill in the first place.
|
|
16769
|
+
*/
|
|
16770
|
+
const readSkillDirectory = Effect.fn("skillDirectory.readSkillDirectory")(function* (skillsRoot) {
|
|
16771
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16772
|
+
const path = yield* Path.Path;
|
|
16773
|
+
const entries = yield* fileSystem.readDirectory(skillsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16774
|
+
const skills = [];
|
|
16775
|
+
const refused = [];
|
|
16776
|
+
for (const entry of [...entries].sort()) {
|
|
16777
|
+
if (isIgnoredEntry(entry)) continue;
|
|
16778
|
+
const directory = path.join(skillsRoot, entry);
|
|
16779
|
+
if ((yield* fileSystem.stat(directory).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "Directory") continue;
|
|
16780
|
+
if (!(yield* fileSystem.exists(path.join(directory, "SKILL.md")).pipe(Effect.orElseSucceed(() => false)))) continue;
|
|
16781
|
+
const read = yield* readSkillFiles(directory);
|
|
16782
|
+
if ("refusal" in read) {
|
|
16783
|
+
refused.push({
|
|
16784
|
+
name: entry,
|
|
16785
|
+
reason: read.refusal
|
|
16786
|
+
});
|
|
16787
|
+
continue;
|
|
16788
|
+
}
|
|
16789
|
+
skills.push({
|
|
16790
|
+
name: entry,
|
|
16791
|
+
files: read.files
|
|
16792
|
+
});
|
|
16793
|
+
}
|
|
16794
|
+
return {
|
|
16795
|
+
skills,
|
|
16796
|
+
refused
|
|
16797
|
+
};
|
|
16798
|
+
});
|
|
16799
|
+
/**
|
|
16800
|
+
* Replace a skill directory with exactly the asset's files.
|
|
16801
|
+
*
|
|
16802
|
+
* Written to a sibling and then swapped, so a crash mid-write leaves the old
|
|
16803
|
+
* skill intact rather than half of the new one. A skill is loaded as a unit and
|
|
16804
|
+
* a partially written one is instructions with the middle missing, which an
|
|
16805
|
+
* agent will happily follow.
|
|
16806
|
+
*/
|
|
16807
|
+
const writeSkillDirectory = Effect.fn("skillDirectory.writeSkillDirectory")(function* (input) {
|
|
16808
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16809
|
+
const path = yield* Path.Path;
|
|
16810
|
+
const target = path.join(input.skillsRoot, input.name);
|
|
16811
|
+
const staging = path.join(input.skillsRoot, `.${input.name}.p4code-staging`);
|
|
16812
|
+
yield* fileSystem.makeDirectory(input.skillsRoot, { recursive: true });
|
|
16813
|
+
yield* fileSystem.remove(staging, {
|
|
16814
|
+
recursive: true,
|
|
16815
|
+
force: true
|
|
16816
|
+
});
|
|
16817
|
+
yield* fileSystem.makeDirectory(staging, { recursive: true });
|
|
16818
|
+
for (const file of input.files) {
|
|
16819
|
+
const destination = path.join(staging, file.path);
|
|
16820
|
+
yield* fileSystem.makeDirectory(path.dirname(destination), { recursive: true });
|
|
16821
|
+
yield* fileSystem.writeFileString(destination, file.content);
|
|
16822
|
+
}
|
|
16823
|
+
yield* fileSystem.remove(target, {
|
|
16824
|
+
recursive: true,
|
|
16825
|
+
force: true
|
|
16826
|
+
});
|
|
16827
|
+
yield* fileSystem.rename(staging, target);
|
|
16828
|
+
});
|
|
16829
|
+
const removeSkillDirectory = Effect.fn("skillDirectory.removeSkillDirectory")(function* (input) {
|
|
16830
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16831
|
+
const path = yield* Path.Path;
|
|
16832
|
+
yield* fileSystem.remove(path.join(input.skillsRoot, input.name), {
|
|
16833
|
+
recursive: true,
|
|
16834
|
+
force: true
|
|
16835
|
+
});
|
|
16836
|
+
});
|
|
16837
|
+
/**
|
|
16838
|
+
* Whether this text is a definition the provider would actually load.
|
|
16839
|
+
*
|
|
16840
|
+
* Both fields, not just the header. `name` is how a subagent is addressed and
|
|
16841
|
+
* `description` is how the orchestrator decides to address it, so a definition
|
|
16842
|
+
* missing either is present on disk and unreachable in practice.
|
|
16843
|
+
*/
|
|
16844
|
+
const isLoadableAgentDefinition = (content) => {
|
|
16845
|
+
const frontmatter = parseMarkdownFrontmatter(content);
|
|
16846
|
+
return frontmatter.kind === "parsed" && frontmatter.name !== void 0 && frontmatter.description !== void 0;
|
|
16847
|
+
};
|
|
16848
|
+
/**
|
|
16849
|
+
* Every definition in the directory, as syncable assets.
|
|
16850
|
+
*
|
|
16851
|
+
* A file that will not load is refused rather than skipped, unlike a stray
|
|
16852
|
+
* `.DS_Store`: a `.md` in this directory is meant to be an agent, so a broken
|
|
16853
|
+
* header is a fault worth reporting instead of noise to ignore.
|
|
16854
|
+
*/
|
|
16855
|
+
const readAgentDefinitions = Effect.fn("agentDefinitionFiles.read")(function* (agentsRoot) {
|
|
16856
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16857
|
+
const path = yield* Path.Path;
|
|
16858
|
+
const entries = yield* fileSystem.readDirectory(agentsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16859
|
+
const skills = [];
|
|
16860
|
+
const refused = [];
|
|
16861
|
+
for (const entry of [...entries].sort()) {
|
|
16862
|
+
if (!entry.endsWith(".md") || entry.startsWith(".")) continue;
|
|
16863
|
+
const absolute = path.join(agentsRoot, entry);
|
|
16864
|
+
if ((yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "File") continue;
|
|
16865
|
+
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16866
|
+
if (content === void 0) {
|
|
16867
|
+
refused.push({
|
|
16868
|
+
name: entry,
|
|
16869
|
+
reason: "unreadable"
|
|
16870
|
+
});
|
|
16871
|
+
continue;
|
|
16872
|
+
}
|
|
16873
|
+
if (looksBinary$1(content)) {
|
|
16874
|
+
refused.push({
|
|
16875
|
+
name: entry,
|
|
16876
|
+
reason: "binary-file"
|
|
16877
|
+
});
|
|
16878
|
+
continue;
|
|
16879
|
+
}
|
|
16880
|
+
if (content.length > 524288) {
|
|
16881
|
+
refused.push({
|
|
16882
|
+
name: entry,
|
|
16883
|
+
reason: "file-too-large"
|
|
16884
|
+
});
|
|
16885
|
+
continue;
|
|
16886
|
+
}
|
|
16887
|
+
if (!isLoadableAgentDefinition(content)) {
|
|
16888
|
+
refused.push({
|
|
16889
|
+
name: entry,
|
|
16890
|
+
reason: "unreadable"
|
|
16891
|
+
});
|
|
16892
|
+
continue;
|
|
16893
|
+
}
|
|
16894
|
+
const files = [{
|
|
16895
|
+
path: entry,
|
|
16896
|
+
content
|
|
16897
|
+
}];
|
|
16898
|
+
if (agentAssetTotalBytes(files) > 4194304) {
|
|
16899
|
+
refused.push({
|
|
16900
|
+
name: entry,
|
|
16901
|
+
reason: "asset-too-large"
|
|
16902
|
+
});
|
|
16903
|
+
continue;
|
|
16904
|
+
}
|
|
16905
|
+
skills.push({
|
|
16906
|
+
name: entry,
|
|
16907
|
+
files
|
|
16908
|
+
});
|
|
16909
|
+
}
|
|
16910
|
+
return {
|
|
16911
|
+
skills,
|
|
16912
|
+
refused
|
|
16913
|
+
};
|
|
16914
|
+
});
|
|
16915
|
+
/**
|
|
16916
|
+
* Write a definition that arrived from the hub.
|
|
16917
|
+
*
|
|
16918
|
+
* Refused unless it is exactly one markdown file whose path is the asset's own
|
|
16919
|
+
* name and whose frontmatter would load. The last check is the one that matters
|
|
16920
|
+
* across machines: it is what stops a hub row edited by hand from installing a
|
|
16921
|
+
* file the provider quietly ignores on every machine that syncs it.
|
|
16922
|
+
*
|
|
16923
|
+
* Written atomically because a definition is read whole when a subagent starts,
|
|
16924
|
+
* and half of one is instructions with the contract missing.
|
|
16925
|
+
*/
|
|
16926
|
+
const writeAgentDefinition = Effect.fn("agentDefinitionFiles.write")(function* (input) {
|
|
16927
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16928
|
+
const path = yield* Path.Path;
|
|
16929
|
+
const file = input.files[0];
|
|
16930
|
+
if (!input.name.endsWith(".md") || input.files.length !== 1 || file === void 0 || file.path !== input.name) return "refused";
|
|
16931
|
+
if (!isLoadableAgentDefinition(file.content)) return "refused";
|
|
16932
|
+
yield* fileSystem.makeDirectory(input.agentsRoot, { recursive: true }).pipe(Effect.ignore);
|
|
16933
|
+
return yield* writeFileStringAtomically({
|
|
16934
|
+
filePath: path.join(input.agentsRoot, input.name),
|
|
16935
|
+
contents: file.content
|
|
16936
|
+
}).pipe(Effect.as("written"), Effect.orElseSucceed(() => "failed"));
|
|
16937
|
+
});
|
|
16938
|
+
const removeAgentDefinition = Effect.fn("agentDefinitionFiles.remove")(function* (input) {
|
|
16939
|
+
if (!input.name.endsWith(".md")) return;
|
|
16940
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16941
|
+
const path = yield* Path.Path;
|
|
16942
|
+
yield* fileSystem.remove(path.join(input.agentsRoot, input.name), { force: true });
|
|
16943
|
+
});
|
|
16431
16944
|
//#endregion
|
|
16432
16945
|
//#region src/sync/HubLink.ts
|
|
16433
16946
|
/**
|
|
@@ -16524,7 +17037,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
|
|
|
16524
17037
|
shareMode: input.shareMode
|
|
16525
17038
|
});
|
|
16526
17039
|
const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
|
|
16527
|
-
const make$
|
|
17040
|
+
const make$62 = Effect.fn("HubLink.make")(function* (environment) {
|
|
16528
17041
|
const secrets = yield* ServerSecretStore;
|
|
16529
17042
|
const env = environment ?? process.env;
|
|
16530
17043
|
const fromEnv = fromEnvironment(env);
|
|
@@ -16590,7 +17103,7 @@ const make$61 = Effect.fn("HubLink.make")(function* (environment) {
|
|
|
16590
17103
|
})
|
|
16591
17104
|
};
|
|
16592
17105
|
});
|
|
16593
|
-
const layer$
|
|
17106
|
+
const layer$57 = Layer.effect(HubLink, make$62());
|
|
16594
17107
|
//#endregion
|
|
16595
17108
|
//#region src/sync/HubAssetClient.ts
|
|
16596
17109
|
/**
|
|
@@ -16623,7 +17136,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
|
|
|
16623
17136
|
const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
|
|
16624
17137
|
const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
|
|
16625
17138
|
var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
|
|
16626
|
-
const make$
|
|
17139
|
+
const make$61 = Effect.gen(function* () {
|
|
16627
17140
|
const http = yield* HttpClient.HttpClient;
|
|
16628
17141
|
const link = yield* HubLink;
|
|
16629
17142
|
const requireSettings = Effect.gen(function* () {
|
|
@@ -16705,7 +17218,7 @@ const make$60 = Effect.gen(function* () {
|
|
|
16705
17218
|
remove
|
|
16706
17219
|
};
|
|
16707
17220
|
});
|
|
16708
|
-
const layer$
|
|
17221
|
+
const layer$56 = Layer.effect(HubAssetClient, make$61);
|
|
16709
17222
|
//#endregion
|
|
16710
17223
|
//#region src/sync/mcpRegistrationFiles.ts
|
|
16711
17224
|
/**
|
|
@@ -16802,178 +17315,6 @@ const removeMcpRegistration = Effect.fn("mcpRegistrationFiles.remove")(function*
|
|
|
16802
17315
|
yield* fileSystem.remove(path.join(input.registrationsRoot, input.name), { force: true });
|
|
16803
17316
|
});
|
|
16804
17317
|
//#endregion
|
|
16805
|
-
//#region src/sync/skillDirectory.ts
|
|
16806
|
-
/**
|
|
16807
|
-
* Reading and writing skill directories on disk.
|
|
16808
|
-
*
|
|
16809
|
-
* Kept apart from the sync itself so the filesystem shape has its own tests,
|
|
16810
|
-
* and so the rules about what is refusable — binaries, oversized files, deep
|
|
16811
|
-
* trees — live in one place rather than being rediscovered at each call site.
|
|
16812
|
-
*
|
|
16813
|
-
* @module sync/skillDirectory
|
|
16814
|
-
*/
|
|
16815
|
-
/** Bounded so a stray symlink or a vendored `node_modules` cannot walk forever. */
|
|
16816
|
-
const MAX_SKILL_DEPTH = 4;
|
|
16817
|
-
/**
|
|
16818
|
-
* Entries that are never part of a skill, skipped rather than refused.
|
|
16819
|
-
*
|
|
16820
|
-
* The distinction matters. Refusing an asset because it cannot be represented
|
|
16821
|
-
* faithfully is right — a half-materialized skill loads and misbehaves. But a
|
|
16822
|
-
* `.DS_Store` is not part of the skill at all, and macOS drops one into any
|
|
16823
|
-
* directory that has been opened in Finder, so refusing on its account rejects
|
|
16824
|
-
* a perfectly good skill for a reason its author cannot see.
|
|
16825
|
-
*
|
|
16826
|
-
* These could not sync in any case: `AgentAssetFilePath` rejects a leading dot,
|
|
16827
|
-
* so a dotfile has no representable path even if it were text.
|
|
16828
|
-
*/
|
|
16829
|
-
const IGNORED_ENTRIES = /* @__PURE__ */ new Set([
|
|
16830
|
-
".DS_Store",
|
|
16831
|
-
"Thumbs.db",
|
|
16832
|
-
".git",
|
|
16833
|
-
".svn",
|
|
16834
|
-
".hg",
|
|
16835
|
-
"node_modules",
|
|
16836
|
-
"__pycache__",
|
|
16837
|
-
".venv",
|
|
16838
|
-
".pytest_cache",
|
|
16839
|
-
".ruff_cache",
|
|
16840
|
-
".mypy_cache"
|
|
16841
|
-
]);
|
|
16842
|
-
/**
|
|
16843
|
-
* Anything else with a leading dot is skipped too — an editor's `.idea`, a
|
|
16844
|
-
* `.gitignore` that belongs to the checkout rather than the skill. The rule is
|
|
16845
|
-
* the same one the path schema already enforces, applied earlier so it reads as
|
|
16846
|
-
* "not part of the skill" instead of "this skill is broken".
|
|
16847
|
-
*/
|
|
16848
|
-
const isIgnoredEntry = (entry) => IGNORED_ENTRIES.has(entry) || entry.startsWith(".");
|
|
16849
|
-
/**
|
|
16850
|
-
* A file is binary if decoding it yields a NUL.
|
|
16851
|
-
*
|
|
16852
|
-
* Cheap, and right for what is being asked: the question is not "is this valid
|
|
16853
|
-
* UTF-8" but "can this round-trip through a JSON text column", and a NUL is
|
|
16854
|
-
* what actually breaks that on the way back out of SQLite.
|
|
16855
|
-
*/
|
|
16856
|
-
const looksBinary$1 = (content) => content.includes("\0");
|
|
16857
|
-
/**
|
|
16858
|
-
* Read one directory as an asset's files, or say why it cannot be one.
|
|
16859
|
-
*
|
|
16860
|
-
* Exported because memory's `global-memory/` is the same shape by a different
|
|
16861
|
-
* name: a directory whose whole contents are the asset. Sharing this keeps the
|
|
16862
|
-
* refusal rules — depth, file count, binaries, size — identical for both,
|
|
16863
|
-
* rather than letting a second copy drift into accepting what the first
|
|
16864
|
-
* rejects.
|
|
16865
|
-
*/
|
|
16866
|
-
const readSkillFiles = Effect.fn("skillDirectory.readSkillFiles")(function* (root) {
|
|
16867
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16868
|
-
const path = yield* Path.Path;
|
|
16869
|
-
const files = [];
|
|
16870
|
-
const walk = Effect.fn("skillDirectory.walk")(function* (directory, prefix, depth) {
|
|
16871
|
-
if (depth > MAX_SKILL_DEPTH) return "too-deep";
|
|
16872
|
-
const entries = yield* fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
16873
|
-
for (const entry of [...entries].sort()) {
|
|
16874
|
-
if (isIgnoredEntry(entry)) continue;
|
|
16875
|
-
const absolute = path.join(directory, entry);
|
|
16876
|
-
const relative = prefix === "" ? entry : `${prefix}/${entry}`;
|
|
16877
|
-
const info = yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16878
|
-
if (info === void 0) return "unreadable";
|
|
16879
|
-
if (info.type === "Directory") {
|
|
16880
|
-
const nested = yield* walk(absolute, relative, depth + 1);
|
|
16881
|
-
if (nested !== null) return nested;
|
|
16882
|
-
continue;
|
|
16883
|
-
}
|
|
16884
|
-
if (info.type !== "File") continue;
|
|
16885
|
-
if (files.length >= 512) return "too-many-files";
|
|
16886
|
-
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16887
|
-
if (content === void 0) return "unreadable";
|
|
16888
|
-
if (looksBinary$1(content)) return "binary-file";
|
|
16889
|
-
if (content.length > 524288) return "file-too-large";
|
|
16890
|
-
files.push({
|
|
16891
|
-
path: relative,
|
|
16892
|
-
content
|
|
16893
|
-
});
|
|
16894
|
-
}
|
|
16895
|
-
return null;
|
|
16896
|
-
});
|
|
16897
|
-
const refusal = yield* walk(root, "", 1);
|
|
16898
|
-
if (refusal !== null) return { refusal };
|
|
16899
|
-
if (agentAssetTotalBytes(files) > 4194304) return { refusal: "asset-too-large" };
|
|
16900
|
-
return { files };
|
|
16901
|
-
});
|
|
16902
|
-
/**
|
|
16903
|
-
* Read every skill directory under a user-scope `skills/` root.
|
|
16904
|
-
*
|
|
16905
|
-
* A directory with no `SKILL.md` is not a skill and is skipped silently —
|
|
16906
|
-
* Claude Code would ignore it too, so reporting it would be noise about
|
|
16907
|
-
* something that was never a skill in the first place.
|
|
16908
|
-
*/
|
|
16909
|
-
const readSkillDirectory = Effect.fn("skillDirectory.readSkillDirectory")(function* (skillsRoot) {
|
|
16910
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16911
|
-
const path = yield* Path.Path;
|
|
16912
|
-
const entries = yield* fileSystem.readDirectory(skillsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16913
|
-
const skills = [];
|
|
16914
|
-
const refused = [];
|
|
16915
|
-
for (const entry of [...entries].sort()) {
|
|
16916
|
-
if (isIgnoredEntry(entry)) continue;
|
|
16917
|
-
const directory = path.join(skillsRoot, entry);
|
|
16918
|
-
if ((yield* fileSystem.stat(directory).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "Directory") continue;
|
|
16919
|
-
if (!(yield* fileSystem.exists(path.join(directory, "SKILL.md")).pipe(Effect.orElseSucceed(() => false)))) continue;
|
|
16920
|
-
const read = yield* readSkillFiles(directory);
|
|
16921
|
-
if ("refusal" in read) {
|
|
16922
|
-
refused.push({
|
|
16923
|
-
name: entry,
|
|
16924
|
-
reason: read.refusal
|
|
16925
|
-
});
|
|
16926
|
-
continue;
|
|
16927
|
-
}
|
|
16928
|
-
skills.push({
|
|
16929
|
-
name: entry,
|
|
16930
|
-
files: read.files
|
|
16931
|
-
});
|
|
16932
|
-
}
|
|
16933
|
-
return {
|
|
16934
|
-
skills,
|
|
16935
|
-
refused
|
|
16936
|
-
};
|
|
16937
|
-
});
|
|
16938
|
-
/**
|
|
16939
|
-
* Replace a skill directory with exactly the asset's files.
|
|
16940
|
-
*
|
|
16941
|
-
* Written to a sibling and then swapped, so a crash mid-write leaves the old
|
|
16942
|
-
* skill intact rather than half of the new one. A skill is loaded as a unit and
|
|
16943
|
-
* a partially written one is instructions with the middle missing, which an
|
|
16944
|
-
* agent will happily follow.
|
|
16945
|
-
*/
|
|
16946
|
-
const writeSkillDirectory = Effect.fn("skillDirectory.writeSkillDirectory")(function* (input) {
|
|
16947
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16948
|
-
const path = yield* Path.Path;
|
|
16949
|
-
const target = path.join(input.skillsRoot, input.name);
|
|
16950
|
-
const staging = path.join(input.skillsRoot, `.${input.name}.p4code-staging`);
|
|
16951
|
-
yield* fileSystem.makeDirectory(input.skillsRoot, { recursive: true });
|
|
16952
|
-
yield* fileSystem.remove(staging, {
|
|
16953
|
-
recursive: true,
|
|
16954
|
-
force: true
|
|
16955
|
-
});
|
|
16956
|
-
yield* fileSystem.makeDirectory(staging, { recursive: true });
|
|
16957
|
-
for (const file of input.files) {
|
|
16958
|
-
const destination = path.join(staging, file.path);
|
|
16959
|
-
yield* fileSystem.makeDirectory(path.dirname(destination), { recursive: true });
|
|
16960
|
-
yield* fileSystem.writeFileString(destination, file.content);
|
|
16961
|
-
}
|
|
16962
|
-
yield* fileSystem.remove(target, {
|
|
16963
|
-
recursive: true,
|
|
16964
|
-
force: true
|
|
16965
|
-
});
|
|
16966
|
-
yield* fileSystem.rename(staging, target);
|
|
16967
|
-
});
|
|
16968
|
-
const removeSkillDirectory = Effect.fn("skillDirectory.removeSkillDirectory")(function* (input) {
|
|
16969
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16970
|
-
const path = yield* Path.Path;
|
|
16971
|
-
yield* fileSystem.remove(path.join(input.skillsRoot, input.name), {
|
|
16972
|
-
recursive: true,
|
|
16973
|
-
force: true
|
|
16974
|
-
});
|
|
16975
|
-
});
|
|
16976
|
-
//#endregion
|
|
16977
17318
|
//#region src/sync/memoryFile.ts
|
|
16978
17319
|
/**
|
|
16979
17320
|
* Reading and writing user-scope memory files.
|
|
@@ -17254,12 +17595,20 @@ const PROCEED = { _tag: "proceed" };
|
|
|
17254
17595
|
* - A skill without `SKILL.md` would be invisible to discovery: the picker
|
|
17255
17596
|
* skips directories without one, so the create would look like it silently
|
|
17256
17597
|
* failed. Refused with the reason instead.
|
|
17598
|
+
* - An agent definition is one markdown file whose name is the asset's own, and
|
|
17599
|
+
* anything else is invisible in the same way — the provider reads `.md` in
|
|
17600
|
+
* that directory and nothing else. The frontmatter check is the writer's,
|
|
17601
|
+
* because it needs a YAML parse this module deliberately does without.
|
|
17257
17602
|
* - An existing local name is never overwritten. Create means create; editing
|
|
17258
17603
|
* is a different operation with a different surface.
|
|
17259
17604
|
*/
|
|
17260
17605
|
function decideCreateLocal(input) {
|
|
17261
17606
|
if (input.asset.kind === "mcp") return refuse("refused", "MCP registrations are created through mcp.save, not as files.");
|
|
17262
17607
|
if (input.asset.kind === "skill" && !input.asset.files.some((file) => file.path === "SKILL.md")) return refuse("refused", "A skill needs a SKILL.md at its root; without one, discovery skips the directory.");
|
|
17608
|
+
if (input.asset.kind === "agent") {
|
|
17609
|
+
const [file, ...rest] = input.asset.files;
|
|
17610
|
+
if (!input.asset.name.endsWith(".md") || rest.length > 0 || file?.path !== input.asset.name) return refuse("refused", "An agent definition is a single markdown file named after the asset, for example 'reviewer.md' holding 'reviewer.md'.");
|
|
17611
|
+
}
|
|
17263
17612
|
if (input.localNames.has(input.asset.name)) return refuse("exists", `An asset named '${input.asset.name}' already exists locally.`);
|
|
17264
17613
|
return PROCEED;
|
|
17265
17614
|
}
|
|
@@ -17444,7 +17793,7 @@ const EMPTY_REPORT = {
|
|
|
17444
17793
|
unavailable: null
|
|
17445
17794
|
};
|
|
17446
17795
|
var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
|
|
17447
|
-
const make$
|
|
17796
|
+
const make$60 = Effect.gen(function* () {
|
|
17448
17797
|
const client = yield* HubAssetClient;
|
|
17449
17798
|
const link = yield* HubLink;
|
|
17450
17799
|
const settingsStore = yield* ServerSettingsService;
|
|
@@ -17502,6 +17851,22 @@ const make$59 = Effect.gen(function* () {
|
|
|
17502
17851
|
name
|
|
17503
17852
|
}).pipe(Effect.ignore)
|
|
17504
17853
|
},
|
|
17854
|
+
{
|
|
17855
|
+
kind: "agent",
|
|
17856
|
+
resolveRoot: Effect.gen(function* () {
|
|
17857
|
+
return yield* resolveClaudeUserAgentsDir(yield* claudeHome());
|
|
17858
|
+
}),
|
|
17859
|
+
read: (root) => readAgentDefinitions(root),
|
|
17860
|
+
write: ({ root, name, files }) => writeAgentDefinition({
|
|
17861
|
+
agentsRoot: root,
|
|
17862
|
+
name,
|
|
17863
|
+
files
|
|
17864
|
+
}),
|
|
17865
|
+
remove: ({ root, name }) => removeAgentDefinition({
|
|
17866
|
+
agentsRoot: root,
|
|
17867
|
+
name
|
|
17868
|
+
}).pipe(Effect.ignore)
|
|
17869
|
+
},
|
|
17505
17870
|
{
|
|
17506
17871
|
kind: "mcp",
|
|
17507
17872
|
resolveRoot: Effect.succeed(path.join(config.stateDir, "mcp")),
|
|
@@ -18141,10 +18506,266 @@ const make$59 = Effect.gen(function* () {
|
|
|
18141
18506
|
removeLocal
|
|
18142
18507
|
};
|
|
18143
18508
|
});
|
|
18144
|
-
const layer$
|
|
18509
|
+
const layer$55 = Layer.effect(AssetSync, make$60);
|
|
18510
|
+
//#endregion
|
|
18511
|
+
//#region src/provider/CompressPrompts.ts
|
|
18512
|
+
/**
|
|
18513
|
+
* Caveman-style response compression rulesets, adapted from the caveman
|
|
18514
|
+
* plugin's SKILL.md. Pre-baked per level so injection is a lookup, not a
|
|
18515
|
+
* runtime template. Applies to assistant prose only: code blocks, commits,
|
|
18516
|
+
* PRs, error strings, and safety-critical text stay uncompressed.
|
|
18517
|
+
*/
|
|
18518
|
+
const COMPRESS_SHARED_RULES = `Respond terse. All technical substance stays. Only fluff dies.
|
|
18519
|
+
|
|
18520
|
+
## Persistence
|
|
18521
|
+
|
|
18522
|
+
Active every response. No revert after many turns. No filler drift. Still active if unsure.
|
|
18523
|
+
|
|
18524
|
+
## Rules
|
|
18525
|
+
|
|
18526
|
+
Drop filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Short synonyms (big not extensive, fix not "implement a solution for"). No decorative tables/emoji, no dumping long raw error logs unless asked - quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations (cfg/impl/req/res/fn) - tokenizers split them same as the full word: zero tokens saved, reader still decodes. No causal arrows (->) in prose. Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
|
18527
|
+
|
|
18528
|
+
Never drop not/never/no/only/except: flipping the meaning costs more than any token saved. Numbers and units exact.
|
|
18529
|
+
|
|
18530
|
+
Tool calls: fire direct. No plan, no progress note between calls, never announce the next call. If the harness asks for a short lead-in before tool calls, write that one line and nothing more. Otherwise text before a call only to clarify, warn about a security or irreversible action, or resolve ambiguity.
|
|
18531
|
+
|
|
18532
|
+
Preserve the user's dominant language: reply in the language the user writes, whatever language the examples or the surrounding context use. Compress the style, not the language. Every emitted line in that language, lead-in lines included, not just the final reply. Always keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless the user explicitly asks for translation.
|
|
18533
|
+
|
|
18534
|
+
Dropping articles applies to article languages only. Where small markers carry case or role (particles, postpositions), keep them: grammar, not filler. Compress politeness and filler instead.
|
|
18535
|
+
|
|
18536
|
+
No self-reference. Never name or announce the style. No compressed answer plus normal recap - compressed output only. Exception: user explicitly asks what the mode is.
|
|
18537
|
+
|
|
18538
|
+
## Auto-Clarity
|
|
18539
|
+
|
|
18540
|
+
Drop compression and write normally when:
|
|
18541
|
+
- Security warnings
|
|
18542
|
+
- Irreversible action confirmations
|
|
18543
|
+
- Multi-step sequences where fragment order or omitted conjunctions risk misread
|
|
18544
|
+
- Compression itself creates technical ambiguity (e.g. "migrate table drop column backup first" - order unclear without articles and conjunctions)
|
|
18545
|
+
- User asks to clarify or repeats a question
|
|
18546
|
+
|
|
18547
|
+
Resume compression after the clear part is done. The example below shows the format only: write the warning in the session's language, not the example's.
|
|
18548
|
+
|
|
18549
|
+
Example - destructive operation:
|
|
18550
|
+
> **Warning:** This will permanently delete all rows in the \`users\` table and cannot be undone.
|
|
18551
|
+
> \`\`\`sql
|
|
18552
|
+
> DROP TABLE users;
|
|
18553
|
+
> \`\`\`
|
|
18554
|
+
> Compression resumes. Verify a backup exists first.
|
|
18555
|
+
|
|
18556
|
+
## Boundaries
|
|
18557
|
+
|
|
18558
|
+
Anything persisted outside the chat: write normal, uncompressed prose. Covers code, code comments, commit messages, PR and issue text, docs, memory files, and messages to third parties.`;
|
|
18559
|
+
const COMPRESS_RULESETS = {
|
|
18560
|
+
lite: `## Response compression: lite
|
|
18561
|
+
|
|
18562
|
+
${COMPRESS_SHARED_RULES}
|
|
18563
|
+
|
|
18564
|
+
## Intensity: lite
|
|
18565
|
+
|
|
18566
|
+
No filler or hedging. Keep articles and full sentences. Professional but tight.
|
|
18567
|
+
|
|
18568
|
+
Example - "Why does my React component re-render?"
|
|
18569
|
+
"Your component re-renders because you create a new object reference each render. Wrap it in \`useMemo\`."
|
|
18570
|
+
|
|
18571
|
+
Example - "Explain database connection pooling."
|
|
18572
|
+
"Connection pooling reuses open connections instead of creating a new one per request. Avoids repeated handshake overhead."`,
|
|
18573
|
+
full: `## Response compression: full
|
|
18574
|
+
|
|
18575
|
+
${COMPRESS_SHARED_RULES}
|
|
18576
|
+
|
|
18577
|
+
## Intensity: full
|
|
18578
|
+
|
|
18579
|
+
Drop articles (a/an/the), fragments OK, short synonyms. Pattern: [thing] [action] [reason]. [next step].
|
|
18580
|
+
|
|
18581
|
+
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
|
18582
|
+
Yes: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"
|
|
18583
|
+
|
|
18584
|
+
Example - "Why does my React component re-render?"
|
|
18585
|
+
"New object ref each render. Inline object prop = new ref = re-render. Wrap in \`useMemo\`."
|
|
18586
|
+
|
|
18587
|
+
Example - "Explain database connection pooling."
|
|
18588
|
+
"Pool reuses open DB connections. No new connection per request. Skips handshake overhead."`,
|
|
18589
|
+
ultra: `## Response compression: ultra
|
|
18590
|
+
|
|
18591
|
+
${COMPRESS_SHARED_RULES}
|
|
18592
|
+
|
|
18593
|
+
## Intensity: ultra
|
|
18594
|
+
|
|
18595
|
+
Drop articles, fragments OK, short synonyms. Also strip conjunctions when cause-then-effect stays unambiguous. One word when one word is enough. State each fact once. Still no invented abbreviations (cfg/impl/req/res/fn/auth) and still no arrows (X -> Y): both measure as zero token saving and cost decode clarity. Code symbols, function names, API names, error strings: never touch.
|
|
18596
|
+
|
|
18597
|
+
Example - "Why does my React component re-render?"
|
|
18598
|
+
"Inline object prop, new ref, re-render. \`useMemo\`."
|
|
18599
|
+
|
|
18600
|
+
Example - "Explain database connection pooling."
|
|
18601
|
+
"Pool reuses open DB connections. No per-request handshake."`
|
|
18602
|
+
};
|
|
18603
|
+
const COMPRESS_TURN_REMINDERS = {
|
|
18604
|
+
lite: "[response style: compressed lite - no filler/hedging, full sentences; keep negations and numbers exact; code/commits/security text normal]",
|
|
18605
|
+
full: "[response style: compressed full - terse fragments, drop articles/filler; keep negations and numbers exact; code/commits/security text normal]",
|
|
18606
|
+
ultra: "[response style: compressed ultra - maximum terseness, one word when enough; keep negations and numbers exact; code/commits/security text normal]"
|
|
18607
|
+
};
|
|
18608
|
+
function compressRulesetFor(mode) {
|
|
18609
|
+
return mode === "off" ? void 0 : COMPRESS_RULESETS[mode];
|
|
18610
|
+
}
|
|
18611
|
+
function compressTurnReminderFor(mode) {
|
|
18612
|
+
return mode === "off" ? void 0 : COMPRESS_TURN_REMINDERS[mode];
|
|
18613
|
+
}
|
|
18614
|
+
//#endregion
|
|
18615
|
+
//#region src/sync/agentPresets.ts
|
|
18616
|
+
/**
|
|
18617
|
+
* The row ceiling both presets impose.
|
|
18618
|
+
*
|
|
18619
|
+
* A cap rather than "be brief": an uncapped list is how a subagent turns a
|
|
18620
|
+
* bounded answer back into a page, and a number is the only form of that
|
|
18621
|
+
* instruction a model cannot negotiate with.
|
|
18622
|
+
*/
|
|
18623
|
+
const PRESET_MAX_RESULT_ROWS = 20;
|
|
18624
|
+
/**
|
|
18625
|
+
* Named `p4-*` on purpose.
|
|
18626
|
+
*
|
|
18627
|
+
* The directory is shared with whatever the user has written by hand, and
|
|
18628
|
+
* installing a preset must never quietly replace their own `reviewer`. The
|
|
18629
|
+
* prefix also makes provenance obvious in the settings list, where every kind
|
|
18630
|
+
* of agent definition appears in one column.
|
|
18631
|
+
*/
|
|
18632
|
+
const PRESET_NAME_PREFIX = "p4-";
|
|
18633
|
+
/**
|
|
18634
|
+
* What every preset says about what it is handed.
|
|
18635
|
+
*
|
|
18636
|
+
* Shared because it is the same contract whatever the subagent does: no
|
|
18637
|
+
* inherited history, exact paths, and a refusal instead of a guess. The last
|
|
18638
|
+
* clause is the one that earns its place — a subagent that invents the missing
|
|
18639
|
+
* half of an under-specified prompt returns something confident and wrong,
|
|
18640
|
+
* which costs more than the turn that asked for it.
|
|
18641
|
+
*/
|
|
18642
|
+
const INPUT_CONTRACT = `## What you are given
|
|
18643
|
+
|
|
18644
|
+
You inherit no session history. The prompt is the whole of what you know: one
|
|
18645
|
+
request, plus whatever exact paths the orchestrator has already established.
|
|
18646
|
+
Treat those paths as authoritative and start from them rather than searching for
|
|
18647
|
+
them again.
|
|
18648
|
+
|
|
18649
|
+
What the prompt does not say, you do not assume. If it names no request, or
|
|
18650
|
+
names a path that is not there, answer with the BLOCKED line below. Guessing at
|
|
18651
|
+
the missing half of a request produces an answer that reads as confident and is
|
|
18652
|
+
not checkable, which is worse than no answer.`;
|
|
18653
|
+
/**
|
|
18654
|
+
* What every preset says about the answer.
|
|
18655
|
+
*
|
|
18656
|
+
* The two sentinels matter as much as the table. Without `NONE` a clean run
|
|
18657
|
+
* comes back as a paragraph explaining that everything looked fine; without
|
|
18658
|
+
* `BLOCKED` an under-specified request comes back as an apology. Both are the
|
|
18659
|
+
* narrative this contract exists to remove, and both are cheap to name.
|
|
18660
|
+
*/
|
|
18661
|
+
const outputContract = (row, extra) => [
|
|
18662
|
+
"## What you return",
|
|
18663
|
+
"",
|
|
18664
|
+
"Your final message is the result. It is the table and nothing else: no",
|
|
18665
|
+
"preamble, no restatement of the request, no summary underneath it.",
|
|
18666
|
+
"",
|
|
18667
|
+
`One row per finding, at most ${PRESET_MAX_RESULT_ROWS}, most important first:`,
|
|
18668
|
+
"",
|
|
18669
|
+
` ${row}`,
|
|
18670
|
+
"",
|
|
18671
|
+
"Nothing to report: the single word NONE.",
|
|
18672
|
+
"Request insufficient: BLOCKED - <what is missing>, on one line.",
|
|
18673
|
+
"",
|
|
18674
|
+
"Paths are repository-relative and carry a line number. A row without one is",
|
|
18675
|
+
"a row you have not verified: open the file and get it.",
|
|
18676
|
+
...extra
|
|
18677
|
+
].join("\n");
|
|
18678
|
+
/**
|
|
18679
|
+
* Assemble one definition.
|
|
18680
|
+
*
|
|
18681
|
+
* The compression ruleset goes last so the contract above it is read first.
|
|
18682
|
+
* Both are instructions to the same model and neither overrides the other:
|
|
18683
|
+
* the contract fixes the shape, the ruleset fixes the prose inside it.
|
|
18684
|
+
*/
|
|
18685
|
+
const buildPreset = (input) => {
|
|
18686
|
+
const name = `${PRESET_NAME_PREFIX}${input.name}`;
|
|
18687
|
+
const content = [
|
|
18688
|
+
"---",
|
|
18689
|
+
`name: ${name}`,
|
|
18690
|
+
`description: ${JSON.stringify(input.description)}`,
|
|
18691
|
+
`tools: ${input.tools}`,
|
|
18692
|
+
"---",
|
|
18693
|
+
"",
|
|
18694
|
+
input.body.trim(),
|
|
18695
|
+
"",
|
|
18696
|
+
compressRulesetFor("full") ?? "",
|
|
18697
|
+
""
|
|
18698
|
+
].join("\n");
|
|
18699
|
+
const fileName = `${name}.md`;
|
|
18700
|
+
return {
|
|
18701
|
+
name: fileName,
|
|
18702
|
+
summary: input.summary,
|
|
18703
|
+
files: [{
|
|
18704
|
+
path: fileName,
|
|
18705
|
+
content
|
|
18706
|
+
}]
|
|
18707
|
+
};
|
|
18708
|
+
};
|
|
18709
|
+
/** Every preset, in the order `p4c agents presets` lists them. */
|
|
18710
|
+
const AGENT_PRESETS = [buildPreset({
|
|
18711
|
+
name: "investigator",
|
|
18712
|
+
description: "Find where something lives in the codebase. Use when the answer is a set of locations rather than a change. Returns a fixed table of path:line rows and nothing else.",
|
|
18713
|
+
tools: "Read, Grep, Glob, Bash",
|
|
18714
|
+
summary: "Locates code and answers with a path:line table.",
|
|
18715
|
+
body: `You locate code. You do not change it, and you say no more about any location
|
|
18716
|
+
than the one clause that explains why it is in the list.
|
|
18717
|
+
|
|
18718
|
+
${INPUT_CONTRACT}
|
|
18719
|
+
|
|
18720
|
+
${outputContract("path:line - symbol - one clause on why it matters", [
|
|
18721
|
+
"",
|
|
18722
|
+
"Symbol is the function, type or constant the line belongs to, not a paraphrase",
|
|
18723
|
+
"of the line. Where there is genuinely no symbol, name the block: a route table,",
|
|
18724
|
+
"a config literal."
|
|
18725
|
+
])}`
|
|
18726
|
+
}), buildPreset({
|
|
18727
|
+
name: "reviewer",
|
|
18728
|
+
description: "Review named files or a diff for defects. Use when the answer is a list of findings rather than a change. Returns a fixed table of path:line findings and nothing else.",
|
|
18729
|
+
tools: "Read, Grep, Glob, Bash",
|
|
18730
|
+
summary: "Reviews named files and answers with a severity-tagged finding table.",
|
|
18731
|
+
body: `You review code you did not write. You do not change it, and you do not report
|
|
18732
|
+
what it does well - the orchestrator asked what is wrong, and a list padded with
|
|
18733
|
+
approval is a list that has to be re-read to find the three rows that matter.
|
|
18734
|
+
|
|
18735
|
+
${INPUT_CONTRACT}
|
|
18736
|
+
|
|
18737
|
+
${outputContract("path:line - severity - what is wrong, and what it breaks", [
|
|
18738
|
+
"",
|
|
18739
|
+
"Severity is one of three words:",
|
|
18740
|
+
"",
|
|
18741
|
+
" bug - wrong behaviour reachable in ordinary use. Name the input that reaches it.",
|
|
18742
|
+
" risk - correct today, and depends on something the code does not state.",
|
|
18743
|
+
" nit - style or clarity. Include only when the file is otherwise clean.",
|
|
18744
|
+
"",
|
|
18745
|
+
"A finding you cannot reach with a concrete input is a risk, not a bug. Saying",
|
|
18746
|
+
"so is what keeps the bug rows worth reading."
|
|
18747
|
+
])}`
|
|
18748
|
+
})];
|
|
18749
|
+
/**
|
|
18750
|
+
* Look one up by the name someone typed.
|
|
18751
|
+
*
|
|
18752
|
+
* Both spellings are accepted - `investigator` and `p4-investigator.md` - because
|
|
18753
|
+
* the listing shows the filename and the sentence people write is the bare word,
|
|
18754
|
+
* and refusing one of the two would be refusing whichever the person happened to
|
|
18755
|
+
* read most recently.
|
|
18756
|
+
*/
|
|
18757
|
+
const findAgentPreset = (requested) => {
|
|
18758
|
+
const wanted = requested.trim().toLowerCase();
|
|
18759
|
+
if (wanted.length === 0) return void 0;
|
|
18760
|
+
return AGENT_PRESETS.find((preset) => {
|
|
18761
|
+
const fileName = preset.name.toLowerCase();
|
|
18762
|
+
const bare = fileName.slice(3, -3);
|
|
18763
|
+
return wanted === fileName || wanted === bare || wanted === `${PRESET_NAME_PREFIX}${bare}`;
|
|
18764
|
+
});
|
|
18765
|
+
};
|
|
18145
18766
|
//#endregion
|
|
18146
18767
|
//#region src/cli/assets.ts
|
|
18147
|
-
const SyncServicesLive = layer$
|
|
18768
|
+
const SyncServicesLive = layer$55.pipe(Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$61.pipe(Layer.provide(layer$68))), Layer.provideMerge(layer$68), Layer.provideMerge(FetchHttpClient.layer));
|
|
18148
18769
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
18149
18770
|
const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
|
|
18150
18771
|
return yield* Effect.gen(function* () {
|
|
@@ -18152,7 +18773,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
|
18152
18773
|
sync: yield* AssetSync,
|
|
18153
18774
|
link: yield* HubLink
|
|
18154
18775
|
});
|
|
18155
|
-
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$
|
|
18776
|
+
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
|
|
18156
18777
|
});
|
|
18157
18778
|
const STATE_LABEL = {
|
|
18158
18779
|
synced: "synced",
|
|
@@ -18186,6 +18807,14 @@ const MCP_WORDS = {
|
|
|
18186
18807
|
rootLabel: "MCP",
|
|
18187
18808
|
nameDescription: "Registration filename, for example linear.json."
|
|
18188
18809
|
};
|
|
18810
|
+
const AGENT_WORDS = {
|
|
18811
|
+
kind: "agent",
|
|
18812
|
+
command: "agents",
|
|
18813
|
+
singular: "agent definition",
|
|
18814
|
+
plural: "agent definitions",
|
|
18815
|
+
rootLabel: "Agents",
|
|
18816
|
+
nameDescription: "Definition filename, for example p4-reviewer.md."
|
|
18817
|
+
};
|
|
18189
18818
|
const rootFor = (status, kind) => status.roots.find((root) => root.kind === kind)?.path ?? null;
|
|
18190
18819
|
/**
|
|
18191
18820
|
* Exported for tests. These two are the only place a wrong report becomes a
|
|
@@ -18248,7 +18877,8 @@ const formatReport = (report) => {
|
|
|
18248
18877
|
const ALL_WORDS = [
|
|
18249
18878
|
SKILL_WORDS,
|
|
18250
18879
|
MEMORY_WORDS,
|
|
18251
|
-
MCP_WORDS
|
|
18880
|
+
MCP_WORDS,
|
|
18881
|
+
AGENT_WORDS
|
|
18252
18882
|
];
|
|
18253
18883
|
/**
|
|
18254
18884
|
* The whole picture, in one page.
|
|
@@ -18380,6 +19010,67 @@ const makeAssetCommands = (words) => {
|
|
|
18380
19010
|
const skillCommands = makeAssetCommands(SKILL_WORDS);
|
|
18381
19011
|
const memoryCommands = makeAssetCommands(MEMORY_WORDS);
|
|
18382
19012
|
const mcpCommands = makeAssetCommands(MCP_WORDS);
|
|
19013
|
+
const agentCommands = makeAssetCommands(AGENT_WORDS);
|
|
19014
|
+
/**
|
|
19015
|
+
* What one install attempt did, in a sentence.
|
|
19016
|
+
*
|
|
19017
|
+
* Exported for tests: `exists` in particular must never read as success. The
|
|
19018
|
+
* whole point of refusing to overwrite is that someone who has edited a preset
|
|
19019
|
+
* keeps their edits, and a line saying "installed" would tell them the opposite.
|
|
19020
|
+
*/
|
|
19021
|
+
const formatPresetInstall = (input) => {
|
|
19022
|
+
if (input.outcome === "created") return `Installed ${input.name}${input.path === null ? "" : ` at ${input.path}`}`;
|
|
19023
|
+
if (input.outcome === "exists") return `Skipped ${input.name}: it already exists here, and installing would overwrite your copy.`;
|
|
19024
|
+
return `Could not install ${input.name}: ${input.detail ?? input.outcome}`;
|
|
19025
|
+
};
|
|
19026
|
+
/**
|
|
19027
|
+
* `p4c agents presets` — what is on offer, before anything is written.
|
|
19028
|
+
*
|
|
19029
|
+
* Its own command rather than a flag on `install`, because reading the list is
|
|
19030
|
+
* what people do first and a listing that requires naming an install target is
|
|
19031
|
+
* not a listing.
|
|
19032
|
+
*/
|
|
19033
|
+
const presetsCommand = Command.make("presets", { ...authLocationFlags }).pipe(Command.withDescription("List the agent definitions p4code ships, without installing any."), Command.withHandler(() => Console.log([
|
|
19034
|
+
...AGENT_PRESETS.map((preset) => ` ${preset.name.padEnd(28)} ${preset.summary}`),
|
|
19035
|
+
"",
|
|
19036
|
+
"Install one with: p4c agents install <name>"
|
|
19037
|
+
].join("\n"))));
|
|
19038
|
+
/**
|
|
19039
|
+
* Install a shipped definition onto this machine, and only this machine.
|
|
19040
|
+
*
|
|
19041
|
+
* `createLocal` rather than a hub write, so an installed preset starts out
|
|
19042
|
+
* exactly like one written by hand: local, unmanaged, and shared only when
|
|
19043
|
+
* something explicitly publishes it. It also means this works with no hub
|
|
19044
|
+
* configured, which is when a fresh machine most wants it.
|
|
19045
|
+
*/
|
|
19046
|
+
const presetInstallCommand = Command.make("install", {
|
|
19047
|
+
...authLocationFlags,
|
|
19048
|
+
name: Argument.string("name").pipe(Argument.withDescription("Preset to install, for example investigator."), Argument.optional),
|
|
19049
|
+
all: Flag.boolean("all").pipe(Flag.withDescription("Install every preset p4code ships."))
|
|
19050
|
+
}).pipe(Command.withDescription("Install a p4code agent definition into this machine's agents directory."), Command.withHandler((flags) => runWithSync(flags, ({ sync }) => Effect.gen(function* () {
|
|
19051
|
+
const requested = Option.getOrUndefined(flags.name);
|
|
19052
|
+
if (!flags.all && requested === void 0) return yield* Console.error("Name a preset, or pass --all to install every one.\nSee them with: p4c agents presets");
|
|
19053
|
+
const chosen = flags.all ? AGENT_PRESETS : (() => {
|
|
19054
|
+
const found = findAgentPreset(requested ?? "");
|
|
19055
|
+
return found === void 0 ? [] : [found];
|
|
19056
|
+
})();
|
|
19057
|
+
if (chosen.length === 0) return yield* Console.error(`No preset named ${requested ?? ""}. See them with: p4c agents presets`);
|
|
19058
|
+
const lines = [];
|
|
19059
|
+
for (const preset of chosen) {
|
|
19060
|
+
const result = yield* sync.createLocal({
|
|
19061
|
+
kind: AGENT_WORDS.kind,
|
|
19062
|
+
name: preset.name,
|
|
19063
|
+
files: preset.files
|
|
19064
|
+
});
|
|
19065
|
+
lines.push(formatPresetInstall({
|
|
19066
|
+
name: preset.name,
|
|
19067
|
+
outcome: result.outcome,
|
|
19068
|
+
path: result.path,
|
|
19069
|
+
detail: result.detail
|
|
19070
|
+
}));
|
|
19071
|
+
}
|
|
19072
|
+
return yield* Console.log(lines.join("\n"));
|
|
19073
|
+
}))));
|
|
18383
19074
|
const shareAllCommand = Command.make("share-all", { ...authLocationFlags }).pipe(Command.withDescription("Share everything, including new items, automatically. The default."), Command.withHandler((flags) => runWithSync(flags, ({ sync, link }) => Effect.gen(function* () {
|
|
18384
19075
|
yield* link.setShareMode("all");
|
|
18385
19076
|
yield* Console.log(formatStatus(yield* sync.status));
|
|
@@ -18432,6 +19123,23 @@ const mcpCommand = Command.make("mcp", {}).pipe(Command.withDescription("Share M
|
|
|
18432
19123
|
mcpCommands.showCommand,
|
|
18433
19124
|
mcpCommands.removeCommand
|
|
18434
19125
|
]));
|
|
19126
|
+
/**
|
|
19127
|
+
* `p4c agents` — the subagent definitions this machine runs.
|
|
19128
|
+
*
|
|
19129
|
+
* The same six subcommands every kind gets, plus the two that are particular to
|
|
19130
|
+
* this one: p4code ships definitions, and a definition is the only place a
|
|
19131
|
+
* subagent's input and output contract can be stated.
|
|
19132
|
+
*/
|
|
19133
|
+
const agentsCommand = Command.make("agents", {}).pipe(Command.withDescription("Share subagent definitions across your machines, and install p4code's."), Command.withSubcommands([
|
|
19134
|
+
agentCommands.statusCommand,
|
|
19135
|
+
agentCommands.syncCommand,
|
|
19136
|
+
agentCommands.publishCommand,
|
|
19137
|
+
agentCommands.unpublishCommand,
|
|
19138
|
+
agentCommands.showCommand,
|
|
19139
|
+
agentCommands.removeCommand,
|
|
19140
|
+
presetsCommand,
|
|
19141
|
+
presetInstallCommand
|
|
19142
|
+
]));
|
|
18435
19143
|
/** Exported for the hub command, which owns `connect`/`disconnect`. */
|
|
18436
19144
|
const hubLinkCommands = [hubConnectCommand, hubDisconnectCommand];
|
|
18437
19145
|
//#endregion
|
|
@@ -23417,12 +24125,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
|
|
|
23417
24125
|
stderrTruncated: stderr.truncated
|
|
23418
24126
|
};
|
|
23419
24127
|
});
|
|
23420
|
-
const make$
|
|
24128
|
+
const make$59 = Effect.fn("ProcessRunner.make")(function* () {
|
|
23421
24129
|
const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
|
|
23422
24130
|
const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
|
|
23423
24131
|
return ProcessRunner.of({ run });
|
|
23424
24132
|
});
|
|
23425
|
-
const layer$
|
|
24133
|
+
const layer$54 = Layer.effect(ProcessRunner, make$59());
|
|
23426
24134
|
//#endregion
|
|
23427
24135
|
//#region src/project/RepositoryIdentityResolver.ts
|
|
23428
24136
|
const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
|
|
@@ -23513,7 +24221,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
|
|
|
23513
24221
|
rootPath: cacheKey
|
|
23514
24222
|
}) : null;
|
|
23515
24223
|
});
|
|
23516
|
-
const make$
|
|
24224
|
+
const make$58 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
|
|
23517
24225
|
const processRunner = yield* ProcessRunner;
|
|
23518
24226
|
const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
|
|
23519
24227
|
capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
|
|
@@ -23528,7 +24236,7 @@ const make$57 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
|
|
|
23528
24236
|
});
|
|
23529
24237
|
return RepositoryIdentityResolver.of({ resolve });
|
|
23530
24238
|
});
|
|
23531
|
-
const layer$
|
|
24239
|
+
const layer$53 = Layer.effect(RepositoryIdentityResolver, make$58()).pipe(Layer.provide(layer$54));
|
|
23532
24240
|
//#endregion
|
|
23533
24241
|
//#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
|
|
23534
24242
|
const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
|
|
@@ -25348,7 +26056,7 @@ function mergeWithDefaultKeybindings(custom) {
|
|
|
25348
26056
|
* Keybindings - Service tag for keybinding configuration operations.
|
|
25349
26057
|
*/
|
|
25350
26058
|
var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
|
|
25351
|
-
const make$
|
|
26059
|
+
const make$57 = Effect.gen(function* () {
|
|
25352
26060
|
const { keybindingsConfigPath } = yield* ServerConfig$1;
|
|
25353
26061
|
const fs = yield* FileSystem.FileSystem;
|
|
25354
26062
|
const path = yield* Path.Path;
|
|
@@ -25609,7 +26317,7 @@ const make$56 = Effect.gen(function* () {
|
|
|
25609
26317
|
}))
|
|
25610
26318
|
};
|
|
25611
26319
|
});
|
|
25612
|
-
const layer$
|
|
26320
|
+
const layer$52 = Layer.effect(Keybindings, make$57);
|
|
25613
26321
|
//#endregion
|
|
25614
26322
|
//#region src/process/externalLauncher.ts
|
|
25615
26323
|
/**
|
|
@@ -25836,7 +26544,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
|
|
|
25836
26544
|
cause
|
|
25837
26545
|
}));
|
|
25838
26546
|
});
|
|
25839
|
-
const make$
|
|
26547
|
+
const make$56 = Effect.gen(function* () {
|
|
25840
26548
|
const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
|
|
25841
26549
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
25842
26550
|
const path = yield* Path.Path;
|
|
@@ -25847,7 +26555,7 @@ const make$55 = Effect.gen(function* () {
|
|
|
25847
26555
|
launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
|
|
25848
26556
|
});
|
|
25849
26557
|
});
|
|
25850
|
-
const layer$
|
|
26558
|
+
const layer$51 = Layer.effect(ExternalLauncher, make$56);
|
|
25851
26559
|
//#endregion
|
|
25852
26560
|
//#region src/orchestration/Services/OrchestrationReactor.ts
|
|
25853
26561
|
/**
|
|
@@ -25865,7 +26573,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
|
|
|
25865
26573
|
//#endregion
|
|
25866
26574
|
//#region src/serverLifecycleEvents.ts
|
|
25867
26575
|
var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
|
|
25868
|
-
const make$
|
|
26576
|
+
const make$55 = Effect.gen(function* () {
|
|
25869
26577
|
const pubsub = yield* PubSub.unbounded();
|
|
25870
26578
|
const state = yield* Ref.make({
|
|
25871
26579
|
sequence: 0,
|
|
@@ -25889,7 +26597,7 @@ const make$54 = Effect.gen(function* () {
|
|
|
25889
26597
|
}
|
|
25890
26598
|
};
|
|
25891
26599
|
});
|
|
25892
|
-
const layer$
|
|
26600
|
+
const layer$50 = Layer.effect(ServerLifecycleEvents, make$55);
|
|
25893
26601
|
//#endregion
|
|
25894
26602
|
//#region src/telemetry/Identify.ts
|
|
25895
26603
|
const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
|
|
@@ -26062,7 +26770,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
|
|
|
26062
26770
|
/** No-op layer for callers that intentionally disable telemetry. */
|
|
26063
26771
|
static layerTest = Layer.succeed(AnalyticsService, inert);
|
|
26064
26772
|
};
|
|
26065
|
-
const make$
|
|
26773
|
+
const make$54 = Effect.gen(function* () {
|
|
26066
26774
|
const telemetryConfig = yield* TelemetryEnvConfig;
|
|
26067
26775
|
const posthogKey = telemetryConfig.posthogKey.trim();
|
|
26068
26776
|
if (!telemetryConfig.enabled || posthogKey === "") return inert;
|
|
@@ -26132,7 +26840,7 @@ const make$53 = Effect.gen(function* () {
|
|
|
26132
26840
|
flush
|
|
26133
26841
|
});
|
|
26134
26842
|
});
|
|
26135
|
-
const layer$
|
|
26843
|
+
const layer$49 = Layer.effect(AnalyticsService, make$54);
|
|
26136
26844
|
AnalyticsService.layerTest;
|
|
26137
26845
|
//#endregion
|
|
26138
26846
|
//#region src/service/pinnedRuntime.ts
|
|
@@ -26479,7 +27187,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
|
|
|
26479
27187
|
}
|
|
26480
27188
|
};
|
|
26481
27189
|
var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
|
|
26482
|
-
const make$
|
|
27190
|
+
const make$53 = Effect.fn("cloud.boot_service.make")(function* (input) {
|
|
26483
27191
|
const hostExecPath = yield* HostProcessExecutablePath;
|
|
26484
27192
|
const hostArguments = yield* HostProcessArguments;
|
|
26485
27193
|
const host = input.host ?? {
|
|
@@ -26698,7 +27406,7 @@ const make$52 = Effect.fn("cloud.boot_service.make")(function* (input) {
|
|
|
26698
27406
|
logPath
|
|
26699
27407
|
});
|
|
26700
27408
|
});
|
|
26701
|
-
const layer$
|
|
27409
|
+
const layer$48 = (input) => Layer.effect(BootService, make$53(input));
|
|
26702
27410
|
//#endregion
|
|
26703
27411
|
//#region src/service/selfUpdate.ts
|
|
26704
27412
|
/**
|
|
@@ -26773,7 +27481,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
|
|
|
26773
27481
|
return null;
|
|
26774
27482
|
});
|
|
26775
27483
|
var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
|
|
26776
|
-
const make$
|
|
27484
|
+
const make$52 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
26777
27485
|
const serverConfig = yield* ServerConfig$1;
|
|
26778
27486
|
const fs = yield* FileSystem.FileSystem;
|
|
26779
27487
|
const path = yield* Path.Path;
|
|
@@ -26923,7 +27631,7 @@ const make$51 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
26923
27631
|
});
|
|
26924
27632
|
return ServerSelfUpdate.of({ update });
|
|
26925
27633
|
});
|
|
26926
|
-
const layer$
|
|
27634
|
+
const layer$47 = Layer.effect(ServerSelfUpdate, make$52()).pipe(Layer.provide(layer$54));
|
|
26927
27635
|
//#endregion
|
|
26928
27636
|
//#region src/environment/ServerEnvironmentLabel.ts
|
|
26929
27637
|
const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
|
|
@@ -27055,7 +27763,7 @@ function platformArch(architecture) {
|
|
|
27055
27763
|
default: return "other";
|
|
27056
27764
|
}
|
|
27057
27765
|
}
|
|
27058
|
-
const make$
|
|
27766
|
+
const make$51 = Effect.gen(function* () {
|
|
27059
27767
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
27060
27768
|
const path = yield* Path.Path;
|
|
27061
27769
|
const serverConfig = yield* ServerConfig$1;
|
|
@@ -27118,7 +27826,7 @@ const make$50 = Effect.gen(function* () {
|
|
|
27118
27826
|
* state. It intentionally has no fallback Layer.succeed value: callers must
|
|
27119
27827
|
* provide the external platform services and a ServerConfig.
|
|
27120
27828
|
*/
|
|
27121
|
-
const layer$
|
|
27829
|
+
const layer$46 = Layer.effect(ServerEnvironment, make$51).pipe(Layer.provide(layer$54));
|
|
27122
27830
|
//#endregion
|
|
27123
27831
|
//#region src/provider/Services/ProviderSessionReaper.ts
|
|
27124
27832
|
var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
|
|
@@ -28195,7 +28903,7 @@ const maybeOpenBrowser = (target) => Effect.gen(function* () {
|
|
|
28195
28903
|
yield* (yield* ExternalLauncher).launchBrowser(target).pipe(Effect.catch(() => Effect.logInfo("browser auto-open unavailable", { hint: `Open ${target} in your browser.` })));
|
|
28196
28904
|
});
|
|
28197
28905
|
const runStartupPhase = (phase, effect) => effect.pipe(Effect.annotateSpans({ "startup.phase": phase }), Effect.withSpan(`server.startup.${phase}`));
|
|
28198
|
-
const make$
|
|
28906
|
+
const make$50 = Effect.gen(function* () {
|
|
28199
28907
|
const serverConfig = yield* ServerConfig$1;
|
|
28200
28908
|
const keybindings = yield* Keybindings;
|
|
28201
28909
|
const orchestrationReactor = yield* OrchestrationReactor;
|
|
@@ -28336,7 +29044,7 @@ const make$49 = Effect.gen(function* () {
|
|
|
28336
29044
|
enqueueCommand: commandGate.enqueueCommand
|
|
28337
29045
|
};
|
|
28338
29046
|
});
|
|
28339
|
-
const layer$
|
|
29047
|
+
const layer$45 = Layer.effect(ServerRuntimeStartup, make$50);
|
|
28340
29048
|
//#endregion
|
|
28341
29049
|
//#region src/serverRuntimeState.ts
|
|
28342
29050
|
const PersistedServerRuntimeState = Schema$1.Struct({
|
|
@@ -28484,7 +29192,7 @@ function expandHomePath$2(input, path) {
|
|
|
28484
29192
|
if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
|
|
28485
29193
|
return input;
|
|
28486
29194
|
}
|
|
28487
|
-
const make$
|
|
29195
|
+
const make$49 = Effect.gen(function* () {
|
|
28488
29196
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
28489
29197
|
const path = yield* Path.Path;
|
|
28490
29198
|
const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
|
|
@@ -28541,7 +29249,7 @@ const make$48 = Effect.gen(function* () {
|
|
|
28541
29249
|
resolveRelativePathWithinRoot
|
|
28542
29250
|
});
|
|
28543
29251
|
});
|
|
28544
|
-
const layer$
|
|
29252
|
+
const layer$44 = Layer.effect(WorkspacePaths, make$49);
|
|
28545
29253
|
//#endregion
|
|
28546
29254
|
//#region src/cli/project.ts
|
|
28547
29255
|
const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
|
|
@@ -28647,7 +29355,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
|
|
|
28647
29355
|
operation: "generateProjectCommandId",
|
|
28648
29356
|
cause
|
|
28649
29357
|
})));
|
|
28650
|
-
const ProjectCliRuntimeLive = Layer.mergeAll(layer$
|
|
29358
|
+
const ProjectCliRuntimeLive = Layer.mergeAll(layer$44, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$53), Layer.provideMerge(layerConfig)));
|
|
28651
29359
|
const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
|
|
28652
29360
|
const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
|
|
28653
29361
|
scopes: AuthAdministrativeScopes,
|
|
@@ -28738,7 +29446,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
28738
29446
|
});
|
|
28739
29447
|
yield* Console.log(output);
|
|
28740
29448
|
}));
|
|
28741
|
-
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$
|
|
29449
|
+
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
|
|
28742
29450
|
return yield* Effect.gen(function* () {
|
|
28743
29451
|
const snapshot = yield* getOfflineSnapshot();
|
|
28744
29452
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
@@ -28749,7 +29457,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
28749
29457
|
});
|
|
28750
29458
|
yield* Console.log(output);
|
|
28751
29459
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
28752
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$
|
|
29460
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$44).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$71(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
28753
29461
|
});
|
|
28754
29462
|
const projectAddCommand = Command.make("add", {
|
|
28755
29463
|
...projectLocationFlags,
|
|
@@ -29356,7 +30064,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
|
|
|
29356
30064
|
filePath: error.filePath,
|
|
29357
30065
|
errorTag: error._tag
|
|
29358
30066
|
}));
|
|
29359
|
-
const make$
|
|
30067
|
+
const make$48 = Effect.gen(function* () {
|
|
29360
30068
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
29361
30069
|
const path = yield* Path.Path;
|
|
29362
30070
|
const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
|
|
@@ -29377,7 +30085,7 @@ const make$47 = Effect.gen(function* () {
|
|
|
29377
30085
|
});
|
|
29378
30086
|
return P4ProjectFileLoader.of({ load });
|
|
29379
30087
|
});
|
|
29380
|
-
const layer$
|
|
30088
|
+
const layer$43 = Layer.effect(P4ProjectFileLoader, make$48);
|
|
29381
30089
|
//#endregion
|
|
29382
30090
|
//#region src/project/ProjectFaviconResolver.ts
|
|
29383
30091
|
/**
|
|
@@ -29448,7 +30156,7 @@ function extractIconHref(source) {
|
|
|
29448
30156
|
return null;
|
|
29449
30157
|
}
|
|
29450
30158
|
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) }));
|
|
29451
|
-
const make$
|
|
30159
|
+
const make$47 = Effect.gen(function* () {
|
|
29452
30160
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
29453
30161
|
const path = yield* Path.Path;
|
|
29454
30162
|
const workspacePaths = yield* WorkspacePaths;
|
|
@@ -29517,7 +30225,7 @@ const make$46 = Effect.gen(function* () {
|
|
|
29517
30225
|
});
|
|
29518
30226
|
return ProjectFaviconResolver.of({ resolvePath });
|
|
29519
30227
|
});
|
|
29520
|
-
const layer$
|
|
30228
|
+
const layer$42 = Layer.effect(ProjectFaviconResolver, make$47);
|
|
29521
30229
|
//#endregion
|
|
29522
30230
|
//#region src/assets/AssetAccess.ts
|
|
29523
30231
|
const ASSET_ROUTE_PREFIX = "/api/assets";
|
|
@@ -29770,10 +30478,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
|
|
|
29770
30478
|
//#endregion
|
|
29771
30479
|
//#region src/observability/BrowserTraceCollector.ts
|
|
29772
30480
|
var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
|
|
29773
|
-
const make$
|
|
30481
|
+
const make$46 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
|
|
29774
30482
|
for (const record of records) sink.push(record);
|
|
29775
30483
|
}) });
|
|
29776
|
-
const layer$
|
|
30484
|
+
const layer$41 = (sink) => Layer.succeed(BrowserTraceCollector, make$46(sink));
|
|
29777
30485
|
//#endregion
|
|
29778
30486
|
//#region src/auth/http.ts
|
|
29779
30487
|
const CREDENTIAL_RESPONSE_HEADERS = {
|
|
@@ -30736,7 +31444,7 @@ const NESTED_PAYLOAD_KEYS = [
|
|
|
30736
31444
|
"operations"
|
|
30737
31445
|
];
|
|
30738
31446
|
const MAX_COLLECT_DEPTH = 4;
|
|
30739
|
-
function asRecord$
|
|
31447
|
+
function asRecord$4(value) {
|
|
30740
31448
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
30741
31449
|
}
|
|
30742
31450
|
function pushChangedFilePath(target, value) {
|
|
@@ -30751,7 +31459,7 @@ function collectChangedFilePaths(value, target, depth) {
|
|
|
30751
31459
|
for (const entry of value) collectChangedFilePaths(entry, target, depth + 1);
|
|
30752
31460
|
return;
|
|
30753
31461
|
}
|
|
30754
|
-
const record = asRecord$
|
|
31462
|
+
const record = asRecord$4(value);
|
|
30755
31463
|
if (!record) return;
|
|
30756
31464
|
for (const field of CHANGED_FILE_FIELDS) pushChangedFilePath(target, record[field]);
|
|
30757
31465
|
for (const nestedKey of NESTED_PAYLOAD_KEYS) if (nestedKey in record) collectChangedFilePaths(record[nestedKey], target, depth + 1);
|
|
@@ -30762,7 +31470,7 @@ function collectChangedFilePaths(value, target, depth) {
|
|
|
30762
31470
|
*/
|
|
30763
31471
|
function collectActivityChangedFilePaths(payload) {
|
|
30764
31472
|
const target = /* @__PURE__ */ new Set();
|
|
30765
|
-
collectChangedFilePaths(asRecord$
|
|
31473
|
+
collectChangedFilePaths(asRecord$4(asRecord$4(payload)?.data), target, 0);
|
|
30766
31474
|
return target;
|
|
30767
31475
|
}
|
|
30768
31476
|
/**
|
|
@@ -32667,7 +33375,7 @@ const classifyNonZeroExit = (command, stderr) => {
|
|
|
32667
33375
|
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";
|
|
32668
33376
|
return "command-failed";
|
|
32669
33377
|
};
|
|
32670
|
-
const make$
|
|
33378
|
+
const make$45 = Effect.gen(function* () {
|
|
32671
33379
|
const processRunner = yield* ProcessRunner;
|
|
32672
33380
|
const run = Effect.fn("VcsProcess.run")(function* (input) {
|
|
32673
33381
|
const baseError = {
|
|
@@ -32724,7 +33432,7 @@ const make$44 = Effect.gen(function* () {
|
|
|
32724
33432
|
});
|
|
32725
33433
|
return VcsProcess.of({ run });
|
|
32726
33434
|
});
|
|
32727
|
-
const layer$
|
|
33435
|
+
const layer$40 = Layer.effect(VcsProcess, make$45).pipe(Layer.provide(layer$54));
|
|
32728
33436
|
//#endregion
|
|
32729
33437
|
//#region src/vcs/VcsDriver.ts
|
|
32730
33438
|
var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
|
|
@@ -33175,12 +33883,12 @@ const makeVcsDriver = Effect.gen(function* () {
|
|
|
33175
33883
|
const driver = yield* makeVcsDriverShape();
|
|
33176
33884
|
return VcsDriver.of(driver);
|
|
33177
33885
|
});
|
|
33178
|
-
const make$
|
|
33886
|
+
const make$44 = Effect.gen(function* () {
|
|
33179
33887
|
const git = yield* makeGitVcsDriverCore();
|
|
33180
33888
|
return GitVcsDriver.of(git);
|
|
33181
33889
|
});
|
|
33182
33890
|
Layer.effect(VcsDriver, makeVcsDriver);
|
|
33183
|
-
const layer$
|
|
33891
|
+
const layer$39 = Layer.effect(GitVcsDriver, make$44);
|
|
33184
33892
|
//#endregion
|
|
33185
33893
|
//#region src/vcs/VcsProjectConfig.ts
|
|
33186
33894
|
const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
|
|
@@ -33212,7 +33920,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
|
|
|
33212
33920
|
configPath: error.configPath,
|
|
33213
33921
|
errorTag: error._tag
|
|
33214
33922
|
}));
|
|
33215
|
-
const make$
|
|
33923
|
+
const make$43 = Effect.gen(function* () {
|
|
33216
33924
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
33217
33925
|
const path = yield* Path.Path;
|
|
33218
33926
|
const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
|
|
@@ -33253,7 +33961,7 @@ const make$42 = Effect.gen(function* () {
|
|
|
33253
33961
|
});
|
|
33254
33962
|
return VcsProjectConfig.of({ resolveKind });
|
|
33255
33963
|
});
|
|
33256
|
-
const layer$
|
|
33964
|
+
const layer$38 = Layer.effect(VcsProjectConfig, make$43);
|
|
33257
33965
|
//#endregion
|
|
33258
33966
|
//#region src/vcs/VcsDriverRegistry.ts
|
|
33259
33967
|
const DETECTION_CACHE_CAPACITY = 2048;
|
|
@@ -33273,7 +33981,7 @@ function parseDetectionCacheKey(key) {
|
|
|
33273
33981
|
cwd: key.slice(separatorIndex + 1)
|
|
33274
33982
|
};
|
|
33275
33983
|
}
|
|
33276
|
-
const make$
|
|
33984
|
+
const make$42 = Effect.gen(function* () {
|
|
33277
33985
|
const projectConfig = yield* VcsProjectConfig;
|
|
33278
33986
|
const git = yield* makeVcsDriver;
|
|
33279
33987
|
const drivers = { git };
|
|
@@ -33330,7 +34038,7 @@ const make$41 = Effect.gen(function* () {
|
|
|
33330
34038
|
resolve
|
|
33331
34039
|
});
|
|
33332
34040
|
});
|
|
33333
|
-
const layer$
|
|
34041
|
+
const layer$37 = Layer.effect(VcsDriverRegistry, make$42).pipe(Layer.provide(layer$38));
|
|
33334
34042
|
//#endregion
|
|
33335
34043
|
//#region src/checkpointing/CheckpointStore.ts
|
|
33336
34044
|
/**
|
|
@@ -33350,7 +34058,7 @@ const layer$36 = Layer.effect(VcsDriverRegistry, make$41).pipe(Layer.provide(lay
|
|
|
33350
34058
|
*/
|
|
33351
34059
|
/** Service tag for checkpoint persistence and restore operations. */
|
|
33352
34060
|
var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
|
|
33353
|
-
const make$
|
|
34061
|
+
const make$41 = Effect.gen(function* () {
|
|
33354
34062
|
const vcsRegistry = yield* VcsDriverRegistry;
|
|
33355
34063
|
const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
|
|
33356
34064
|
const handle = yield* vcsRegistry.resolve({ cwd });
|
|
@@ -33389,7 +34097,7 @@ const make$40 = Effect.gen(function* () {
|
|
|
33389
34097
|
deleteCheckpointRefs
|
|
33390
34098
|
});
|
|
33391
34099
|
});
|
|
33392
|
-
const layer$
|
|
34100
|
+
const layer$36 = Layer.effect(CheckpointStore, make$41);
|
|
33393
34101
|
//#endregion
|
|
33394
34102
|
//#region src/checkpointing/CheckpointDiffQuery.ts
|
|
33395
34103
|
/**
|
|
@@ -33411,7 +34119,7 @@ function buildTurnDiffResult(input, diff) {
|
|
|
33411
34119
|
diff
|
|
33412
34120
|
};
|
|
33413
34121
|
}
|
|
33414
|
-
const make$
|
|
34122
|
+
const make$40 = Effect.gen(function* () {
|
|
33415
34123
|
const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
|
|
33416
34124
|
const checkpointStore = yield* CheckpointStore;
|
|
33417
34125
|
const threadActivities = yield* ProjectionThreadActivityRepository;
|
|
@@ -33577,7 +34285,7 @@ const make$39 = Effect.gen(function* () {
|
|
|
33577
34285
|
getFullThreadDiff
|
|
33578
34286
|
});
|
|
33579
34287
|
});
|
|
33580
|
-
const layer$
|
|
34288
|
+
const layer$35 = Layer.effect(CheckpointDiffQuery, make$40);
|
|
33581
34289
|
//#endregion
|
|
33582
34290
|
//#region ../../packages/shared/src/toolCategory.ts
|
|
33583
34291
|
const TOOL_CATEGORY_TITLES = {
|
|
@@ -33846,22 +34554,22 @@ function classifyToolCategory(input) {
|
|
|
33846
34554
|
if (normalized.includes("image")) return "image_view";
|
|
33847
34555
|
return "tool";
|
|
33848
34556
|
}
|
|
33849
|
-
function asRecord$
|
|
34557
|
+
function asRecord$3(value) {
|
|
33850
34558
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
33851
34559
|
}
|
|
33852
34560
|
/** Classify from a runtime item payload's `data` (`{ toolName, input }`). */
|
|
33853
34561
|
function classifyToolCategoryFromToolData(data) {
|
|
33854
|
-
const record = asRecord$
|
|
34562
|
+
const record = asRecord$3(data);
|
|
33855
34563
|
const toolName = record?.toolName;
|
|
33856
34564
|
if (typeof toolName !== "string" || toolName.trim().length === 0) return;
|
|
33857
34565
|
return classifyToolCategory({
|
|
33858
34566
|
toolName,
|
|
33859
|
-
toolInput: asRecord$
|
|
34567
|
+
toolInput: asRecord$3(record?.input)
|
|
33860
34568
|
});
|
|
33861
34569
|
}
|
|
33862
34570
|
//#endregion
|
|
33863
34571
|
//#region src/orchestration/ActivityPayloadProjection.ts
|
|
33864
|
-
function asRecord$
|
|
34572
|
+
function asRecord$2(value) {
|
|
33865
34573
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
33866
34574
|
}
|
|
33867
34575
|
function asTrimmedString$1(value) {
|
|
@@ -33884,7 +34592,7 @@ function collectChangedFiles(value, target, seen, depth) {
|
|
|
33884
34592
|
}
|
|
33885
34593
|
return;
|
|
33886
34594
|
}
|
|
33887
|
-
const record = asRecord$
|
|
34595
|
+
const record = asRecord$2(value);
|
|
33888
34596
|
if (!record) return;
|
|
33889
34597
|
pushChangedFile(target, seen, record.path);
|
|
33890
34598
|
pushChangedFile(target, seen, record.filePath);
|
|
@@ -33910,13 +34618,13 @@ function collectChangedFiles(value, target, seen, depth) {
|
|
|
33910
34618
|
}
|
|
33911
34619
|
}
|
|
33912
34620
|
function projectCommandData(data) {
|
|
33913
|
-
const item = asRecord$
|
|
34621
|
+
const item = asRecord$2(data.item);
|
|
33914
34622
|
if (!item) return;
|
|
33915
34623
|
const projectedItem = {};
|
|
33916
34624
|
if ("command" in item) projectedItem.command = item.command;
|
|
33917
|
-
const input = asRecord$
|
|
34625
|
+
const input = asRecord$2(item.input);
|
|
33918
34626
|
if (input && "command" in input) projectedItem.input = { command: input.command };
|
|
33919
|
-
const result = asRecord$
|
|
34627
|
+
const result = asRecord$2(item.result);
|
|
33920
34628
|
if (result && "command" in result) projectedItem.result = { command: result.command };
|
|
33921
34629
|
return Object.keys(projectedItem).length > 0 ? projectedItem : void 0;
|
|
33922
34630
|
}
|
|
@@ -33932,7 +34640,7 @@ function summarizeToolTextOutput(value) {
|
|
|
33932
34640
|
return null;
|
|
33933
34641
|
}
|
|
33934
34642
|
function projectRawOutput(value) {
|
|
33935
|
-
const rawOutput = asRecord$
|
|
34643
|
+
const rawOutput = asRecord$2(value);
|
|
33936
34644
|
if (!rawOutput) return;
|
|
33937
34645
|
if (typeof rawOutput.totalFiles === "number" && Number.isFinite(rawOutput.totalFiles)) return {
|
|
33938
34646
|
totalFiles: rawOutput.totalFiles,
|
|
@@ -33954,14 +34662,14 @@ function projectRawOutput(value) {
|
|
|
33954
34662
|
* the full payload in persistence and the event store.
|
|
33955
34663
|
*/
|
|
33956
34664
|
function projectActivityPayload(activity) {
|
|
33957
|
-
const payload = asRecord$
|
|
33958
|
-
const data = asRecord$
|
|
34665
|
+
const payload = asRecord$2(activity.payload);
|
|
34666
|
+
const data = asRecord$2(payload?.data);
|
|
33959
34667
|
if (!payload || !data || payload.itemType === "mcp_tool_call") return activity;
|
|
33960
34668
|
const projectedData = {};
|
|
33961
34669
|
const item = projectCommandData(data);
|
|
33962
34670
|
if (item) projectedData.item = item;
|
|
33963
34671
|
if ("command" in data) projectedData.command = data.command;
|
|
33964
|
-
const input = asRecord$
|
|
34672
|
+
const input = asRecord$2(data.input);
|
|
33965
34673
|
if (input && "command" in input) projectedData.input = { command: input.command };
|
|
33966
34674
|
const changedFiles = [];
|
|
33967
34675
|
collectChangedFiles(data, changedFiles, /* @__PURE__ */ new Set(), 0);
|
|
@@ -33988,7 +34696,7 @@ function projectActivityPayload(activity) {
|
|
|
33988
34696
|
*/
|
|
33989
34697
|
function isResolvableContextWindowActivity(activity) {
|
|
33990
34698
|
if (activity.kind !== "context-window.updated") return false;
|
|
33991
|
-
const usedTokens = asRecord$
|
|
34699
|
+
const usedTokens = asRecord$2(activity.payload)?.usedTokens;
|
|
33992
34700
|
return typeof usedTokens === "number" && Number.isFinite(usedTokens) && usedTokens >= 0;
|
|
33993
34701
|
}
|
|
33994
34702
|
/**
|
|
@@ -34004,7 +34712,7 @@ function isResolvableContextWindowActivity(activity) {
|
|
|
34004
34712
|
* client.
|
|
34005
34713
|
*/
|
|
34006
34714
|
function withoutContextWindowBreakdown$1(activity) {
|
|
34007
|
-
const payload = asRecord$
|
|
34715
|
+
const payload = asRecord$2(activity.payload);
|
|
34008
34716
|
if (!payload || payload.breakdown === void 0) return activity;
|
|
34009
34717
|
const { breakdown: _breakdown, ...rest } = payload;
|
|
34010
34718
|
return {
|
|
@@ -34019,7 +34727,7 @@ function dropStaleContextWindowActivities(activities) {
|
|
|
34019
34727
|
const retainedIndexes = new Set(latestIndexByTurn.values());
|
|
34020
34728
|
let breakdownIndex = null;
|
|
34021
34729
|
for (const index of retainedIndexes) {
|
|
34022
|
-
if (asRecord$
|
|
34730
|
+
if (asRecord$2(activities[index].payload)?.breakdown === void 0) continue;
|
|
34023
34731
|
if (breakdownIndex === null || index > breakdownIndex) breakdownIndex = index;
|
|
34024
34732
|
}
|
|
34025
34733
|
return activities.flatMap((activity, index) => {
|
|
@@ -34147,6 +34855,7 @@ const buildTaskRow = (input, taskId, timestamp, repositoryKey = null) => ({
|
|
|
34147
34855
|
projectId: input.projectId ?? null,
|
|
34148
34856
|
repositoryKey: repositoryKey ?? input.repositoryKey ?? null,
|
|
34149
34857
|
threadId: input.threadId ?? null,
|
|
34858
|
+
externalRef: input.externalRef ?? null,
|
|
34150
34859
|
title: input.title,
|
|
34151
34860
|
body: input.body ?? "",
|
|
34152
34861
|
status: input.status ?? "backlog",
|
|
@@ -34739,7 +35448,7 @@ function makeUpdateState(input) {
|
|
|
34739
35448
|
output: input.output ?? null
|
|
34740
35449
|
};
|
|
34741
35450
|
}
|
|
34742
|
-
const make$
|
|
35451
|
+
const make$39 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
|
|
34743
35452
|
const providerRegistry = yield* ProviderRegistry;
|
|
34744
35453
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
34745
35454
|
const httpClient = yield* HttpClient.HttpClient;
|
|
@@ -34854,7 +35563,7 @@ const make$38 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
|
|
|
34854
35563
|
});
|
|
34855
35564
|
return ProviderMaintenanceRunner.of({ updateProvider });
|
|
34856
35565
|
});
|
|
34857
|
-
const layer$
|
|
35566
|
+
const layer$34 = Layer.effect(ProviderMaintenanceRunner, make$39());
|
|
34858
35567
|
//#endregion
|
|
34859
35568
|
//#region src/provider/Services/ProviderInstanceRegistry.ts
|
|
34860
35569
|
var ProviderInstanceRegistry = class extends Context.Service()("@p4code/cli/provider/Services/ProviderInstanceRegistry") {};
|
|
@@ -34878,11 +35587,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
|
|
|
34878
35587
|
detail: "This provider does not report account usage."
|
|
34879
35588
|
}))))
|
|
34880
35589
|
});
|
|
34881
|
-
const make$
|
|
35590
|
+
const make$38 = Effect.gen(function* () {
|
|
34882
35591
|
const registry = yield* ProviderInstanceRegistry;
|
|
34883
35592
|
return makeTextGenerationFromRegistry(registry);
|
|
34884
35593
|
});
|
|
34885
|
-
const layer$
|
|
35594
|
+
const layer$33 = Layer.effect(TextGeneration, make$38);
|
|
34886
35595
|
const SKILL_MANIFEST_FILENAME = "SKILL.md";
|
|
34887
35596
|
/**
|
|
34888
35597
|
* Split a catalogue id (`owner/repo/skill-name`) into its parts.
|
|
@@ -34991,7 +35700,7 @@ function toAssetFilePath(repositoryPath, root) {
|
|
|
34991
35700
|
const REGISTRY_SEARCH_URL = "https://www.skills.sh/api/search";
|
|
34992
35701
|
const GITHUB_API_URL = "https://api.github.com";
|
|
34993
35702
|
const GITHUB_RAW_URL = "https://raw.githubusercontent.com";
|
|
34994
|
-
const REQUEST_TIMEOUT_MS = 15e3;
|
|
35703
|
+
const REQUEST_TIMEOUT_MS$1 = 15e3;
|
|
34995
35704
|
/** More than a person reads, few enough that the panel is a list and not a feed. */
|
|
34996
35705
|
const MAX_SEARCH_RESULTS = 25;
|
|
34997
35706
|
/**
|
|
@@ -35028,10 +35737,10 @@ const emptyFetch = (id, unavailable) => ({
|
|
|
35028
35737
|
skipped: [],
|
|
35029
35738
|
unavailable
|
|
35030
35739
|
});
|
|
35031
|
-
const make$
|
|
35740
|
+
const make$37 = Effect.gen(function* () {
|
|
35032
35741
|
const http = yield* HttpClient.HttpClient;
|
|
35033
35742
|
const request = Effect.fn("SkillRegistry.request")(function* (url) {
|
|
35034
|
-
return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
|
|
35743
|
+
return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$1));
|
|
35035
35744
|
});
|
|
35036
35745
|
const search = (query) => Effect.gen(function* () {
|
|
35037
35746
|
const response = yield* request(`${REGISTRY_SEARCH_URL}?q=${encodeURIComponent(query)}`).pipe(Effect.orElseSucceed(() => null));
|
|
@@ -35099,7 +35808,309 @@ const make$36 = Effect.gen(function* () {
|
|
|
35099
35808
|
fetch
|
|
35100
35809
|
};
|
|
35101
35810
|
});
|
|
35102
|
-
const layer$
|
|
35811
|
+
const layer$32 = Layer.effect(SkillRegistry, make$37);
|
|
35812
|
+
//#endregion
|
|
35813
|
+
//#region ../../packages/shared/src/ticketReference.ts
|
|
35814
|
+
/**
|
|
35815
|
+
* Recognizing a tracker reference in text somebody pasted.
|
|
35816
|
+
*
|
|
35817
|
+
* Shared because both ends need the same answer: the composer decides whether a
|
|
35818
|
+
* paste is worth a round trip, and the server decides what to ask the tracker.
|
|
35819
|
+
* Two copies of this rule would differ, and the difference would read as "it
|
|
35820
|
+
* works on my machine but the paste does nothing".
|
|
35821
|
+
*
|
|
35822
|
+
* @module ticketReference
|
|
35823
|
+
*/
|
|
35824
|
+
/**
|
|
35825
|
+
* A Linear issue URL, e.g.
|
|
35826
|
+
* `https://linear.app/omnicasa/issue/MOBILE-12262/haptic-feedback`.
|
|
35827
|
+
*
|
|
35828
|
+
* The workspace slug is what makes the URL unguessable from the identifier
|
|
35829
|
+
* alone, which is why a resolved ticket keeps the tracker's own URL rather than
|
|
35830
|
+
* rebuilding one.
|
|
35831
|
+
*/
|
|
35832
|
+
const LINEAR_ISSUE_URL_PATTERN = /https?:\/\/(?:www\.)?linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]*-\d+)/;
|
|
35833
|
+
/**
|
|
35834
|
+
* A bare identifier, e.g. `MOBILE-12262`.
|
|
35835
|
+
*
|
|
35836
|
+
* Anchored: only a paste that is *nothing but* an identifier counts. Scanning
|
|
35837
|
+
* prose for this shape would claim things that are not tickets - a date range,
|
|
35838
|
+
* a version, a p4code readable id like `P4-40` - and each false positive costs
|
|
35839
|
+
* a request and, worse, a wrong link.
|
|
35840
|
+
*/
|
|
35841
|
+
const BARE_IDENTIFIER_PATTERN = /^([A-Za-z][A-Za-z0-9]*-\d+)$/;
|
|
35842
|
+
/**
|
|
35843
|
+
* The issue identifier a paste refers to, or `null` when it refers to none.
|
|
35844
|
+
*
|
|
35845
|
+
* Case is normalized up, the way every tracker prints it, so the same ticket
|
|
35846
|
+
* pasted two ways compares equal.
|
|
35847
|
+
*/
|
|
35848
|
+
function parseTicketReference(text) {
|
|
35849
|
+
const trimmed = text.trim();
|
|
35850
|
+
if (trimmed.length === 0) return null;
|
|
35851
|
+
const url = LINEAR_ISSUE_URL_PATTERN.exec(trimmed);
|
|
35852
|
+
if (url?.[1] !== void 0) return url[1].toUpperCase();
|
|
35853
|
+
const bare = BARE_IDENTIFIER_PATTERN.exec(trimmed);
|
|
35854
|
+
if (bare?.[1] !== void 0) return bare[1].toUpperCase();
|
|
35855
|
+
return null;
|
|
35856
|
+
}
|
|
35857
|
+
//#endregion
|
|
35858
|
+
//#region src/mcp/McpToolClient.ts
|
|
35859
|
+
/**
|
|
35860
|
+
* Calling a tool on somebody else's MCP server.
|
|
35861
|
+
*
|
|
35862
|
+
* The rest of `apps/server/src/mcp` points the other way: it *exposes* p4code's
|
|
35863
|
+
* toolkit to an agent. This is the only place p4code is the client, and it
|
|
35864
|
+
* exists because one feature - recognizing a pasted ticket - needs an answer
|
|
35865
|
+
* before any agent turn runs, from a server the user has already registered.
|
|
35866
|
+
*
|
|
35867
|
+
* Deliberately not a general MCP client. There is no session reuse, no tool
|
|
35868
|
+
* listing, no notifications, no SSE stream held open: one request/response pair
|
|
35869
|
+
* per call, which is all streamable HTTP requires for a plain `tools/call`. A
|
|
35870
|
+
* real client belongs here only once something needs one.
|
|
35871
|
+
*
|
|
35872
|
+
* @module mcp/McpToolClient
|
|
35873
|
+
*/
|
|
35874
|
+
/**
|
|
35875
|
+
* The version p4code speaks. Sent on `initialize` and echoed back as a header
|
|
35876
|
+
* on the follow-up calls, which is what the spec asks of a client that has
|
|
35877
|
+
* already negotiated.
|
|
35878
|
+
*/
|
|
35879
|
+
const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
35880
|
+
const REQUEST_TIMEOUT_MS = 15e3;
|
|
35881
|
+
var McpToolCallError = class extends Schema$1.TaggedErrorClass()("McpToolCallError", {
|
|
35882
|
+
detail: Schema$1.String,
|
|
35883
|
+
status: Schema$1.NullOr(Schema$1.Number)
|
|
35884
|
+
}) {
|
|
35885
|
+
get message() {
|
|
35886
|
+
return this.detail;
|
|
35887
|
+
}
|
|
35888
|
+
};
|
|
35889
|
+
const unauthorized$1 = (status) => status === 401 || status === 403;
|
|
35890
|
+
/**
|
|
35891
|
+
* Read a JSON-RPC envelope out of a response that may be either JSON or SSE.
|
|
35892
|
+
*
|
|
35893
|
+
* A streamable-http server picks the encoding, and both are legal answers to a
|
|
35894
|
+
* single call. The SSE branch keeps the last `data:` payload that parses: a
|
|
35895
|
+
* server is free to emit progress notifications ahead of the result, and the
|
|
35896
|
+
* result is what a caller of this asked for.
|
|
35897
|
+
*/
|
|
35898
|
+
const readEnvelope = (body) => {
|
|
35899
|
+
const direct = parseJsonObject(body);
|
|
35900
|
+
if (direct !== void 0) return direct;
|
|
35901
|
+
let latest;
|
|
35902
|
+
for (const line of body.split("\n")) {
|
|
35903
|
+
const trimmed = line.trim();
|
|
35904
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
35905
|
+
const parsed = parseJsonObject(trimmed.slice(5).trim());
|
|
35906
|
+
if (parsed !== void 0) latest = parsed;
|
|
35907
|
+
}
|
|
35908
|
+
return latest;
|
|
35909
|
+
};
|
|
35910
|
+
const parseJsonObject = (text) => {
|
|
35911
|
+
if (text.length === 0) return void 0;
|
|
35912
|
+
try {
|
|
35913
|
+
const parsed = JSON.parse(text);
|
|
35914
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
35915
|
+
} catch {
|
|
35916
|
+
return;
|
|
35917
|
+
}
|
|
35918
|
+
};
|
|
35919
|
+
const textOf = (content) => {
|
|
35920
|
+
if (!Array.isArray(content)) return "";
|
|
35921
|
+
const parts = [];
|
|
35922
|
+
for (const block of content) {
|
|
35923
|
+
if (typeof block !== "object" || block === null) continue;
|
|
35924
|
+
const record = block;
|
|
35925
|
+
if (record["type"] === "text" && typeof record["text"] === "string") parts.push(record["text"]);
|
|
35926
|
+
}
|
|
35927
|
+
return parts.join("\n");
|
|
35928
|
+
};
|
|
35929
|
+
/**
|
|
35930
|
+
* One `tools/call`, preceded by the `initialize` handshake the transport
|
|
35931
|
+
* requires.
|
|
35932
|
+
*
|
|
35933
|
+
* The handshake is not optional and not cacheable here: a server may hand out a
|
|
35934
|
+
* session id, and this holds no session state between calls. Two round trips
|
|
35935
|
+
* for one answer is the price of not keeping a connection open for a feature
|
|
35936
|
+
* that fires when somebody pastes something.
|
|
35937
|
+
*/
|
|
35938
|
+
const callMcpTool = Effect.fn("McpToolClient.callMcpTool")(function* (input) {
|
|
35939
|
+
const http = yield* HttpClient$1.HttpClient;
|
|
35940
|
+
const post = (body, extraHeaders) => http.execute(HttpClientRequest$1.bodyJsonUnsafe(HttpClientRequest$1.setHeaders(HttpClientRequest$1.post(input.url), {
|
|
35941
|
+
...input.headers,
|
|
35942
|
+
...extraHeaders,
|
|
35943
|
+
accept: "application/json, text/event-stream"
|
|
35944
|
+
}), body)).pipe(Effect.timeout(REQUEST_TIMEOUT_MS), Effect.catchCause((cause) => Effect.fail(new McpToolCallError({
|
|
35945
|
+
detail: `Could not reach ${input.url}: ${String(cause)}`,
|
|
35946
|
+
status: null
|
|
35947
|
+
}))));
|
|
35948
|
+
const initialize = yield* post({
|
|
35949
|
+
jsonrpc: "2.0",
|
|
35950
|
+
id: 1,
|
|
35951
|
+
method: "initialize",
|
|
35952
|
+
params: {
|
|
35953
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
35954
|
+
capabilities: {},
|
|
35955
|
+
clientInfo: {
|
|
35956
|
+
name: "p4code",
|
|
35957
|
+
version: "0.0.0"
|
|
35958
|
+
}
|
|
35959
|
+
}
|
|
35960
|
+
}, {});
|
|
35961
|
+
if (initialize.status >= 400) return yield* new McpToolCallError({
|
|
35962
|
+
detail: unauthorized$1(initialize.status) ? "The server rejected p4code's credentials." : `The server answered ${initialize.status} to initialize.`,
|
|
35963
|
+
status: initialize.status
|
|
35964
|
+
});
|
|
35965
|
+
const sessionId = initialize.headers["mcp-session-id"];
|
|
35966
|
+
const sessionHeaders = {
|
|
35967
|
+
"mcp-protocol-version": MCP_PROTOCOL_VERSION,
|
|
35968
|
+
...sessionId === void 0 ? {} : { "mcp-session-id": sessionId }
|
|
35969
|
+
};
|
|
35970
|
+
yield* post({
|
|
35971
|
+
jsonrpc: "2.0",
|
|
35972
|
+
method: "notifications/initialized"
|
|
35973
|
+
}, sessionHeaders).pipe(Effect.ignore);
|
|
35974
|
+
const response = yield* post({
|
|
35975
|
+
jsonrpc: "2.0",
|
|
35976
|
+
id: 2,
|
|
35977
|
+
method: "tools/call",
|
|
35978
|
+
params: {
|
|
35979
|
+
name: input.toolName,
|
|
35980
|
+
arguments: input.arguments
|
|
35981
|
+
}
|
|
35982
|
+
}, sessionHeaders);
|
|
35983
|
+
const body = yield* response.text.pipe(Effect.catchCause(() => Effect.succeed("")));
|
|
35984
|
+
if (response.status >= 400) return yield* new McpToolCallError({
|
|
35985
|
+
detail: unauthorized$1(response.status) ? "The server rejected p4code's credentials." : `The server answered ${response.status} to ${input.toolName}.`,
|
|
35986
|
+
status: response.status
|
|
35987
|
+
});
|
|
35988
|
+
const envelope = readEnvelope(body);
|
|
35989
|
+
if (envelope === void 0) return yield* new McpToolCallError({
|
|
35990
|
+
detail: `The server's answer to ${input.toolName} was not JSON-RPC.`,
|
|
35991
|
+
status: response.status
|
|
35992
|
+
});
|
|
35993
|
+
const error = envelope["error"];
|
|
35994
|
+
if (typeof error === "object" && error !== null) {
|
|
35995
|
+
const detail = error["message"];
|
|
35996
|
+
return yield* new McpToolCallError({
|
|
35997
|
+
detail: typeof detail === "string" ? detail : `${input.toolName} failed.`,
|
|
35998
|
+
status: response.status
|
|
35999
|
+
});
|
|
36000
|
+
}
|
|
36001
|
+
const result = envelope["result"];
|
|
36002
|
+
if (typeof result !== "object" || result === null) return yield* new McpToolCallError({
|
|
36003
|
+
detail: `${input.toolName} returned no result.`,
|
|
36004
|
+
status: response.status
|
|
36005
|
+
});
|
|
36006
|
+
const resultRecord = result;
|
|
36007
|
+
const text = textOf(resultRecord["content"]);
|
|
36008
|
+
if (resultRecord["isError"] === true) return yield* new McpToolCallError({
|
|
36009
|
+
detail: text.length > 0 ? text : `${input.toolName} reported an error.`,
|
|
36010
|
+
status: response.status
|
|
36011
|
+
});
|
|
36012
|
+
return {
|
|
36013
|
+
structuredContent: resultRecord["structuredContent"],
|
|
36014
|
+
text
|
|
36015
|
+
};
|
|
36016
|
+
});
|
|
36017
|
+
//#endregion
|
|
36018
|
+
//#region src/mcp/TicketResolver.ts
|
|
36019
|
+
/**
|
|
36020
|
+
* The registered server name a Linear ticket is resolved through.
|
|
36021
|
+
*
|
|
36022
|
+
* Matched by name rather than by URL: the name is what the user typed in
|
|
36023
|
+
* Settings and what every other surface calls it, and a workspace may reach
|
|
36024
|
+
* Linear through a proxy URL that no pattern here would recognize.
|
|
36025
|
+
*/
|
|
36026
|
+
const LINEAR_SERVER_NAME = "linear";
|
|
36027
|
+
/** Linear's own tool for reading one issue. */
|
|
36028
|
+
const LINEAR_GET_ISSUE_TOOL = "get_issue";
|
|
36029
|
+
var TicketResolver = class extends Context.Service()("@p4code/cli/mcp/TicketResolver") {};
|
|
36030
|
+
const stringField$1 = (record, key) => {
|
|
36031
|
+
const value = record[key];
|
|
36032
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
36033
|
+
};
|
|
36034
|
+
const asRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
36035
|
+
/**
|
|
36036
|
+
* Find the issue inside whatever the tool returned.
|
|
36037
|
+
*
|
|
36038
|
+
* MCP leaves the shape of a tool result to the tool, so this accepts the three
|
|
36039
|
+
* arrangements in the wild - the structured payload itself, that payload with
|
|
36040
|
+
* the issue nested one level down, or a text block that happens to be JSON -
|
|
36041
|
+
* rather than assuming the one Linear happens to send today.
|
|
36042
|
+
*/
|
|
36043
|
+
const readIssue = (result) => {
|
|
36044
|
+
const structured = asRecord$1(result.structuredContent);
|
|
36045
|
+
if (structured !== void 0) {
|
|
36046
|
+
const nested = asRecord$1(structured["issue"]);
|
|
36047
|
+
if (nested !== void 0) return nested;
|
|
36048
|
+
if (stringField$1(structured, "url") !== void 0) return structured;
|
|
36049
|
+
}
|
|
36050
|
+
try {
|
|
36051
|
+
const parsed = JSON.parse(result.text);
|
|
36052
|
+
const record = asRecord$1(parsed);
|
|
36053
|
+
if (record === void 0) return structured;
|
|
36054
|
+
return asRecord$1(record["issue"]) ?? record;
|
|
36055
|
+
} catch {
|
|
36056
|
+
return structured;
|
|
36057
|
+
}
|
|
36058
|
+
};
|
|
36059
|
+
const make$36 = Effect.gen(function* () {
|
|
36060
|
+
const registry = yield* McpRegistry;
|
|
36061
|
+
const oauth = yield* McpOAuth;
|
|
36062
|
+
const http = yield* HttpClient$1.HttpClient;
|
|
36063
|
+
return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
|
|
36064
|
+
const identifier = parseTicketReference(reference);
|
|
36065
|
+
if (identifier === null) return yield* new TicketResolveError({
|
|
36066
|
+
reason: "not_found",
|
|
36067
|
+
detail: `"${reference}" does not look like a ticket reference.`
|
|
36068
|
+
});
|
|
36069
|
+
const server = (yield* registry.list).find((candidate) => candidate.registration.name.toLowerCase() === LINEAR_SERVER_NAME && candidate.registration.enabled);
|
|
36070
|
+
if (server === void 0 || server.registration.transport === "stdio") return yield* new TicketResolveError({
|
|
36071
|
+
reason: "not_configured",
|
|
36072
|
+
detail: "No Linear MCP server is registered on this machine. Add it in Settings to link tickets."
|
|
36073
|
+
});
|
|
36074
|
+
const registration = server.registration;
|
|
36075
|
+
const token = yield* oauth.accessTokenFor(registration);
|
|
36076
|
+
const headers = {
|
|
36077
|
+
...registration.headers,
|
|
36078
|
+
...Option.isSome(token) ? { authorization: `Bearer ${token.value}` } : {}
|
|
36079
|
+
};
|
|
36080
|
+
if (Option.isNone(token) && Object.keys(registration.headers ?? {}).length === 0) return yield* new TicketResolveError({
|
|
36081
|
+
reason: "not_authorized",
|
|
36082
|
+
detail: "Linear is registered but not signed in on this machine. Sign in from Settings."
|
|
36083
|
+
});
|
|
36084
|
+
const result = yield* callMcpTool({
|
|
36085
|
+
url: registration.url,
|
|
36086
|
+
headers,
|
|
36087
|
+
toolName: LINEAR_GET_ISSUE_TOOL,
|
|
36088
|
+
arguments: { id: identifier }
|
|
36089
|
+
}).pipe(Effect.provideService(HttpClient$1.HttpClient, http), Effect.mapError((error) => error.status === 401 || error.status === 403 ? new TicketResolveError({
|
|
36090
|
+
reason: "not_authorized",
|
|
36091
|
+
detail: "Linear rejected p4code's sign-in. Sign in again from Settings."
|
|
36092
|
+
}) : new TicketResolveError({
|
|
36093
|
+
reason: "unavailable",
|
|
36094
|
+
detail: error.detail
|
|
36095
|
+
})));
|
|
36096
|
+
const issue = readIssue(result);
|
|
36097
|
+
const url = issue === void 0 ? void 0 : stringField$1(issue, "url");
|
|
36098
|
+
const title = issue === void 0 ? void 0 : stringField$1(issue, "title");
|
|
36099
|
+
if (issue === void 0 || url === void 0 || title === void 0) return yield* new TicketResolveError({
|
|
36100
|
+
reason: "not_found",
|
|
36101
|
+
detail: `Linear returned nothing usable for ${identifier}.`
|
|
36102
|
+
});
|
|
36103
|
+
return {
|
|
36104
|
+
externalRef: {
|
|
36105
|
+
source: "linear",
|
|
36106
|
+
identifier: stringField$1(issue, "identifier") ?? identifier,
|
|
36107
|
+
url
|
|
36108
|
+
},
|
|
36109
|
+
title
|
|
36110
|
+
};
|
|
36111
|
+
}) };
|
|
36112
|
+
});
|
|
36113
|
+
const layer$31 = Layer.effect(TicketResolver, make$36);
|
|
35103
36114
|
//#endregion
|
|
35104
36115
|
//#region ../../packages/shared/src/KeyedCoalescingWorker.ts
|
|
35105
36116
|
const makeKeyedCoalescingWorker = (options) => Effect.gen(function* () {
|
|
@@ -37189,7 +38200,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
|
|
|
37189
38200
|
subscribeMetadata
|
|
37190
38201
|
});
|
|
37191
38202
|
});
|
|
37192
|
-
const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$
|
|
38203
|
+
const layer$29 = Layer.effect(TerminalManager, make$34()).pipe(Layer.provide(layer$54));
|
|
37193
38204
|
//#endregion
|
|
37194
38205
|
//#region src/mcp/McpInvocationContext.ts
|
|
37195
38206
|
var McpInvocationContext = class extends Context.Service()("@p4code/cli/mcp/McpInvocationContext") {};
|
|
@@ -44269,6 +45280,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
44269
45280
|
[WS_METHODS.tasksUpdate, AuthOrchestrationOperateScope],
|
|
44270
45281
|
[WS_METHODS.tasksDelete, AuthOrchestrationOperateScope],
|
|
44271
45282
|
[WS_METHODS.tasksStartThread, AuthOrchestrationOperateScope],
|
|
45283
|
+
[WS_METHODS.ticketsResolve, AuthOrchestrationReadScope],
|
|
44272
45284
|
[WS_METHODS.subscribeTasks, AuthOrchestrationReadScope],
|
|
44273
45285
|
[WS_METHODS.hubGetSyncStatus, AuthOrchestrationReadScope],
|
|
44274
45286
|
[WS_METHODS.hubConnect, AuthOrchestrationOperateScope],
|
|
@@ -44423,6 +45435,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
44423
45435
|
const mcpOAuth = yield* McpOAuth;
|
|
44424
45436
|
const claudeMcpFiles = yield* ClaudeMcpFiles;
|
|
44425
45437
|
const skillRegistry = yield* SkillRegistry;
|
|
45438
|
+
const ticketResolver = yield* TicketResolver;
|
|
44426
45439
|
const listMcpServersEverywhere = Effect.gen(function* () {
|
|
44427
45440
|
const servers = [...yield* mcpRegistry.list];
|
|
44428
45441
|
const projectRows = yield* projectionProjects.listAll().pipe(Effect.map((rows) => rows.filter((row) => row.deletedAt === null)), Effect.orElseSucceed(() => []));
|
|
@@ -44969,6 +45982,13 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
44969
45982
|
threadId
|
|
44970
45983
|
};
|
|
44971
45984
|
}), { "rpc.aggregate": "tasks" }),
|
|
45985
|
+
/**
|
|
45986
|
+
* What a pasted reference is, straight from the tracker.
|
|
45987
|
+
*
|
|
45988
|
+
* Nothing is written here: the client decides whether the answer
|
|
45989
|
+
* becomes a task, so an accidental paste costs one request and no row.
|
|
45990
|
+
*/
|
|
45991
|
+
[WS_METHODS.ticketsResolve]: ({ reference }) => observeRpcEffect$1(WS_METHODS.ticketsResolve, ticketResolver.resolve(reference), { "rpc.aggregate": "tickets" }),
|
|
44972
45992
|
[WS_METHODS.subscribeTasks]: (_input) => observeRpcStreamEffect$1(WS_METHODS.subscribeTasks, Effect.succeed(taskRepository.streamWithSnapshot.pipe(Stream.catch((cause) => Stream.fromEffect(toTaskStoreError("tasks.subscribe")(cause))))), { "rpc.aggregate": "tasks" }),
|
|
44973
45993
|
[WS_METHODS.hubGetSyncStatus]: (_input) => observeRpcEffect$1(WS_METHODS.hubGetSyncStatus, assetSync.status, { "rpc.aggregate": "hub" }),
|
|
44974
45994
|
[WS_METHODS.hubConnect]: (input) => observeRpcEffect$1(WS_METHODS.hubConnect, hubLink.connect(input).pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause.message })), Effect.andThen(assetSync.status)), { "rpc.aggregate": "hub" }),
|
|
@@ -45236,7 +46256,7 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
|
|
|
45236
46256
|
const serverAuth = yield* EnvironmentAuth;
|
|
45237
46257
|
const sessions = yield* SessionStore;
|
|
45238
46258
|
const session = yield* serverAuth.authenticateWebSocketUpgrade(request).pipe(Effect.catchIf(isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid(serverAuthCredentialReason(error))), Effect.catchIf(isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error)));
|
|
45239
|
-
const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$
|
|
46259
|
+
const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$34), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(TaskRepository, taskRepository)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(Layer.succeed(P4ProjectFileLoader, p4ProjectFileLoader)), 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$39), Layer.provide(layer$37.pipe(Layer.provide(layer$38))))), Layer.provide(layer$40))))));
|
|
45240
46260
|
return yield* Effect.acquireUseRelease(sessions.markConnected(session.sessionId), () => rpcWebSocketHttpEffect, () => sessions.markDisconnected(session.sessionId));
|
|
45241
46261
|
}).pipe(Effect.catchTags({
|
|
45242
46262
|
EnvironmentAuthInvalidError: HttpServerRespondable.toResponse,
|
|
@@ -47560,7 +48580,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
|
|
|
47560
48580
|
};
|
|
47561
48581
|
});
|
|
47562
48582
|
var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
|
|
47563
|
-
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$
|
|
48583
|
+
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$72));
|
|
47564
48584
|
//#endregion
|
|
47565
48585
|
//#region src/textGeneration/TextGenerationUtils.ts
|
|
47566
48586
|
const isTextGenerationError$3 = Schema$1.is(TextGenerationError);
|
|
@@ -49044,88 +50064,6 @@ function formatAskUserQuestionAnswers(answers) {
|
|
|
49044
50064
|
return formatted;
|
|
49045
50065
|
}
|
|
49046
50066
|
//#endregion
|
|
49047
|
-
//#region src/provider/CompressPrompts.ts
|
|
49048
|
-
/**
|
|
49049
|
-
* Caveman-style response compression rulesets, adapted from the caveman
|
|
49050
|
-
* plugin's SKILL.md. Pre-baked per level so injection is a lookup, not a
|
|
49051
|
-
* runtime template. Applies to assistant prose only: code blocks, commits,
|
|
49052
|
-
* PRs, error strings, and safety-critical text stay uncompressed.
|
|
49053
|
-
*/
|
|
49054
|
-
const COMPRESS_SHARED_RULES = `Respond terse. All technical substance stays. Only fluff dies.
|
|
49055
|
-
|
|
49056
|
-
## Persistence
|
|
49057
|
-
|
|
49058
|
-
Active every response. No revert after many turns. No filler drift. Still active if unsure.
|
|
49059
|
-
|
|
49060
|
-
## Rules
|
|
49061
|
-
|
|
49062
|
-
Drop filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Short synonyms (big not extensive, fix not "implement a solution for"). No tool-call narration, no decorative tables/emoji, no dumping long raw error logs unless asked - quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations (cfg/impl/req/res/fn) - tokenizers split them same as the full word: zero tokens saved, reader still decodes. No causal arrows (->) in prose. Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
|
49063
|
-
|
|
49064
|
-
Preserve the user's dominant language: compress the style, not the language. Always keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless the user explicitly asks for translation.
|
|
49065
|
-
|
|
49066
|
-
No self-reference. Never name or announce the style. No compressed answer plus normal recap - compressed output only. Exception: user explicitly asks what the mode is.
|
|
49067
|
-
|
|
49068
|
-
## Auto-Clarity
|
|
49069
|
-
|
|
49070
|
-
Drop compression and write normally when:
|
|
49071
|
-
- Security warnings
|
|
49072
|
-
- Irreversible action confirmations
|
|
49073
|
-
- Multi-step sequences where fragment order or omitted conjunctions risk misread
|
|
49074
|
-
- Compression itself creates technical ambiguity
|
|
49075
|
-
- User asks to clarify or repeats a question
|
|
49076
|
-
|
|
49077
|
-
Resume compression after the clear part is done.
|
|
49078
|
-
|
|
49079
|
-
## Boundaries
|
|
49080
|
-
|
|
49081
|
-
Code, commit messages, and PR descriptions: write normal, uncompressed.`;
|
|
49082
|
-
const COMPRESS_RULESETS = {
|
|
49083
|
-
lite: `## Response compression: lite
|
|
49084
|
-
|
|
49085
|
-
${COMPRESS_SHARED_RULES}
|
|
49086
|
-
|
|
49087
|
-
## Intensity: lite
|
|
49088
|
-
|
|
49089
|
-
No filler or hedging. Keep articles and full sentences. Professional but tight.
|
|
49090
|
-
|
|
49091
|
-
Example - "Why does my React component re-render?"
|
|
49092
|
-
"Your component re-renders because you create a new object reference each render. Wrap it in \`useMemo\`."`,
|
|
49093
|
-
full: `## Response compression: full
|
|
49094
|
-
|
|
49095
|
-
${COMPRESS_SHARED_RULES}
|
|
49096
|
-
|
|
49097
|
-
## Intensity: full
|
|
49098
|
-
|
|
49099
|
-
Drop articles (a/an/the), fragments OK, short synonyms. Pattern: [thing] [action] [reason]. [next step].
|
|
49100
|
-
|
|
49101
|
-
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
|
49102
|
-
Yes: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"
|
|
49103
|
-
|
|
49104
|
-
Example - "Why does my React component re-render?"
|
|
49105
|
-
"New object ref each render. Inline object prop = new ref = re-render. Wrap in \`useMemo\`."`,
|
|
49106
|
-
ultra: `## Response compression: ultra
|
|
49107
|
-
|
|
49108
|
-
${COMPRESS_SHARED_RULES}
|
|
49109
|
-
|
|
49110
|
-
## Intensity: ultra
|
|
49111
|
-
|
|
49112
|
-
Drop articles, fragments OK, short synonyms. Also strip conjunctions when cause-then-effect stays unambiguous. One word when one word is enough. State each fact once. Code symbols, function names, API names, error strings: never touch.
|
|
49113
|
-
|
|
49114
|
-
Example - "Why does my React component re-render?"
|
|
49115
|
-
"Inline object prop, new ref, re-render. \`useMemo\`."`
|
|
49116
|
-
};
|
|
49117
|
-
const COMPRESS_TURN_REMINDERS = {
|
|
49118
|
-
lite: "[response style: compressed lite - no filler/hedging, full sentences; code/commits/security text normal]",
|
|
49119
|
-
full: "[response style: compressed full - terse fragments, drop articles/filler; code/commits/security text normal]",
|
|
49120
|
-
ultra: "[response style: compressed ultra - maximum terseness, one word when enough; code/commits/security text normal]"
|
|
49121
|
-
};
|
|
49122
|
-
function compressRulesetFor(mode) {
|
|
49123
|
-
return mode === "off" ? void 0 : COMPRESS_RULESETS[mode];
|
|
49124
|
-
}
|
|
49125
|
-
function compressTurnReminderFor(mode) {
|
|
49126
|
-
return mode === "off" ? void 0 : COMPRESS_TURN_REMINDERS[mode];
|
|
49127
|
-
}
|
|
49128
|
-
//#endregion
|
|
49129
50067
|
//#region src/provider/Layers/ClaudeAdapter.ts
|
|
49130
50068
|
/**
|
|
49131
50069
|
* ClaudeAdapterLive - Scoped live implementation for the Claude Agent provider adapter.
|
|
@@ -49687,6 +50625,20 @@ const SUPPORTED_CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
49687
50625
|
/** Emitted by the CLI but absent from the SDK's `SDKMessage` union. */
|
|
49688
50626
|
const UNTYPED_COMMAND_LIFECYCLE_MESSAGE = "command_lifecycle";
|
|
49689
50627
|
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.";
|
|
50628
|
+
/**
|
|
50629
|
+
* Said whichever way the setting is set, and said as an override.
|
|
50630
|
+
*
|
|
50631
|
+
* The preset system prompt this adapter requests already carries an
|
|
50632
|
+
* instruction not to spawn subagents unless the user asked — and a preset is
|
|
50633
|
+
* taken wholesale, so there is no version of it with that line removed. An
|
|
50634
|
+
* append that merely stayed silent when subagents are allowed would therefore
|
|
50635
|
+
* leave the restriction standing, which is the one outcome the setting exists
|
|
50636
|
+
* to change. Both texts name the earlier instruction they replace, because a
|
|
50637
|
+
* later instruction that does not acknowledge the conflict reads as an
|
|
50638
|
+
* accident rather than a decision.
|
|
50639
|
+
*/
|
|
50640
|
+
const SUBAGENTS_ALLOWED_PROMPT = "You may spawn subagents with the Task tool whenever it helps, without asking the user first. This overrides any earlier instruction to use subagents only when they are requested. Prefer one for bounded search or review work whose result is much smaller than the reading it takes to produce; do the work inline when the answer is a single file or a single edit.";
|
|
50641
|
+
const SUBAGENTS_ON_REQUEST_PROMPT = "Do not spawn subagents with the Task tool unless the user asks for one. This overrides any earlier instruction permitting them on your own initiative. Do the work inline instead, and say so if it would have been better delegated.";
|
|
49690
50642
|
const CLAUDE_SETTING_SOURCES = [
|
|
49691
50643
|
"user",
|
|
49692
50644
|
"project",
|
|
@@ -49915,6 +50867,25 @@ function sdkNativeMethod(message) {
|
|
|
49915
50867
|
* would only duplicate a refresh that has already happened.
|
|
49916
50868
|
*/
|
|
49917
50869
|
const CONSUMED_UNDECLARED_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set(["background_tasks_changed", "vcs_state_changed"]);
|
|
50870
|
+
/**
|
|
50871
|
+
* Another undeclared-but-real subtype, emitted once the CLI has pushed the
|
|
50872
|
+
* turn's changes to a hosting provider:
|
|
50873
|
+
* `{provider: "github", url: "https://github.com/org/repo/pull/96",
|
|
50874
|
+
* repo: "org/repo", identifier: "96"}`. Mapped to `code.published` so the
|
|
50875
|
+
* work log links the pull request instead of logging an unknown-subtype
|
|
50876
|
+
* warning.
|
|
50877
|
+
*/
|
|
50878
|
+
const CODE_CHANGE_PUBLISHED_SUBTYPE = "code_change_published";
|
|
50879
|
+
function codePublishedPayloadFromSdkMessage(message) {
|
|
50880
|
+
const record = message;
|
|
50881
|
+
if (typeof record.url !== "string" || record.url.length === 0) return;
|
|
50882
|
+
return {
|
|
50883
|
+
hostingProvider: typeof record.provider === "string" && record.provider.length > 0 ? record.provider : "unknown",
|
|
50884
|
+
url: record.url,
|
|
50885
|
+
...typeof record.repo === "string" && record.repo.length > 0 ? { repo: record.repo } : {},
|
|
50886
|
+
...typeof record.identifier === "string" && record.identifier.length > 0 ? { identifier: record.identifier } : {}
|
|
50887
|
+
};
|
|
50888
|
+
}
|
|
49918
50889
|
const SDK_MESSAGE_NOISE_KEYS = /* @__PURE__ */ new Set([
|
|
49919
50890
|
"type",
|
|
49920
50891
|
"subtype",
|
|
@@ -50886,6 +51857,17 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50886
51857
|
}
|
|
50887
51858
|
};
|
|
50888
51859
|
if (CONSUMED_UNDECLARED_SYSTEM_SUBTYPES.has(message.subtype)) return;
|
|
51860
|
+
if (message.subtype === CODE_CHANGE_PUBLISHED_SUBTYPE) {
|
|
51861
|
+
const published = codePublishedPayloadFromSdkMessage(message);
|
|
51862
|
+
if (published) {
|
|
51863
|
+
yield* offerRuntimeEvent({
|
|
51864
|
+
...base,
|
|
51865
|
+
type: "code.published",
|
|
51866
|
+
payload: published
|
|
51867
|
+
});
|
|
51868
|
+
return;
|
|
51869
|
+
}
|
|
51870
|
+
}
|
|
50889
51871
|
switch (message.subtype) {
|
|
50890
51872
|
case "init":
|
|
50891
51873
|
yield* offerRuntimeEvent({
|
|
@@ -51520,8 +52502,13 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51520
52502
|
const mcpSession = readMcpProviderSession(input.threadId);
|
|
51521
52503
|
const externalMcpServers = options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers;
|
|
51522
52504
|
const narrateBeforeTools = options?.resolveToolCallNarration === void 0 ? DEFAULT_SERVER_SETTINGS.enableToolCallNarration : yield* options.resolveToolCallNarration;
|
|
52505
|
+
const unpromptedSubagents = options?.resolveUnpromptedSubagents === void 0 ? DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents : yield* options.resolveUnpromptedSubagents;
|
|
51523
52506
|
const compressRuleset = compressRulesetFor(input.compressMode ?? "off");
|
|
51524
|
-
const systemPromptAppend = [
|
|
52507
|
+
const systemPromptAppend = [
|
|
52508
|
+
...narrateBeforeTools ? [NARRATE_BEFORE_TOOLS_PROMPT] : [],
|
|
52509
|
+
unpromptedSubagents ? SUBAGENTS_ALLOWED_PROMPT : SUBAGENTS_ON_REQUEST_PROMPT,
|
|
52510
|
+
...compressRuleset !== void 0 ? [compressRuleset] : []
|
|
52511
|
+
].join("\n\n");
|
|
51525
52512
|
const queryOptions = {
|
|
51526
52513
|
...input.cwd ? { cwd: input.cwd } : {},
|
|
51527
52514
|
...apiModelId ? { model: apiModelId } : {},
|
|
@@ -51983,12 +52970,14 @@ const ClaudeDriver = {
|
|
|
51983
52970
|
const mcpRegistry = yield* McpRegistry;
|
|
51984
52971
|
const resolveDisabledSkills = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => []));
|
|
51985
52972
|
const resolveToolCallNarration = serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableToolCallNarration), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableToolCallNarration));
|
|
52973
|
+
const resolveUnpromptedSubagents = serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableUnpromptedSubagents), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents));
|
|
51986
52974
|
const adapter = yield* makeClaudeAdapter(effectiveConfig, {
|
|
51987
52975
|
instanceId,
|
|
51988
52976
|
environment: processEnv,
|
|
51989
52977
|
resolveMcpServers: mcpRegistry.resolveForSession,
|
|
51990
52978
|
resolveDisabledSkills,
|
|
51991
52979
|
resolveToolCallNarration,
|
|
52980
|
+
resolveUnpromptedSubagents,
|
|
51992
52981
|
...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
|
|
51993
52982
|
});
|
|
51994
52983
|
const textGeneration = yield* makeClaudeTextGeneration(effectiveConfig, processEnv);
|
|
@@ -85633,6 +86622,24 @@ function runtimeEventToActivities(event, taskTitle) {
|
|
|
85633
86622
|
turnId: toTurnId$1(event.turnId) ?? null,
|
|
85634
86623
|
...maybeSequence
|
|
85635
86624
|
}];
|
|
86625
|
+
case "code.published": {
|
|
86626
|
+
const label = event.payload.repo ? event.payload.identifier ? `${event.payload.repo}#${event.payload.identifier}` : event.payload.repo : event.payload.url;
|
|
86627
|
+
return [{
|
|
86628
|
+
id: event.eventId,
|
|
86629
|
+
createdAt: event.createdAt,
|
|
86630
|
+
tone: "info",
|
|
86631
|
+
kind: "code.published",
|
|
86632
|
+
summary: `Published ${label}`,
|
|
86633
|
+
payload: {
|
|
86634
|
+
hostingProvider: event.payload.hostingProvider,
|
|
86635
|
+
url: event.payload.url,
|
|
86636
|
+
...event.payload.repo ? { repo: event.payload.repo } : {},
|
|
86637
|
+
...event.payload.identifier ? { identifier: event.payload.identifier } : {}
|
|
86638
|
+
},
|
|
86639
|
+
turnId: toTurnId$1(event.turnId) ?? null,
|
|
86640
|
+
...maybeSequence
|
|
86641
|
+
}];
|
|
86642
|
+
}
|
|
85636
86643
|
case "turn.plan.updated": return [{
|
|
85637
86644
|
id: event.eventId,
|
|
85638
86645
|
createdAt: event.createdAt,
|
|
@@ -88017,7 +89024,7 @@ const ObservabilityLive = Layer.unwrap(Effect.gen(function* () {
|
|
|
88017
89024
|
sink,
|
|
88018
89025
|
...delegate ? { delegate } : {}
|
|
88019
89026
|
});
|
|
88020
|
-
return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$
|
|
89027
|
+
return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$41(sink));
|
|
88021
89028
|
})).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(otlpSerializationLayer));
|
|
88022
89029
|
const metricsLayer = config.otlpMetricsUrl === void 0 ? Layer.empty : OtlpMetrics.layer({
|
|
88023
89030
|
url: config.otlpMetricsUrl,
|
|
@@ -88296,25 +89303,25 @@ const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReacto
|
|
|
88296
89303
|
const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$3));
|
|
88297
89304
|
const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
|
|
88298
89305
|
const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));
|
|
88299
|
-
const VcsDriverRegistryLayerLive = layer$
|
|
88300
|
-
const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$
|
|
88301
|
-
const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$
|
|
88302
|
-
const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$
|
|
89306
|
+
const VcsDriverRegistryLayerLive = layer$37.pipe(Layer.provide(layer$38));
|
|
89307
|
+
const SourceControlProviderRegistryLayerLive = layer$14.pipe(Layer.provide(Layer.mergeAll(layer$22, layer$20, layer$18, layer$16)), Layer.provideMerge(layer$39), Layer.provideMerge(VcsDriverRegistryLayerLive));
|
|
89308
|
+
const GitManagerLayerLive = layer$13.pipe(Layer.provideMerge(layer$23), Layer.provideMerge(layer$39), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$33));
|
|
89309
|
+
const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$39));
|
|
88303
89310
|
const GitWorkflowLayerLive = layer$12.pipe(Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(GitLayerLive));
|
|
88304
|
-
const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$
|
|
88305
|
-
const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$
|
|
88306
|
-
const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$
|
|
88307
|
-
const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$
|
|
88308
|
-
const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$
|
|
89311
|
+
const SourceControlRepositoryServiceLayerLive = layer$4.pipe(Layer.provideMerge(layer$39), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
|
|
89312
|
+
const ReviewLayerLive = layer$9.pipe(Layer.provideMerge(layer$39), Layer.provideMerge(VcsDriverRegistryLayerLive));
|
|
89313
|
+
const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$38), 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))));
|
|
89314
|
+
const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$35), Layer.provideMerge(layer$36.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
|
|
89315
|
+
const PortScannerLayerLive = layer$30.pipe(Layer.provide(layer$54));
|
|
88309
89316
|
const TerminalLayerLive = layer$29.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
|
|
88310
89317
|
const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(PortScannerLayerLive));
|
|
88311
|
-
const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$
|
|
88312
|
-
const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$
|
|
88313
|
-
const WorkspaceLayerLive = Layer.mergeAll(layer$
|
|
88314
|
-
const ProjectFaviconResolverLayerLive = layer$
|
|
88315
|
-
const AuthLayerLive = layer$
|
|
89318
|
+
const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$44));
|
|
89319
|
+
const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$44), Layer.provide(WorkspaceEntriesLayerLive));
|
|
89320
|
+
const WorkspaceLayerLive = Layer.mergeAll(layer$44, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
89321
|
+
const ProjectFaviconResolverLayerLive = layer$42.pipe(Layer.provide(layer$44), Layer.provide(layer$43));
|
|
89322
|
+
const AuthLayerLive = layer$64.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$68));
|
|
88316
89323
|
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
88317
|
-
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$
|
|
89324
|
+
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$52), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$61.pipe(Layer.provide(layer$68))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$53), Layer.provideMerge(layer$46), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$31.pipe(Layer.provideMerge(Layer.mergeAll(layer$58, layer$32).pipe(Layer.provideMerge(layer$59), Layer.provideMerge(layer$60))))), Layer.provideMerge(layer$68)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$49), Layer.provideMerge(layer$51), Layer.provideMerge(layer$50), Layer.provide(layer$72));
|
|
88318
89325
|
/**
|
|
88319
89326
|
* Hub asset sync.
|
|
88320
89327
|
*
|
|
@@ -88322,9 +89329,9 @@ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(Checkpo
|
|
|
88322
89329
|
* not-configured shape, and every call reports that rather than failing — so
|
|
88323
89330
|
* this costs a server with no hub nothing but a layer.
|
|
88324
89331
|
*/
|
|
88325
|
-
const AssetSyncLive = layer$
|
|
88326
|
-
const RuntimeServicesLive = layer$
|
|
88327
|
-
const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, 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$
|
|
89332
|
+
const AssetSyncLive = layer$55.pipe(Layer.provideMerge(layer$56), Layer.provideMerge(layer$57));
|
|
89333
|
+
const RuntimeServicesLive = layer$45.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
|
|
89334
|
+
const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, 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$43), Layer.provide(layer$28), Layer.provide(layer$47), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
|
|
88328
89335
|
const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
|
|
88329
89336
|
const config = yield* ServerConfig$1;
|
|
88330
89337
|
yield* fixPath();
|
|
@@ -88367,7 +89374,7 @@ const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
|
|
|
88367
89374
|
cause,
|
|
88368
89375
|
servePort: configured.servePort
|
|
88369
89376
|
}))) : Effect.void)) : Layer.empty;
|
|
88370
|
-
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$
|
|
89377
|
+
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$40), Layer.provideMerge(PlatformServicesLive));
|
|
88371
89378
|
}));
|
|
88372
89379
|
const runServer = Layer.launch(makeServerLayer);
|
|
88373
89380
|
//#endregion
|
|
@@ -88529,11 +89536,11 @@ const reconcileWithNewBinary = Effect.fn("cli.selfUpdate.reconcileWithNewBinary"
|
|
|
88529
89536
|
//#endregion
|
|
88530
89537
|
//#region src/cli/service.ts
|
|
88531
89538
|
const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
|
|
88532
|
-
const bootServiceLayer = (config) => layer$
|
|
89539
|
+
const bootServiceLayer = (config) => layer$48({
|
|
88533
89540
|
baseDir: config.baseDir,
|
|
88534
89541
|
logsDir: config.logsDir,
|
|
88535
89542
|
cliVersion: version
|
|
88536
|
-
}).pipe(Layer.provide(layer$
|
|
89543
|
+
}).pipe(Layer.provide(layer$54));
|
|
88537
89544
|
/** Install, update, or repair the service using the CLI version running this command. */
|
|
88538
89545
|
const reconcileService = Effect.fn("cli.service.reconcile")(function* () {
|
|
88539
89546
|
const service = yield* BootService;
|
|
@@ -88580,7 +89587,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* (flags, run, pr
|
|
|
88580
89587
|
const baseDirNotice = yield* describeNonDefaultBaseDir(config.baseDir);
|
|
88581
89588
|
if (baseDirNotice !== null) yield* Console.error(baseDirNotice);
|
|
88582
89589
|
if (prepare !== void 0) yield* prepare(config);
|
|
88583
|
-
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$
|
|
89590
|
+
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$54)));
|
|
88584
89591
|
});
|
|
88585
89592
|
/**
|
|
88586
89593
|
* Options the installed unit carries, rather than ones this command consumes.
|
|
@@ -88734,7 +89741,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
|
|
|
88734
89741
|
]));
|
|
88735
89742
|
//#endregion
|
|
88736
89743
|
//#region src/bin.ts
|
|
88737
|
-
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$
|
|
89744
|
+
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$72, FetchHttpClient.layer);
|
|
88738
89745
|
/**
|
|
88739
89746
|
* Prints an update reminder before dispatching, at most once a day.
|
|
88740
89747
|
*
|
|
@@ -88764,6 +89771,7 @@ const reportUpdateAvailable = Effect.gen(function* () {
|
|
|
88764
89771
|
const makeCli = () => Command.make("p4c", { ...sharedServerCommandFlags }).pipe(Command.withDescription("Run the P4Code server."), Command.withHandler((flags) => runServerCommand(flags)), Command.withSubcommands([
|
|
88765
89772
|
startCommand,
|
|
88766
89773
|
serveCommand,
|
|
89774
|
+
agentsCommand,
|
|
88767
89775
|
authCommand,
|
|
88768
89776
|
hubCommand,
|
|
88769
89777
|
mcpCommand,
|