@p4code/cli 0.4.12 → 0.4.14
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 +904 -192
- package/dist/client/assets/{DiffPanel-Bm0q47ri.js → DiffPanel-MM5YsTnU.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-BHjHHjcL.js → FilePreviewPanel-iQlsalIt.js} +2 -2
- package/dist/client/assets/{PreviewPanel-zj6J-JNa.js → PreviewPanel-Bf3g60N9.js} +2 -2
- package/dist/client/assets/{PullRequestCodeTab-CmKkBGPQ.js → PullRequestCodeTab-CN7XcZJK.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-C0bQJBDi.js → fileCommentAnnotations-84VIRBlS.js} +2 -2
- package/dist/client/assets/{index-BvhnZNuc.js → index-C1TVFsV3.js} +17 -6
- package/dist/client/assets/{renderFileChildren-C0wGDpTp.js → renderFileChildren-C6gKA6Vk.js} +2 -2
- package/dist/client/assets/{toggle-group-RD3apHNe.js → toggle-group-DMQ6zako.js} +2 -2
- package/dist/client/index.html +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -240,7 +240,7 @@ const make$93 = () => {
|
|
|
240
240
|
const layer$82 = Layer.sync(NetService, make$93);
|
|
241
241
|
//#endregion
|
|
242
242
|
//#region package.json
|
|
243
|
-
var version = "0.4.
|
|
243
|
+
var version = "0.4.14";
|
|
244
244
|
//#endregion
|
|
245
245
|
//#region src/config.ts
|
|
246
246
|
/**
|
|
@@ -21599,6 +21599,90 @@ function expandHomePath$3(value) {
|
|
|
21599
21599
|
if (value.startsWith("~/") || value.startsWith("~\\")) return NodePath.join(NodeOS.homedir(), value.slice(2));
|
|
21600
21600
|
return value;
|
|
21601
21601
|
}
|
|
21602
|
+
/**
|
|
21603
|
+
* Reasoning levels `muse exec --reasoning-effort` accepts, in the CLI's own
|
|
21604
|
+
* order (`muse exec --help`, Muse Code 1.1.1). `high` is the CLI default, so
|
|
21605
|
+
* it is the default here too.
|
|
21606
|
+
*/
|
|
21607
|
+
const MUSE_REASONING_EFFORTS = [
|
|
21608
|
+
"none",
|
|
21609
|
+
"minimal",
|
|
21610
|
+
"low",
|
|
21611
|
+
"medium",
|
|
21612
|
+
"high",
|
|
21613
|
+
"xhigh",
|
|
21614
|
+
"max",
|
|
21615
|
+
"ultra"
|
|
21616
|
+
];
|
|
21617
|
+
const MUSE_DEFAULT_REASONING_EFFORT = "high";
|
|
21618
|
+
/**
|
|
21619
|
+
* Keep a selection Muse cannot parse out of the argv. `reasoningEffort` is a
|
|
21620
|
+
* shared option id — Codex publishes one too — so a selection left over from
|
|
21621
|
+
* another provider can reach this driver, and an unknown level makes
|
|
21622
|
+
* `muse exec` reject the whole invocation.
|
|
21623
|
+
*/
|
|
21624
|
+
function museReasoningEffort(value) {
|
|
21625
|
+
return value !== void 0 && MUSE_REASONING_EFFORTS.includes(value) ? value : void 0;
|
|
21626
|
+
}
|
|
21627
|
+
/**
|
|
21628
|
+
* Build the argument vector for one turn.
|
|
21629
|
+
*
|
|
21630
|
+
* Approval policy is the load-bearing decision here. Muse Code 1.1.1 does
|
|
21631
|
+
* express approval as a policy — `--approval-mode untrusted|on-request|never`,
|
|
21632
|
+
* plus named `--permission-profile` documents — but a headless `exec` run
|
|
21633
|
+
* still has no channel to answer a prompt: approvals are served over MSP
|
|
21634
|
+
* (`muse serve`), not on the `exec` stream. So the only two postures `exec`
|
|
21635
|
+
* can honour end to end are `--disable-approval` (keep the OS sandbox) and
|
|
21636
|
+
* `--yolo` (drop approval and the sandbox, and trust the workspace). P4Code
|
|
21637
|
+
* owns the user-facing runtime mode, so it maps onto those two:
|
|
21638
|
+
*
|
|
21639
|
+
* - `full-access` → `--yolo`
|
|
21640
|
+
* - everything else → `--disable-approval --trust-workspace`, leaving the
|
|
21641
|
+
* sandbox to contain what runs.
|
|
21642
|
+
*
|
|
21643
|
+
* `approval-required` cannot be honoured over `exec`; the adapter emits a
|
|
21644
|
+
* `config.warning` for it rather than silently pretending otherwise. Serving
|
|
21645
|
+
* a real approval prompt means moving the driver onto MSP.
|
|
21646
|
+
*
|
|
21647
|
+
* Flags here were verified against Muse Code 1.1.1-R2514.1 (`muse exec
|
|
21648
|
+
* --help`).
|
|
21649
|
+
*/
|
|
21650
|
+
function buildMuseExecArgs(input) {
|
|
21651
|
+
const args = [
|
|
21652
|
+
"exec",
|
|
21653
|
+
"--json",
|
|
21654
|
+
"--session-id",
|
|
21655
|
+
input.sessionId
|
|
21656
|
+
];
|
|
21657
|
+
if (input.model) args.push("--model", input.model);
|
|
21658
|
+
if (input.reasoningEffort) args.push("--reasoning-effort", input.reasoningEffort);
|
|
21659
|
+
if (input.baseUrl) args.push("--base-url", input.baseUrl);
|
|
21660
|
+
if (input.runtimeMode === "full-access") args.push("--yolo");
|
|
21661
|
+
else {
|
|
21662
|
+
args.push("--disable-approval", "--trust-workspace");
|
|
21663
|
+
switch (input.sandboxMode) {
|
|
21664
|
+
case "danger-full-access":
|
|
21665
|
+
args.push("--disable-sandbox");
|
|
21666
|
+
break;
|
|
21667
|
+
case "read-only":
|
|
21668
|
+
args.push("--disable-write", "--disable-shell");
|
|
21669
|
+
break;
|
|
21670
|
+
default: break;
|
|
21671
|
+
}
|
|
21672
|
+
}
|
|
21673
|
+
for (const imagePath of input.imagePaths ?? []) args.push("--image", imagePath);
|
|
21674
|
+
args.push(input.prompt);
|
|
21675
|
+
return args;
|
|
21676
|
+
}
|
|
21677
|
+
/** `muse --version` argv, used by the provider status probe. */
|
|
21678
|
+
const MUSE_VERSION_ARGS = ["--version"];
|
|
21679
|
+
/**
|
|
21680
|
+
* Whether a runtime mode asks for interactive approvals that Muse Code
|
|
21681
|
+
* cannot deliver headlessly.
|
|
21682
|
+
*/
|
|
21683
|
+
function museApprovalsUnsupported(runtimeMode) {
|
|
21684
|
+
return runtimeMode === "approval-required";
|
|
21685
|
+
}
|
|
21602
21686
|
//#endregion
|
|
21603
21687
|
//#region src/provider/skillOverrides.ts
|
|
21604
21688
|
/**
|
|
@@ -21680,19 +21764,7 @@ function buildSkillOverrides(disabled, discovered) {
|
|
|
21680
21764
|
return Object.keys(overrides).length === 0 ? void 0 : overrides;
|
|
21681
21765
|
}
|
|
21682
21766
|
//#endregion
|
|
21683
|
-
//#region src/provider/Drivers/
|
|
21684
|
-
/**
|
|
21685
|
-
* ClaudeSkills — filesystem discovery of Claude Code skills for the `$` picker.
|
|
21686
|
-
*
|
|
21687
|
-
* Claude Code loads skills from `<config dir>/skills` (user scope) and
|
|
21688
|
-
* `<cwd>/.claude/skills` (project scope), one directory per skill with a
|
|
21689
|
-
* `SKILL.md` carrying YAML frontmatter. The Agent SDK init handshake surfaces
|
|
21690
|
-
* skills only as slash commands without their filesystem paths, so the
|
|
21691
|
-
* provider snapshot scans the same locations directly, mirroring how the
|
|
21692
|
-
* Codex app-server reports its skills.
|
|
21693
|
-
*
|
|
21694
|
-
* @module provider/Drivers/ClaudeSkills
|
|
21695
|
-
*/
|
|
21767
|
+
//#region src/provider/Drivers/skillFrontmatter.ts
|
|
21696
21768
|
const FRONTMATTER_PATTERN$1 = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
21697
21769
|
/** The `true` spellings the CLI accepts for a boolean frontmatter field. */
|
|
21698
21770
|
const TRUTHY_FRONTMATTER_VALUES = /* @__PURE__ */ new Set([
|
|
@@ -21727,10 +21799,10 @@ function stripMarkdownFrontmatter(contents) {
|
|
|
21727
21799
|
/**
|
|
21728
21800
|
* The `name`/`description` header a skill and a subagent definition both carry.
|
|
21729
21801
|
*
|
|
21730
|
-
*
|
|
21802
|
+
* Shared because agent definitions are the same header in a different file,
|
|
21731
21803
|
* and the sync validates them before publishing one. A second copy of this
|
|
21732
21804
|
* would be a second answer to "does this load in the provider", which is the
|
|
21733
|
-
* one question
|
|
21805
|
+
* one question every caller is asking.
|
|
21734
21806
|
*/
|
|
21735
21807
|
function parseMarkdownFrontmatter(contents) {
|
|
21736
21808
|
const match = FRONTMATTER_PATTERN$1.exec(contents);
|
|
@@ -21755,6 +21827,245 @@ function parseMarkdownFrontmatter(contents) {
|
|
|
21755
21827
|
...requiredInteractionMode === void 0 ? {} : { requiredInteractionMode }
|
|
21756
21828
|
};
|
|
21757
21829
|
}
|
|
21830
|
+
//#endregion
|
|
21831
|
+
//#region src/provider/Drivers/SharedSkills.ts
|
|
21832
|
+
/**
|
|
21833
|
+
* SharedSkills — the skills a provider can reach, whichever provider it is.
|
|
21834
|
+
*
|
|
21835
|
+
* A skill is a directory holding `SKILL.md`, and by now every CLI P4Code drives
|
|
21836
|
+
* loads them from the *same* provider-neutral user root — `~/.agents/skills` —
|
|
21837
|
+
* on top of its own. That is why the sync materializes there
|
|
21838
|
+
* (`sync/sharedSkillRoot.ts`): one copy of a skill, visible to all of them.
|
|
21839
|
+
*
|
|
21840
|
+
* What was still Claude- and Codex-only is the *reporting*: the composer's `$`
|
|
21841
|
+
* picker and the transcript's skill chips read `ServerProvider.skills`, and a
|
|
21842
|
+
* provider that never filled that array looked like it had no skills at all
|
|
21843
|
+
* while its CLI was happily loading them. So discovery is done here once,
|
|
21844
|
+
* against the roots each CLI documents, and every driver fills the same field.
|
|
21845
|
+
*
|
|
21846
|
+
* **Reported, not enforced.** `enabled` mirrors P4Code's own disabled-skill
|
|
21847
|
+
* setting so a skill switched off disappears from the picker everywhere. Only
|
|
21848
|
+
* Claude can additionally be told to withhold it from the model
|
|
21849
|
+
* (`skillOverrides`); the other CLIs own their listing, so the toggle is a
|
|
21850
|
+
* P4Code-surface toggle there, exactly as the flag's name in discovery says.
|
|
21851
|
+
*
|
|
21852
|
+
* Roots per CLI, from each vendor's own documentation:
|
|
21853
|
+
* - Cursor: `~/.agents/skills`, `~/.cursor/skills`, `.agents/skills`,
|
|
21854
|
+
* `.cursor/skills` — https://cursor.com/docs/skills
|
|
21855
|
+
* - Grok: `~/.agents/skills`, `~/.grok/skills`, `.agents/skills`,
|
|
21856
|
+
* `.grok/skills` — https://docs.x.ai/build/features/skills-plugins-marketplaces
|
|
21857
|
+
* - OpenCode: `~/.agents/skills`, `~/.claude/skills`,
|
|
21858
|
+
* `<XDG config>/opencode/skills`, and the project mirrors of all three —
|
|
21859
|
+
* https://opencode.ai/docs/skills/
|
|
21860
|
+
* - Muse: `~/.agents/skills`, `<XDG config>/muse/skills`, `.agents/skills`,
|
|
21861
|
+
* plus the foreign personal roots it reads unless `--no-foreign-personal-context`
|
|
21862
|
+
* is passed (`~/.claude/skills`, `~/.codex/skills`), which `muse skills list`
|
|
21863
|
+
* reports as user-scope entries. Those two are gated on
|
|
21864
|
+
* `MUSE_EXEC_LOADS_FOREIGN_PERSONAL_SKILLS`, which the argv builder owns,
|
|
21865
|
+
* so the claim tracks the flag the adapter actually spawns with.
|
|
21866
|
+
*
|
|
21867
|
+
* Claude and Codex are absent on purpose: each already reports its own skills
|
|
21868
|
+
* from a source that knows more than the filesystem does — Claude's config
|
|
21869
|
+
* directory may be redirected per instance, and Codex answers a `skills/list`
|
|
21870
|
+
* RPC that includes skills no directory scan would find.
|
|
21871
|
+
*
|
|
21872
|
+
* @module provider/Drivers/SharedSkills
|
|
21873
|
+
*/
|
|
21874
|
+
/** `SKILL.md` is the only file a skill is required to have. */
|
|
21875
|
+
const SKILL_FILENAME$1 = "SKILL.md";
|
|
21876
|
+
/** The provider-neutral user skill root the sync materializes into. */
|
|
21877
|
+
const SHARED_USER_SKILL_DIRNAME = ".agents";
|
|
21878
|
+
/**
|
|
21879
|
+
* Enumerate skills across roots, least specific first.
|
|
21880
|
+
*
|
|
21881
|
+
* Discovery is best-effort: unreadable roots and malformed skill entries are
|
|
21882
|
+
* skipped so a broken skill never degrades the provider snapshot. On name
|
|
21883
|
+
* collisions the later root wins, matching the most-specific-wins resolution
|
|
21884
|
+
* every one of these CLIs uses — which is also why the shared root is listed
|
|
21885
|
+
* before the provider's own.
|
|
21886
|
+
*/
|
|
21887
|
+
const discoverSkillsInRoots = Effect.fn("discoverSkillsInRoots")(function* (roots, disabledSkills) {
|
|
21888
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
21889
|
+
const path = yield* Path.Path;
|
|
21890
|
+
const skillsByName = /* @__PURE__ */ new Map();
|
|
21891
|
+
for (const root of roots) {
|
|
21892
|
+
const entries = yield* fileSystem.readDirectory(root.directory).pipe(Effect.orElseSucceed(() => []));
|
|
21893
|
+
for (const entry of [...entries].sort()) {
|
|
21894
|
+
const skillPath = path.join(root.directory, entry, SKILL_FILENAME$1);
|
|
21895
|
+
const contents = yield* fileSystem.readFileString(skillPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
21896
|
+
if (contents === void 0) continue;
|
|
21897
|
+
const frontmatter = parseMarkdownFrontmatter(contents);
|
|
21898
|
+
if (frontmatter.kind === "malformed") continue;
|
|
21899
|
+
const name = (frontmatter.kind === "parsed" ? frontmatter.name : void 0) ?? entry.trim();
|
|
21900
|
+
if (!name) continue;
|
|
21901
|
+
skillsByName.set(name, {
|
|
21902
|
+
name,
|
|
21903
|
+
path: skillPath,
|
|
21904
|
+
enabled: !isSkillDisabled({
|
|
21905
|
+
name,
|
|
21906
|
+
path: skillPath
|
|
21907
|
+
}, disabledSkills ?? []),
|
|
21908
|
+
scope: root.scope,
|
|
21909
|
+
...frontmatter.kind === "parsed" && frontmatter.description ? { description: frontmatter.description } : {},
|
|
21910
|
+
...frontmatter.kind === "parsed" && frontmatter.requiredInteractionMode ? { requiredInteractionMode: frontmatter.requiredInteractionMode } : {}
|
|
21911
|
+
});
|
|
21912
|
+
}
|
|
21913
|
+
}
|
|
21914
|
+
return [...skillsByName.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
21915
|
+
});
|
|
21916
|
+
/**
|
|
21917
|
+
* The config home a CLI following the XDG convention reads, matching what the
|
|
21918
|
+
* spawned process would see: its own `XDG_CONFIG_HOME` when set, else
|
|
21919
|
+
* `~/.config`. Read from the provider environment rather than `process.env`
|
|
21920
|
+
* because an instance may be configured with a different one.
|
|
21921
|
+
*/
|
|
21922
|
+
function xdgConfigHome$1(environment) {
|
|
21923
|
+
const configured = environment.XDG_CONFIG_HOME?.trim() ?? "";
|
|
21924
|
+
return configured.length > 0 ? configured : `${homeDirectory(environment)}/.config`;
|
|
21925
|
+
}
|
|
21926
|
+
/**
|
|
21927
|
+
* The home the spawned CLI would resolve: the provider instance's own `HOME`
|
|
21928
|
+
* when it sets one, else this process's. A provider configured with a separate
|
|
21929
|
+
* home keeps its own skills, which is the point of configuring one.
|
|
21930
|
+
*/
|
|
21931
|
+
function homeDirectory(environment) {
|
|
21932
|
+
const configured = environment.HOME?.trim() ?? "";
|
|
21933
|
+
return configured.length > 0 ? configured : NodeOS.homedir();
|
|
21934
|
+
}
|
|
21935
|
+
/**
|
|
21936
|
+
* The provider-neutral roots every CLI reads, least specific first.
|
|
21937
|
+
*
|
|
21938
|
+
* For callers that act on a skill without knowing which provider will run it —
|
|
21939
|
+
* the server-side expansion of a user-invoked skill, which happens before the
|
|
21940
|
+
* turn reaches an adapter.
|
|
21941
|
+
*/
|
|
21942
|
+
const sharedSkillDirectories = Effect.fn("sharedSkillDirectories")(function* (cwd, environment) {
|
|
21943
|
+
const path = yield* Path.Path;
|
|
21944
|
+
const user = path.join(homeDirectory(environment ?? process.env), SHARED_USER_SKILL_DIRNAME, "skills");
|
|
21945
|
+
return cwd === void 0 ? [user] : [user, path.join(cwd, SHARED_USER_SKILL_DIRNAME, "skills")];
|
|
21946
|
+
});
|
|
21947
|
+
/** User-scope roots per driver, shared root first. */
|
|
21948
|
+
function userSkillDirectories(driver, environment) {
|
|
21949
|
+
const home = homeDirectory(environment);
|
|
21950
|
+
const shared = [
|
|
21951
|
+
home,
|
|
21952
|
+
SHARED_USER_SKILL_DIRNAME,
|
|
21953
|
+
"skills"
|
|
21954
|
+
];
|
|
21955
|
+
switch (driver) {
|
|
21956
|
+
case "cursor": return [shared, [
|
|
21957
|
+
home,
|
|
21958
|
+
".cursor",
|
|
21959
|
+
"skills"
|
|
21960
|
+
]];
|
|
21961
|
+
case "grok": return [shared, [
|
|
21962
|
+
home,
|
|
21963
|
+
".grok",
|
|
21964
|
+
"skills"
|
|
21965
|
+
]];
|
|
21966
|
+
case "opencode": return [
|
|
21967
|
+
shared,
|
|
21968
|
+
[
|
|
21969
|
+
home,
|
|
21970
|
+
".claude",
|
|
21971
|
+
"skills"
|
|
21972
|
+
],
|
|
21973
|
+
[
|
|
21974
|
+
xdgConfigHome$1(environment),
|
|
21975
|
+
"opencode",
|
|
21976
|
+
"skills"
|
|
21977
|
+
]
|
|
21978
|
+
];
|
|
21979
|
+
case "muse": return [
|
|
21980
|
+
shared,
|
|
21981
|
+
...[[
|
|
21982
|
+
home,
|
|
21983
|
+
".claude",
|
|
21984
|
+
"skills"
|
|
21985
|
+
], [
|
|
21986
|
+
home,
|
|
21987
|
+
".codex",
|
|
21988
|
+
"skills"
|
|
21989
|
+
]],
|
|
21990
|
+
[
|
|
21991
|
+
xdgConfigHome$1(environment),
|
|
21992
|
+
"muse",
|
|
21993
|
+
"skills"
|
|
21994
|
+
]
|
|
21995
|
+
];
|
|
21996
|
+
default: return [shared];
|
|
21997
|
+
}
|
|
21998
|
+
}
|
|
21999
|
+
/** Project-scope roots per driver, relative to the workspace. */
|
|
22000
|
+
function projectSkillDirectories(driver) {
|
|
22001
|
+
const shared = [SHARED_USER_SKILL_DIRNAME, "skills"];
|
|
22002
|
+
switch (driver) {
|
|
22003
|
+
case "cursor": return [shared, [".cursor", "skills"]];
|
|
22004
|
+
case "grok": return [shared, [".grok", "skills"]];
|
|
22005
|
+
case "opencode": return [
|
|
22006
|
+
shared,
|
|
22007
|
+
[".claude", "skills"],
|
|
22008
|
+
[".opencode", "skills"]
|
|
22009
|
+
];
|
|
22010
|
+
default: return [shared];
|
|
22011
|
+
}
|
|
22012
|
+
}
|
|
22013
|
+
/** Every root a driver's CLI reads, least specific first. */
|
|
22014
|
+
const providerSkillRoots = Effect.fn("providerSkillRoots")(function* (input) {
|
|
22015
|
+
const path = yield* Path.Path;
|
|
22016
|
+
const environment = input.environment ?? process.env;
|
|
22017
|
+
const user = userSkillDirectories(input.driver, environment).map((segments) => ({
|
|
22018
|
+
directory: path.join(...segments),
|
|
22019
|
+
scope: "user"
|
|
22020
|
+
}));
|
|
22021
|
+
const cwd = input.cwd;
|
|
22022
|
+
if (cwd === void 0) return user;
|
|
22023
|
+
return [...user, ...projectSkillDirectories(input.driver).map((segments) => ({
|
|
22024
|
+
directory: path.join(cwd, ...segments),
|
|
22025
|
+
scope: "project"
|
|
22026
|
+
}))];
|
|
22027
|
+
});
|
|
22028
|
+
/**
|
|
22029
|
+
* A snapshot draft carrying the skills its CLI can reach.
|
|
22030
|
+
*
|
|
22031
|
+
* Applied after the probe rather than inside it: what a CLI reports about
|
|
22032
|
+
* itself — version, auth, models — and what it loads off disk are independent,
|
|
22033
|
+
* and a provider that is failing its probe still has the skills sitting there
|
|
22034
|
+
* for the picker.
|
|
22035
|
+
*/
|
|
22036
|
+
const withProviderSkills = Effect.fn("withProviderSkills")(function* (draft, input) {
|
|
22037
|
+
const skills = yield* discoverProviderSkills(input);
|
|
22038
|
+
return {
|
|
22039
|
+
...draft,
|
|
22040
|
+
skills
|
|
22041
|
+
};
|
|
22042
|
+
});
|
|
22043
|
+
/**
|
|
22044
|
+
* The skills a provider snapshot should report for a driver that loads them
|
|
22045
|
+
* from disk. Drivers call this instead of leaving `skills` empty.
|
|
22046
|
+
*/
|
|
22047
|
+
const discoverProviderSkills = Effect.fn("discoverProviderSkills")(function* (input) {
|
|
22048
|
+
const roots = yield* providerSkillRoots({
|
|
22049
|
+
driver: input.driver,
|
|
22050
|
+
cwd: input.cwd,
|
|
22051
|
+
environment: input.environment
|
|
22052
|
+
});
|
|
22053
|
+
return yield* discoverSkillsInRoots(roots, input.disabledSkills);
|
|
22054
|
+
});
|
|
22055
|
+
//#endregion
|
|
22056
|
+
//#region src/provider/Drivers/ClaudeSkills.ts
|
|
22057
|
+
/**
|
|
22058
|
+
* ClaudeSkills — filesystem discovery of Claude Code skills for the `$` picker.
|
|
22059
|
+
*
|
|
22060
|
+
* Claude Code loads skills from `<config dir>/skills` (user scope) and
|
|
22061
|
+
* `<cwd>/.claude/skills` (project scope), one directory per skill with a
|
|
22062
|
+
* `SKILL.md` carrying YAML frontmatter. The Agent SDK init handshake surfaces
|
|
22063
|
+
* skills only as slash commands without their filesystem paths, so the
|
|
22064
|
+
* provider snapshot scans the same locations directly, mirroring how the
|
|
22065
|
+
* Codex app-server reports its skills.
|
|
22066
|
+
*
|
|
22067
|
+
* @module provider/Drivers/ClaudeSkills
|
|
22068
|
+
*/
|
|
21758
22069
|
/**
|
|
21759
22070
|
* Resolve the Claude config directory the CLI would use, matching the
|
|
21760
22071
|
* precedence the spawned CLI sees: the instance's `homePath` (exported as
|
|
@@ -21807,41 +22118,15 @@ const resolveClaudeUserAgentsDir = Effect.fn("resolveClaudeUserAgentsDir")(funct
|
|
|
21807
22118
|
* most-specific-wins resolution.
|
|
21808
22119
|
*/
|
|
21809
22120
|
const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config, cwd, environment, disabledSkills) {
|
|
21810
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
21811
22121
|
const path = yield* Path.Path;
|
|
21812
22122
|
const configDirPath = yield* resolveClaudeConfigDirPath(config, environment ?? process.env, cwd);
|
|
21813
|
-
|
|
22123
|
+
return yield* discoverSkillsInRoots([{
|
|
21814
22124
|
directory: path.join(configDirPath, "skills"),
|
|
21815
22125
|
scope: "user"
|
|
21816
22126
|
}, ...cwd ? [{
|
|
21817
22127
|
directory: path.join(cwd, ".claude", "skills"),
|
|
21818
22128
|
scope: "project"
|
|
21819
|
-
}] : []];
|
|
21820
|
-
const skillsByName = /* @__PURE__ */ new Map();
|
|
21821
|
-
for (const root of roots) {
|
|
21822
|
-
const entries = yield* fileSystem.readDirectory(root.directory).pipe(Effect.orElseSucceed(() => []));
|
|
21823
|
-
for (const entry of [...entries].sort()) {
|
|
21824
|
-
const skillPath = path.join(root.directory, entry, "SKILL.md");
|
|
21825
|
-
const contents = yield* fileSystem.readFileString(skillPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
21826
|
-
if (contents === void 0) continue;
|
|
21827
|
-
const frontmatter = parseMarkdownFrontmatter(contents);
|
|
21828
|
-
if (frontmatter.kind === "malformed") continue;
|
|
21829
|
-
const name = (frontmatter.kind === "parsed" ? frontmatter.name : void 0) ?? entry.trim();
|
|
21830
|
-
if (!name) continue;
|
|
21831
|
-
skillsByName.set(name, {
|
|
21832
|
-
name,
|
|
21833
|
-
path: skillPath,
|
|
21834
|
-
enabled: !isSkillDisabled({
|
|
21835
|
-
name,
|
|
21836
|
-
path: skillPath
|
|
21837
|
-
}, disabledSkills ?? []),
|
|
21838
|
-
scope: root.scope,
|
|
21839
|
-
...frontmatter.kind === "parsed" && frontmatter.description ? { description: frontmatter.description } : {},
|
|
21840
|
-
...frontmatter.kind === "parsed" && frontmatter.requiredInteractionMode ? { requiredInteractionMode: frontmatter.requiredInteractionMode } : {}
|
|
21841
|
-
});
|
|
21842
|
-
}
|
|
21843
|
-
}
|
|
21844
|
-
return [...skillsByName.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
22129
|
+
}] : []], disabledSkills);
|
|
21845
22130
|
});
|
|
21846
22131
|
//#endregion
|
|
21847
22132
|
//#region src/mcp/ClaudeMcpFiles.ts
|
|
@@ -23818,6 +24103,138 @@ function planSkillSync(input) {
|
|
|
23818
24103
|
};
|
|
23819
24104
|
}
|
|
23820
24105
|
//#endregion
|
|
24106
|
+
//#region src/sync/assetMirrorOps.ts
|
|
24107
|
+
/**
|
|
24108
|
+
* Whether this machine may write the asset into a mirror root.
|
|
24109
|
+
*
|
|
24110
|
+
* `foreign` covers the case that matters: a file someone else wrote, or a
|
|
24111
|
+
* symlink a user set up between two providers, whose content matches neither
|
|
24112
|
+
* what is being written nor what P4Code last wrote from here. Overwriting it
|
|
24113
|
+
* would silently replace a configuration the user owns.
|
|
24114
|
+
*/
|
|
24115
|
+
function decideMirrorWrite(input) {
|
|
24116
|
+
if (input.existing === void 0) return "write";
|
|
24117
|
+
if (input.existing === input.next) return "up-to-date";
|
|
24118
|
+
if (input.previous !== void 0 && input.existing === input.previous) return "write";
|
|
24119
|
+
return "foreign";
|
|
24120
|
+
}
|
|
24121
|
+
/**
|
|
24122
|
+
* Whether a mirrored copy may be removed.
|
|
24123
|
+
*
|
|
24124
|
+
* Only a copy that still matches what P4Code wrote. An edited mirror is the
|
|
24125
|
+
* user's, and a deletion upstream is not a reason to throw their edit away.
|
|
24126
|
+
*/
|
|
24127
|
+
function shouldRemoveMirror(input) {
|
|
24128
|
+
return input.existing !== void 0 && input.managed !== void 0 && input.existing === input.managed;
|
|
24129
|
+
}
|
|
24130
|
+
//#endregion
|
|
24131
|
+
//#region src/sync/providerAssetRoots.ts
|
|
24132
|
+
/**
|
|
24133
|
+
* providerAssetRoots — where a memory file or a subagent definition has to
|
|
24134
|
+
* land for a provider other than Claude to read it.
|
|
24135
|
+
*
|
|
24136
|
+
* Skills needed none of this: every CLI P4Code drives loads
|
|
24137
|
+
* `~/.agents/skills`, so one directory serves them all. Memory and subagent
|
|
24138
|
+
* definitions have no such shared root — there is a proposal to standardize a
|
|
24139
|
+
* user-level `AGENTS.md` location and nothing shipped — so each provider reads
|
|
24140
|
+
* its own path, and an asset that exists only in Claude's config directory is
|
|
24141
|
+
* an asset the other providers never see.
|
|
24142
|
+
*
|
|
24143
|
+
* Verified on this machine rather than taken from documentation:
|
|
24144
|
+
* - Codex keeps user instructions at `$CODEX_HOME/AGENTS.md` (default
|
|
24145
|
+
* `~/.codex`) and its subagent definitions at `$CODEX_HOME/agents/*.toml`.
|
|
24146
|
+
* - OpenCode keeps user instructions at `<XDG config>/opencode/AGENTS.md`
|
|
24147
|
+
* and its subagent definitions at `<XDG config>/opencode/agents/*.md`,
|
|
24148
|
+
* markdown with the same `name`/`description` frontmatter Claude uses.
|
|
24149
|
+
* - Cursor reads subagent definitions from `~/.cursor/agents`, and its own
|
|
24150
|
+
* documentation says it also reads `~/.claude/agents` and `~/.codex/agents`
|
|
24151
|
+
* for cross-tool compatibility.
|
|
24152
|
+
* - Muse reads the other CLIs' personal rules — `~/.claude/CLAUDE.md` and
|
|
24153
|
+
* `~/.codex/AGENTS.md` — unless a run passes
|
|
24154
|
+
* `--no-foreign-personal-context`, which P4Code never does
|
|
24155
|
+
* (`MUSE_EXEC_LOADS_FOREIGN_PERSONAL_SKILLS`). It therefore needs no
|
|
24156
|
+
* mirror of its own.
|
|
24157
|
+
*
|
|
24158
|
+
* Deliberately absent:
|
|
24159
|
+
* - **Codex subagents.** They are TOML with `developer_instructions`, not
|
|
24160
|
+
* markdown with frontmatter. Mirroring one would mean converting a
|
|
24161
|
+
* definition rather than copying it, and a lossy conversion of the file
|
|
24162
|
+
* that decides what a subagent is handed is worse than not having it.
|
|
24163
|
+
* - **Cursor user rules.** Cursor keeps cross-project prose in its own
|
|
24164
|
+
* settings store, not in a file on disk, so there is nothing to write.
|
|
24165
|
+
* - **Grok.** Its skills root is confirmed (`~/.agents/skills`); a
|
|
24166
|
+
* user-level instructions file is not, and x.ai's own documentation does
|
|
24167
|
+
* not name one. Writing a guessed path is worse than writing nothing.
|
|
24168
|
+
*
|
|
24169
|
+
* @module sync/providerAssetRoots
|
|
24170
|
+
*/
|
|
24171
|
+
/**
|
|
24172
|
+
* The filename every non-Claude provider here reads its user instructions from.
|
|
24173
|
+
*
|
|
24174
|
+
* Codex and OpenCode read `AGENTS.md` and nothing else, so a `CLAUDE.md` asset
|
|
24175
|
+
* — the default, and what most machines actually hold — reaches them only
|
|
24176
|
+
* under this name. That rename is the one place in the sync where the asset's
|
|
24177
|
+
* name and the file on disk differ, and it is narrow on purpose: it happens for
|
|
24178
|
+
* this one pair of names, into these roots, and only when there is no
|
|
24179
|
+
* `AGENTS.md` asset of its own to mirror instead.
|
|
24180
|
+
*/
|
|
24181
|
+
const SHARED_MEMORY_FILE_NAME = "AGENTS.md";
|
|
24182
|
+
/**
|
|
24183
|
+
* The config home of a CLI following the XDG convention, as the CLI itself
|
|
24184
|
+
* would resolve it.
|
|
24185
|
+
*/
|
|
24186
|
+
function xdgConfigHome(environment) {
|
|
24187
|
+
const configured = environment.XDG_CONFIG_HOME?.trim() ?? "";
|
|
24188
|
+
if (configured.length > 0) return configured;
|
|
24189
|
+
const home = environment.HOME?.trim() ?? "";
|
|
24190
|
+
return `${home.length > 0 ? home : NodeOS.homedir()}/.config`;
|
|
24191
|
+
}
|
|
24192
|
+
/** Codex's home, matching the CLI's own `CODEX_HOME` precedence. */
|
|
24193
|
+
function codexHome(environment) {
|
|
24194
|
+
const configured = environment.CODEX_HOME?.trim() ?? "";
|
|
24195
|
+
return configured.length > 0 ? configured : `${NodeOS.homedir()}/.codex`;
|
|
24196
|
+
}
|
|
24197
|
+
/**
|
|
24198
|
+
* Directories that should hold a copy of a shared memory file.
|
|
24199
|
+
*
|
|
24200
|
+
* Directories rather than file paths because the sync writes an asset by name
|
|
24201
|
+
* into a root, and the name *is* the filename.
|
|
24202
|
+
*/
|
|
24203
|
+
const memoryMirrorRoots = Effect.fn("memoryMirrorRoots")(function* (environment) {
|
|
24204
|
+
const path = yield* Path.Path;
|
|
24205
|
+
const resolved = environment ?? process.env;
|
|
24206
|
+
return [path.resolve(codexHome(resolved)), path.join(path.resolve(xdgConfigHome(resolved)), "opencode")];
|
|
24207
|
+
});
|
|
24208
|
+
/**
|
|
24209
|
+
* Where a memory asset should be copied, and under which name.
|
|
24210
|
+
*
|
|
24211
|
+
* `AGENTS.md` maps to itself. `CLAUDE.md` maps to `AGENTS.md`, but only when no
|
|
24212
|
+
* `AGENTS.md` asset exists: with both in play the destination would otherwise
|
|
24213
|
+
* flip between two sources depending on which one synced last, and a file whose
|
|
24214
|
+
* content depends on ordering is worse than one provider missing a mirror.
|
|
24215
|
+
*/
|
|
24216
|
+
const memoryMirrorTargets = Effect.fn("memoryMirrorTargets")(function* (input) {
|
|
24217
|
+
if (!(input.name === "AGENTS.md" || input.name === "CLAUDE.md" && !input.siblingNames.has("AGENTS.md"))) return [];
|
|
24218
|
+
return (yield* memoryMirrorRoots(input.environment)).map((root) => ({
|
|
24219
|
+
root,
|
|
24220
|
+
name: SHARED_MEMORY_FILE_NAME
|
|
24221
|
+
}));
|
|
24222
|
+
});
|
|
24223
|
+
/** Directories that should hold a copy of a markdown subagent definition. */
|
|
24224
|
+
const agentDefinitionMirrorRoots = Effect.fn("agentDefinitionMirrorRoots")(function* (environment) {
|
|
24225
|
+
const path = yield* Path.Path;
|
|
24226
|
+
const resolved = environment ?? process.env;
|
|
24227
|
+
const home = resolved.HOME?.trim() ?? "";
|
|
24228
|
+
return [path.join(home.length > 0 ? home : NodeOS.homedir(), ".cursor", "agents"), path.join(path.resolve(xdgConfigHome(resolved)), "opencode", "agents")];
|
|
24229
|
+
});
|
|
24230
|
+
/** Where a markdown subagent definition should be copied, under its own name. */
|
|
24231
|
+
const agentDefinitionMirrorTargets = Effect.fn("agentDefinitionMirrorTargets")(function* (input) {
|
|
24232
|
+
return (yield* agentDefinitionMirrorRoots(input.environment)).map((root) => ({
|
|
24233
|
+
root,
|
|
24234
|
+
name: input.name
|
|
24235
|
+
}));
|
|
24236
|
+
});
|
|
24237
|
+
//#endregion
|
|
23821
24238
|
//#region src/sync/sharedSkillRoot.ts
|
|
23822
24239
|
const SHARED_SKILL_ROOT_REFUSAL = "shared-root-migration-blocked";
|
|
23823
24240
|
const inspectLayout = Effect.fn("sharedSkillRoot.inspectLayout")(function* (input) {
|
|
@@ -24055,15 +24472,70 @@ const make$77 = Effect.gen(function* () {
|
|
|
24055
24472
|
}]
|
|
24056
24473
|
}))));
|
|
24057
24474
|
});
|
|
24475
|
+
const prepareSkillRoot = Effect.gen(function* () {
|
|
24476
|
+
yield* prepareSharedSkillRoot({
|
|
24477
|
+
claudeSkillsRoot: yield* claudeHome().pipe(Effect.flatMap(resolveClaudeUserSkillsDir)),
|
|
24478
|
+
manifestPath
|
|
24479
|
+
});
|
|
24480
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning("shared skill root migration failed", { cause })));
|
|
24481
|
+
/** What a mirror root currently holds under that name, if anything. */
|
|
24482
|
+
const mirrorDigest = Effect.fn("AssetSync.mirrorDigest")(function* (driver, root, name) {
|
|
24483
|
+
const existing = (yield* driver.read(root)).skills.find((entry) => entry.name === name);
|
|
24484
|
+
return existing ? digestSkillFiles(existing.files) : void 0;
|
|
24485
|
+
});
|
|
24486
|
+
/**
|
|
24487
|
+
* Copy a managed asset into the roots other providers read.
|
|
24488
|
+
*
|
|
24489
|
+
* Nothing P4Code did not put there is ever overwritten: a mirror holding
|
|
24490
|
+
* content that matches neither what is being written nor what was last
|
|
24491
|
+
* written from here belongs to someone else, and it is left alone. That is
|
|
24492
|
+
* the whole guard — a file the user wrote by hand, or a symlink they set up
|
|
24493
|
+
* between two providers, survives a sync untouched.
|
|
24494
|
+
*/
|
|
24495
|
+
const mirrorWrite = Effect.fn("AssetSync.mirrorWrite")(function* (input) {
|
|
24496
|
+
const mirrors = input.driver.mirrors;
|
|
24497
|
+
if (mirrors === void 0) return;
|
|
24498
|
+
const nextDigest = digestSkillFiles(input.files);
|
|
24499
|
+
const targets = yield* mirrors({
|
|
24500
|
+
name: input.name,
|
|
24501
|
+
siblingNames: input.siblingNames
|
|
24502
|
+
});
|
|
24503
|
+
for (const target of targets) {
|
|
24504
|
+
if (decideMirrorWrite({
|
|
24505
|
+
existing: yield* mirrorDigest(input.driver, target.root, target.name),
|
|
24506
|
+
next: nextDigest,
|
|
24507
|
+
previous: input.previousDigest
|
|
24508
|
+
}) !== "write") continue;
|
|
24509
|
+
yield* input.driver.write({
|
|
24510
|
+
root: target.root,
|
|
24511
|
+
name: target.name,
|
|
24512
|
+
files: input.files
|
|
24513
|
+
});
|
|
24514
|
+
}
|
|
24515
|
+
});
|
|
24516
|
+
/** Drop a mirrored copy, and only a copy that still matches what we wrote. */
|
|
24517
|
+
const mirrorRemove = Effect.fn("AssetSync.mirrorRemove")(function* (input) {
|
|
24518
|
+
const mirrors = input.driver.mirrors;
|
|
24519
|
+
if (mirrors === void 0) return;
|
|
24520
|
+
const targets = yield* mirrors({
|
|
24521
|
+
name: input.name,
|
|
24522
|
+
siblingNames: input.siblingNames
|
|
24523
|
+
});
|
|
24524
|
+
for (const target of targets) {
|
|
24525
|
+
if (!shouldRemoveMirror({
|
|
24526
|
+
existing: yield* mirrorDigest(input.driver, target.root, target.name),
|
|
24527
|
+
managed: input.digest
|
|
24528
|
+
})) continue;
|
|
24529
|
+
yield* input.driver.remove({
|
|
24530
|
+
root: target.root,
|
|
24531
|
+
name: target.name
|
|
24532
|
+
});
|
|
24533
|
+
}
|
|
24534
|
+
});
|
|
24058
24535
|
const drivers = [
|
|
24059
24536
|
{
|
|
24060
24537
|
kind: "skill",
|
|
24061
|
-
prepareRoot:
|
|
24062
|
-
yield* prepareSharedSkillRoot({
|
|
24063
|
-
claudeSkillsRoot: yield* claudeHome().pipe(Effect.flatMap(resolveClaudeUserSkillsDir)),
|
|
24064
|
-
manifestPath
|
|
24065
|
-
});
|
|
24066
|
-
}).pipe(Effect.catchCause((cause) => Effect.logWarning("shared skill root migration failed", { cause }))),
|
|
24538
|
+
prepareRoot: prepareSkillRoot,
|
|
24067
24539
|
resolveState: inspectSkillRoot(),
|
|
24068
24540
|
resolveRoot: inspectSkillRoot().pipe(Effect.map((result) => result.root)),
|
|
24069
24541
|
read: (root) => readSkillDirectory(root),
|
|
@@ -24091,7 +24563,11 @@ const make$77 = Effect.gen(function* () {
|
|
|
24091
24563
|
memoryRoot: root,
|
|
24092
24564
|
sharedMemoryRoot,
|
|
24093
24565
|
name
|
|
24094
|
-
}).pipe(Effect.ignore)
|
|
24566
|
+
}).pipe(Effect.ignore),
|
|
24567
|
+
mirrors: ({ name, siblingNames }) => memoryMirrorTargets({
|
|
24568
|
+
name,
|
|
24569
|
+
siblingNames
|
|
24570
|
+
})
|
|
24095
24571
|
},
|
|
24096
24572
|
{
|
|
24097
24573
|
kind: "agent",
|
|
@@ -24105,7 +24581,8 @@ const make$77 = Effect.gen(function* () {
|
|
|
24105
24581
|
remove: ({ root, name }) => removeAgentDefinition({
|
|
24106
24582
|
agentsRoot: root,
|
|
24107
24583
|
name
|
|
24108
|
-
}).pipe(Effect.ignore)
|
|
24584
|
+
}).pipe(Effect.ignore),
|
|
24585
|
+
mirrors: ({ name }) => agentDefinitionMirrorTargets({ name })
|
|
24109
24586
|
},
|
|
24110
24587
|
{
|
|
24111
24588
|
kind: "mcp",
|
|
@@ -24212,6 +24689,11 @@ const make$77 = Effect.gen(function* () {
|
|
|
24212
24689
|
manifest: views.managed
|
|
24213
24690
|
});
|
|
24214
24691
|
const entries = new Map(views.managed.map((entry) => [entry.name, entry]));
|
|
24692
|
+
/**
|
|
24693
|
+
* Every asset of this kind in play, which the memory driver reads to
|
|
24694
|
+
* decide whether a `CLAUDE.md` may stand in for a missing `AGENTS.md`.
|
|
24695
|
+
*/
|
|
24696
|
+
const assetNames = /* @__PURE__ */ new Set([...views.hub.map((asset) => asset.name), ...views.local.map((asset) => asset.name)]);
|
|
24215
24697
|
/** One manifest for the whole run, re-read per kind so roots do not clobber. */
|
|
24216
24698
|
const commit = Effect.fn("AssetSync.commit")(function* () {
|
|
24217
24699
|
const manifest = yield* readSkillManifest(manifestPath);
|
|
@@ -24232,6 +24714,13 @@ const make$77 = Effect.gen(function* () {
|
|
|
24232
24714
|
});
|
|
24233
24715
|
break;
|
|
24234
24716
|
}
|
|
24717
|
+
yield* mirrorWrite({
|
|
24718
|
+
driver,
|
|
24719
|
+
name: action.asset.name,
|
|
24720
|
+
siblingNames: assetNames,
|
|
24721
|
+
files: action.asset.files,
|
|
24722
|
+
previousDigest: entries.get(action.asset.name)?.digest
|
|
24723
|
+
});
|
|
24235
24724
|
entries.set(action.asset.name, {
|
|
24236
24725
|
name: action.asset.name,
|
|
24237
24726
|
version: action.asset.version,
|
|
@@ -24274,6 +24763,12 @@ const make$77 = Effect.gen(function* () {
|
|
|
24274
24763
|
root,
|
|
24275
24764
|
name: action.name
|
|
24276
24765
|
});
|
|
24766
|
+
yield* mirrorRemove({
|
|
24767
|
+
driver,
|
|
24768
|
+
name: action.name,
|
|
24769
|
+
siblingNames: assetNames,
|
|
24770
|
+
digest: entries.get(action.name)?.digest
|
|
24771
|
+
});
|
|
24277
24772
|
entries.delete(action.name);
|
|
24278
24773
|
removed.push({
|
|
24279
24774
|
kind,
|
|
@@ -24711,6 +25206,13 @@ const make$77 = Effect.gen(function* () {
|
|
|
24711
25206
|
name: input.name,
|
|
24712
25207
|
files: input.files
|
|
24713
25208
|
});
|
|
25209
|
+
if (written === "written") yield* mirrorWrite({
|
|
25210
|
+
driver,
|
|
25211
|
+
name: input.name,
|
|
25212
|
+
siblingNames: new Set(local.skills.map((asset) => asset.name)),
|
|
25213
|
+
files: input.files,
|
|
25214
|
+
previousDigest: void 0
|
|
25215
|
+
});
|
|
24714
25216
|
if (written !== "written") return {
|
|
24715
25217
|
outcome: written,
|
|
24716
25218
|
path: null,
|
|
@@ -24747,10 +25249,17 @@ const make$77 = Effect.gen(function* () {
|
|
|
24747
25249
|
path: null,
|
|
24748
25250
|
detail: decision.detail
|
|
24749
25251
|
};
|
|
25252
|
+
const removedDigest = local.skills.find((entry) => entry.name === ref.name);
|
|
24750
25253
|
yield* driver.remove({
|
|
24751
25254
|
root,
|
|
24752
25255
|
name: ref.name
|
|
24753
25256
|
});
|
|
25257
|
+
yield* mirrorRemove({
|
|
25258
|
+
driver,
|
|
25259
|
+
name: ref.name,
|
|
25260
|
+
siblingNames: new Set(local.skills.map((asset) => asset.name)),
|
|
25261
|
+
digest: removedDigest ? digestSkillFiles(removedDigest.files) : void 0
|
|
25262
|
+
});
|
|
24754
25263
|
return {
|
|
24755
25264
|
outcome: "removed",
|
|
24756
25265
|
path: null,
|
|
@@ -100200,7 +100709,7 @@ const CURSOR_PRESENTATION = {
|
|
|
100200
100709
|
badgeLabel: "Early Access",
|
|
100201
100710
|
showInteractionModeToggle: true
|
|
100202
100711
|
};
|
|
100203
|
-
const EMPTY_CAPABILITIES$
|
|
100712
|
+
const EMPTY_CAPABILITIES$1 = createModelCapabilities({ optionDescriptors: [] });
|
|
100204
100713
|
const CURSOR_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
|
|
100205
100714
|
const CURSOR_PARAMETERIZED_MODEL_PICKER_MIN_VERSION_DATE = 20260408;
|
|
100206
100715
|
const CURSOR_CLI_INSTALLATION_DOCS_URL = "https://cursor.com/docs/cli/installation";
|
|
@@ -100308,7 +100817,7 @@ function getBooleanCurrentValue(option) {
|
|
|
100308
100817
|
if (normalized === "false") return false;
|
|
100309
100818
|
}
|
|
100310
100819
|
function buildCursorCapabilitiesFromConfigOptions(configOptions) {
|
|
100311
|
-
if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$
|
|
100820
|
+
if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$1;
|
|
100312
100821
|
const reasoningConfig = findCursorEffortConfigOption(configOptions);
|
|
100313
100822
|
const reasoningEffortLevels = reasoningConfig?.type === "select" ? flattenSessionConfigSelectOptions(reasoningConfig).flatMap((entry) => {
|
|
100314
100823
|
const normalizedValue = normalizeCursorReasoningValue(entry.value);
|
|
@@ -100477,7 +100986,7 @@ const discoverCursorModelsViaListAvailableModels = (cursorSettings, environment)
|
|
|
100477
100986
|
}), environment);
|
|
100478
100987
|
const discoverCursorModelsViaAcp = (cursorSettings, environment) => discoverCursorModelsViaListAvailableModels(cursorSettings, environment);
|
|
100479
100988
|
function getCursorFallbackModels(cursorSettings) {
|
|
100480
|
-
return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$
|
|
100989
|
+
return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$1);
|
|
100481
100990
|
}
|
|
100482
100991
|
/** Timeout for `agent about` — it's slower than a simple `--version` probe. */
|
|
100483
100992
|
const ABOUT_TIMEOUT_MS = 8e3;
|
|
@@ -100513,7 +101022,7 @@ function buildCursorProviderSnapshot(input) {
|
|
|
100513
101022
|
presentation: CURSOR_PRESENTATION,
|
|
100514
101023
|
enabled: input.cursorSettings.enabled,
|
|
100515
101024
|
checkedAt: input.checkedAt,
|
|
100516
|
-
models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$
|
|
101025
|
+
models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$1),
|
|
100517
101026
|
probe: {
|
|
100518
101027
|
installed: true,
|
|
100519
101028
|
version: input.parsed.version,
|
|
@@ -102007,6 +102516,7 @@ const CursorDriver = {
|
|
|
102007
102516
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
102008
102517
|
const path = yield* Path.Path;
|
|
102009
102518
|
const httpClient = yield* HttpClient.HttpClient;
|
|
102519
|
+
const serverConfig = yield* ServerConfig$1;
|
|
102010
102520
|
const serverSettings = yield* ServerSettingsService;
|
|
102011
102521
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
102012
102522
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
@@ -102035,7 +102545,12 @@ const CursorDriver = {
|
|
|
102035
102545
|
instanceId
|
|
102036
102546
|
});
|
|
102037
102547
|
const textGeneration = yield* makeCursorTextGeneration(effectiveConfig, processEnv);
|
|
102038
|
-
const checkProvider =
|
|
102548
|
+
const checkProvider = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => [])).pipe(Effect.flatMap((disabledSkills) => checkCursorProviderStatus(effectiveConfig, processEnv).pipe(Effect.flatMap((draft) => withProviderSkills(draft, {
|
|
102549
|
+
driver: DRIVER_KIND$3,
|
|
102550
|
+
cwd: serverConfig.cwd,
|
|
102551
|
+
environment: processEnv,
|
|
102552
|
+
disabledSkills
|
|
102553
|
+
})))), Effect.map(stampIdentity), Effect.provideService(Crypto.Crypto, crypto), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path));
|
|
102039
102554
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
102040
102555
|
const snapshot = yield* makeManagedServerProvider({
|
|
102041
102556
|
maintenanceCapabilities,
|
|
@@ -103346,14 +103861,14 @@ const GROK_PRESENTATION = {
|
|
|
103346
103861
|
showInteractionModeToggle: false,
|
|
103347
103862
|
requiresNewThreadForModelChange: true
|
|
103348
103863
|
};
|
|
103349
|
-
const EMPTY_CAPABILITIES
|
|
103864
|
+
const EMPTY_CAPABILITIES = createModelCapabilities({ optionDescriptors: [] });
|
|
103350
103865
|
const VERSION_PROBE_TIMEOUT_MS$1 = 4e3;
|
|
103351
103866
|
const GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
|
|
103352
103867
|
const GROK_BUILT_IN_MODELS = [{
|
|
103353
103868
|
slug: "grok-build",
|
|
103354
103869
|
name: "Grok Build",
|
|
103355
103870
|
isCustom: false,
|
|
103356
|
-
capabilities: EMPTY_CAPABILITIES
|
|
103871
|
+
capabilities: EMPTY_CAPABILITIES
|
|
103357
103872
|
}];
|
|
103358
103873
|
function buildInitialGrokProviderSnapshot(grokSettings) {
|
|
103359
103874
|
return Effect.gen(function* () {
|
|
@@ -103388,7 +103903,7 @@ function buildInitialGrokProviderSnapshot(grokSettings) {
|
|
|
103388
103903
|
});
|
|
103389
103904
|
}
|
|
103390
103905
|
function grokModelsFromSettings(customModels, builtInModels = GROK_BUILT_IN_MODELS) {
|
|
103391
|
-
return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES
|
|
103906
|
+
return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);
|
|
103392
103907
|
}
|
|
103393
103908
|
function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
|
|
103394
103909
|
if (!modelState || modelState.availableModels.length === 0) return [];
|
|
@@ -103401,7 +103916,7 @@ function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
|
|
|
103401
103916
|
slug,
|
|
103402
103917
|
name: model.name.trim() || slug,
|
|
103403
103918
|
isCustom: false,
|
|
103404
|
-
capabilities: EMPTY_CAPABILITIES
|
|
103919
|
+
capabilities: EMPTY_CAPABILITIES
|
|
103405
103920
|
};
|
|
103406
103921
|
}).filter((model) => model !== void 0);
|
|
103407
103922
|
}
|
|
@@ -103576,6 +104091,9 @@ const GrokDriver = {
|
|
|
103576
104091
|
const crypto = yield* Crypto.Crypto;
|
|
103577
104092
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
103578
104093
|
const httpClient = yield* HttpClient.HttpClient;
|
|
104094
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
104095
|
+
const path = yield* Path.Path;
|
|
104096
|
+
const serverConfig = yield* ServerConfig$1;
|
|
103579
104097
|
const serverSettings = yield* ServerSettingsService;
|
|
103580
104098
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
103581
104099
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
@@ -103604,7 +104122,12 @@ const GrokDriver = {
|
|
|
103604
104122
|
instanceId
|
|
103605
104123
|
});
|
|
103606
104124
|
const textGeneration = yield* makeGrokTextGeneration(effectiveConfig, processEnv);
|
|
103607
|
-
const checkProvider =
|
|
104125
|
+
const checkProvider = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => [])).pipe(Effect.flatMap((disabledSkills) => checkGrokProviderStatus(effectiveConfig, processEnv).pipe(Effect.flatMap((draft) => withProviderSkills(draft, {
|
|
104126
|
+
driver: DRIVER_KIND$2,
|
|
104127
|
+
cwd: serverConfig.cwd,
|
|
104128
|
+
environment: processEnv,
|
|
104129
|
+
disabledSkills
|
|
104130
|
+
})))), Effect.map(stampIdentity), Effect.provideService(Crypto.Crypto, crypto), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path));
|
|
103608
104131
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
103609
104132
|
const snapshot = yield* makeManagedServerProvider({
|
|
103610
104133
|
maintenanceCapabilities,
|
|
@@ -103640,68 +104163,21 @@ const GrokDriver = {
|
|
|
103640
104163
|
};
|
|
103641
104164
|
})
|
|
103642
104165
|
};
|
|
103643
|
-
/**
|
|
103644
|
-
* Build the argument vector for one turn.
|
|
103645
|
-
*
|
|
103646
|
-
* Approval policy is the load-bearing decision here. A headless Muse run has
|
|
103647
|
-
* no channel to answer an approval prompt — the CLI only offers
|
|
103648
|
-
* `--disable-approval` (keep the OS sandbox) and `--yolo` (drop approval and
|
|
103649
|
-
* the sandbox, and trust the workspace). P4Code owns the user-facing runtime
|
|
103650
|
-
* mode, so it maps onto those two postures:
|
|
103651
|
-
*
|
|
103652
|
-
* - `full-access` → `--yolo`
|
|
103653
|
-
* - everything else → `--disable-approval --trust-workspace`, leaving the
|
|
103654
|
-
* sandbox to contain what runs.
|
|
103655
|
-
*
|
|
103656
|
-
* `approval-required` cannot be honoured; the adapter emits a
|
|
103657
|
-
* `config.warning` for it rather than silently pretending otherwise.
|
|
103658
|
-
*/
|
|
103659
|
-
function buildMuseExecArgs(input) {
|
|
103660
|
-
const args = [
|
|
103661
|
-
"exec",
|
|
103662
|
-
"--json",
|
|
103663
|
-
"--session-id",
|
|
103664
|
-
input.sessionId
|
|
103665
|
-
];
|
|
103666
|
-
if (input.model) args.push("--model", input.model);
|
|
103667
|
-
if (input.reasoningEffort) args.push("--reasoning-effort", input.reasoningEffort);
|
|
103668
|
-
if (input.baseUrl) args.push("--base-url", input.baseUrl);
|
|
103669
|
-
if (input.runtimeMode === "full-access") args.push("--yolo");
|
|
103670
|
-
else {
|
|
103671
|
-
args.push("--disable-approval", "--trust-workspace");
|
|
103672
|
-
switch (input.sandboxMode) {
|
|
103673
|
-
case "danger-full-access":
|
|
103674
|
-
args.push("--disable-sandbox");
|
|
103675
|
-
break;
|
|
103676
|
-
case "read-only":
|
|
103677
|
-
args.push("--disable-write", "--disable-shell");
|
|
103678
|
-
break;
|
|
103679
|
-
default: break;
|
|
103680
|
-
}
|
|
103681
|
-
}
|
|
103682
|
-
for (const imagePath of input.imagePaths ?? []) args.push("--image", imagePath);
|
|
103683
|
-
args.push(input.prompt);
|
|
103684
|
-
return args;
|
|
103685
|
-
}
|
|
103686
|
-
/** `muse --version` argv, used by the provider status probe. */
|
|
103687
|
-
const MUSE_VERSION_ARGS = ["--version"];
|
|
103688
|
-
/**
|
|
103689
|
-
* Whether a runtime mode asks for interactive approvals that Muse Code
|
|
103690
|
-
* cannot deliver headlessly.
|
|
103691
|
-
*/
|
|
103692
|
-
function museApprovalsUnsupported(runtimeMode) {
|
|
103693
|
-
return runtimeMode === "approval-required";
|
|
103694
|
-
}
|
|
103695
104166
|
//#endregion
|
|
103696
104167
|
//#region src/provider/muse/MuseExecEvents.ts
|
|
103697
104168
|
/**
|
|
103698
104169
|
* MuseExecEvents — pure translation of `muse exec --json` JSONL records into
|
|
103699
104170
|
* canonical `ProviderRuntimeEvent`s.
|
|
103700
104171
|
*
|
|
103701
|
-
* Muse
|
|
103702
|
-
*
|
|
103703
|
-
*
|
|
103704
|
-
*
|
|
104172
|
+
* P4Code drives Muse through `muse exec --json`, which runs one prompt to
|
|
104173
|
+
* completion and prints one JSON object per line; multi-turn threads are
|
|
104174
|
+
* rebuilt by re-invoking with `--session-id`. Muse Code 1.1.1 also serves a
|
|
104175
|
+
* session host over stdio (`muse serve`, the MSP wire protocol, exported by
|
|
104176
|
+
* `muse schema`) with approval, user-input and steering methods that `exec`
|
|
104177
|
+
* has no channel for. Moving onto MSP is the way to lift those limits; the
|
|
104178
|
+
* mapping below stays on the `exec` stream until then.
|
|
104179
|
+
*
|
|
104180
|
+
* Record shapes here were re-verified against CLI 1.1.1-R2514.1.
|
|
103705
104181
|
*
|
|
103706
104182
|
* Everything here is a pure function over a caller-owned state record so the
|
|
103707
104183
|
* mapping can be tested against captured CLI output without spawning a
|
|
@@ -103995,21 +104471,19 @@ function toolCallIdFromIdempotencyKey(key) {
|
|
|
103995
104471
|
const callId = key.slice(5).trim();
|
|
103996
104472
|
return callId.length > 0 ? callId : void 0;
|
|
103997
104473
|
}
|
|
104474
|
+
/**
|
|
104475
|
+
* Muse's tool names, as the CLI itself reports them (Muse Code 1.1.1). Names
|
|
104476
|
+
* it does not have are not listed: an unknown tool still maps to
|
|
104477
|
+
* `dynamic_tool_call`, which is the right answer for the memory, goal, cron,
|
|
104478
|
+
* skill and todo tools that have no canonical item type here.
|
|
104479
|
+
*/
|
|
103998
104480
|
function itemTypeForToolName(toolName) {
|
|
103999
104481
|
switch (toolName) {
|
|
104000
104482
|
case "bash":
|
|
104001
|
-
case "
|
|
104002
|
-
case "run_command":
|
|
104003
|
-
case "unified_exec": return "command_execution";
|
|
104483
|
+
case "bash_input": return "command_execution";
|
|
104004
104484
|
case "write_file":
|
|
104005
|
-
case "edit_file":
|
|
104006
|
-
case "
|
|
104007
|
-
case "apply_patch":
|
|
104008
|
-
case "delete_file":
|
|
104009
|
-
case "move_file": return "file_change";
|
|
104010
|
-
case "web_search":
|
|
104011
|
-
case "web_fetch": return "web_search";
|
|
104012
|
-
case "view_image": return "image_view";
|
|
104485
|
+
case "edit_file": return "file_change";
|
|
104486
|
+
case "web_search": return "web_search";
|
|
104013
104487
|
default:
|
|
104014
104488
|
if (toolName?.startsWith("subagent_")) return "collab_agent_tool_call";
|
|
104015
104489
|
if (toolName?.startsWith("mcp__")) return "mcp_tool_call";
|
|
@@ -104220,16 +104694,174 @@ const makeMuseTextGeneration = (museSettings, environment = process.env) => Effe
|
|
|
104220
104694
|
})
|
|
104221
104695
|
};
|
|
104222
104696
|
});
|
|
104697
|
+
/** The one file the overlay owns; everything else links back to the source. */
|
|
104698
|
+
const MUSE_SETTINGS_FILENAME = "settings.json";
|
|
104699
|
+
/** The credential the launcher and the binary both read through this variable. */
|
|
104700
|
+
const MUSE_AUTH_FILENAME = "auth.json";
|
|
104701
|
+
/** The config subdirectory Muse keeps under an XDG config home. */
|
|
104702
|
+
const MUSE_CONFIG_DIRNAME = "muse";
|
|
104703
|
+
/**
|
|
104704
|
+
* The generated settings file carries this session's bearer token, so the
|
|
104705
|
+
* overlay is private to the account running the server.
|
|
104706
|
+
*/
|
|
104707
|
+
const OVERLAY_DIRECTORY_MODE = 448;
|
|
104708
|
+
const OVERLAY_SETTINGS_MODE = 384;
|
|
104709
|
+
/**
|
|
104710
|
+
* Registered servers as Muse settings entries.
|
|
104711
|
+
*
|
|
104712
|
+
* SSE has no spelling here — Muse reads an entry carrying `type: "sse"` as no
|
|
104713
|
+
* entry at all — so those are reported as skipped instead of written and
|
|
104714
|
+
* quietly ignored. The reserved names are dropped for the reason every other
|
|
104715
|
+
* adapter drops them: a registration called `p4-code` must not displace the
|
|
104716
|
+
* built-in one and take the session's own tools with it.
|
|
104717
|
+
*/
|
|
104718
|
+
function toMuseMcpServers(resolved, reservedNames) {
|
|
104719
|
+
const entries = {};
|
|
104720
|
+
const skipped = [];
|
|
104721
|
+
for (const [name, server] of Object.entries(resolved)) {
|
|
104722
|
+
if (reservedNames.has(name)) continue;
|
|
104723
|
+
if (server.type === "stdio") {
|
|
104724
|
+
entries[name] = {
|
|
104725
|
+
type: "stdio",
|
|
104726
|
+
command: server.command,
|
|
104727
|
+
...server.args.length > 0 ? { args: [...server.args] } : {},
|
|
104728
|
+
...Object.keys(server.env).length > 0 ? { env: { ...server.env } } : {}
|
|
104729
|
+
};
|
|
104730
|
+
continue;
|
|
104731
|
+
}
|
|
104732
|
+
if (server.type === "sse") {
|
|
104733
|
+
skipped.push(name);
|
|
104734
|
+
continue;
|
|
104735
|
+
}
|
|
104736
|
+
entries[name] = {
|
|
104737
|
+
type: "http",
|
|
104738
|
+
url: server.url,
|
|
104739
|
+
...Object.keys(server.headers).length > 0 ? { headers: { ...server.headers } } : {}
|
|
104740
|
+
};
|
|
104741
|
+
}
|
|
104742
|
+
return {
|
|
104743
|
+
entries,
|
|
104744
|
+
skipped
|
|
104745
|
+
};
|
|
104746
|
+
}
|
|
104747
|
+
/**
|
|
104748
|
+
* The config home a CLI following the XDG convention reads, matching what the
|
|
104749
|
+
* spawned process would see: its own `XDG_CONFIG_HOME` when set, else
|
|
104750
|
+
* `~/.config` under the environment's own `HOME`.
|
|
104751
|
+
*/
|
|
104752
|
+
function resolveXdgConfigHome(environment) {
|
|
104753
|
+
const configured = environment.XDG_CONFIG_HOME?.trim() ?? "";
|
|
104754
|
+
if (configured.length > 0) return configured;
|
|
104755
|
+
const home = environment.HOME?.trim() ?? "";
|
|
104756
|
+
return `${home.length > 0 ? home : NodeOS.homedir()}/.config`;
|
|
104757
|
+
}
|
|
104758
|
+
/**
|
|
104759
|
+
* The settings document as this module needs to see it: the one key it owns,
|
|
104760
|
+
* with every other key carried through untouched, so an overlay never quietly
|
|
104761
|
+
* drops a model default or a TUI preference the user set.
|
|
104762
|
+
*/
|
|
104763
|
+
const MuseSettingsFile = Schema$1.StructWithRest(Schema$1.Struct({
|
|
104764
|
+
schema_version: Schema$1.optional(Schema$1.Number),
|
|
104765
|
+
mcpServers: Schema$1.optional(Schema$1.Unknown)
|
|
104766
|
+
}), [Schema$1.Record(Schema$1.String, Schema$1.Unknown)]);
|
|
104767
|
+
const MuseSettingsFileFromJson = Schema$1.fromJsonString(MuseSettingsFile);
|
|
104768
|
+
const decodeSettingsFile = Schema$1.decodeUnknownExit(MuseSettingsFileFromJson);
|
|
104769
|
+
const encodeSettingsFile = Schema$1.encodeSync(MuseSettingsFileFromJson);
|
|
104770
|
+
/** The user's settings document, or an empty one when there is nothing usable to read. */
|
|
104771
|
+
const readSourceSettings = Effect.fnUntraced(function* (settingsPath) {
|
|
104772
|
+
const contents = yield* (yield* FileSystem.FileSystem).readFileString(settingsPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
104773
|
+
if (contents === void 0) return {};
|
|
104774
|
+
const decoded = decodeSettingsFile(contents);
|
|
104775
|
+
return decoded._tag === "Success" ? decoded.value : {};
|
|
104776
|
+
});
|
|
104777
|
+
/**
|
|
104778
|
+
* Prepare the overlay and return the environment a turn must add to reach it.
|
|
104779
|
+
*
|
|
104780
|
+
* Rebuilt every turn: the endpoint and the bearer token are per session, and a
|
|
104781
|
+
* stale settings file would point the CLI at a credential that no longer
|
|
104782
|
+
* authorizes anything.
|
|
104783
|
+
*/
|
|
104784
|
+
const prepareMuseConfigOverlay = Effect.fn("prepareMuseConfigOverlay")(function* (input) {
|
|
104785
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
104786
|
+
const path = yield* Path.Path;
|
|
104787
|
+
const sourceMuseDir = path.join(input.sourceConfigHome, MUSE_CONFIG_DIRNAME);
|
|
104788
|
+
const overlayMuseDir = path.join(input.overlayRoot, MUSE_CONFIG_DIRNAME);
|
|
104789
|
+
yield* fileSystem.makeDirectory(overlayMuseDir, {
|
|
104790
|
+
recursive: true,
|
|
104791
|
+
mode: OVERLAY_DIRECTORY_MODE
|
|
104792
|
+
});
|
|
104793
|
+
yield* fileSystem.chmod(input.overlayRoot, OVERLAY_DIRECTORY_MODE).pipe(Effect.ignore);
|
|
104794
|
+
yield* fileSystem.chmod(overlayMuseDir, OVERLAY_DIRECTORY_MODE).pipe(Effect.ignore);
|
|
104795
|
+
const sourceEntries = yield* fileSystem.readDirectory(sourceMuseDir).pipe(Effect.orElseSucceed(() => []));
|
|
104796
|
+
for (const entry of sourceEntries) {
|
|
104797
|
+
if (entry === MUSE_SETTINGS_FILENAME) continue;
|
|
104798
|
+
const link = path.join(overlayMuseDir, entry);
|
|
104799
|
+
if (!(yield* fileSystem.readLink(link).pipe(Effect.as(true), Effect.orElseSucceed(() => false))) && (yield* fileSystem.exists(link))) continue;
|
|
104800
|
+
yield* fileSystem.remove(link, {
|
|
104801
|
+
recursive: true,
|
|
104802
|
+
force: true
|
|
104803
|
+
});
|
|
104804
|
+
yield* fileSystem.symlink(path.join(sourceMuseDir, entry), link).pipe(Effect.ignore);
|
|
104805
|
+
}
|
|
104806
|
+
const sourceSettings = yield* readSourceSettings(path.join(sourceMuseDir, MUSE_SETTINGS_FILENAME));
|
|
104807
|
+
const sourceServers = sourceSettings.mcpServers;
|
|
104808
|
+
const mergedServers = typeof sourceServers === "object" && sourceServers !== null && !Array.isArray(sourceServers) ? { ...sourceServers } : {};
|
|
104809
|
+
const external = toMuseMcpServers(input.externalServers ?? {}, /* @__PURE__ */ new Set([input.mcpServer.name]));
|
|
104810
|
+
Object.assign(mergedServers, external.entries);
|
|
104811
|
+
mergedServers[input.mcpServer.name] = {
|
|
104812
|
+
type: "http",
|
|
104813
|
+
url: input.mcpServer.url,
|
|
104814
|
+
headers: { Authorization: input.mcpServer.authorizationHeader }
|
|
104815
|
+
};
|
|
104816
|
+
const settings = {
|
|
104817
|
+
...sourceSettings,
|
|
104818
|
+
schema_version: sourceSettings.schema_version ?? 1,
|
|
104819
|
+
mcpServers: mergedServers
|
|
104820
|
+
};
|
|
104821
|
+
const settingsPath = path.join(overlayMuseDir, MUSE_SETTINGS_FILENAME);
|
|
104822
|
+
yield* fileSystem.writeFileString(settingsPath, `${encodeSettingsFile(settings)}\n`, { mode: OVERLAY_SETTINGS_MODE });
|
|
104823
|
+
yield* fileSystem.chmod(settingsPath, OVERLAY_SETTINGS_MODE).pipe(Effect.ignore);
|
|
104824
|
+
return {
|
|
104825
|
+
skippedServers: external.skipped,
|
|
104826
|
+
environment: {
|
|
104827
|
+
XDG_CONFIG_HOME: input.overlayRoot,
|
|
104828
|
+
MUSE_AUTH_PATH: path.join(sourceMuseDir, MUSE_AUTH_FILENAME)
|
|
104829
|
+
}
|
|
104830
|
+
};
|
|
104831
|
+
});
|
|
104832
|
+
/**
|
|
104833
|
+
* Drop what the overlay generated, and nothing else.
|
|
104834
|
+
*
|
|
104835
|
+
* The links are P4Code's to remove; a real file under the overlay is something
|
|
104836
|
+
* Muse wrote during the session — a trust decision, an OAuth token — and it
|
|
104837
|
+
* stays, so the next session on this thread still has it. The directory goes
|
|
104838
|
+
* only when removing the generated file and the links leaves it empty.
|
|
104839
|
+
*/
|
|
104840
|
+
const clearMuseConfigOverlay = Effect.fn("clearMuseConfigOverlay")(function* (overlayRoot) {
|
|
104841
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
104842
|
+
const path = yield* Path.Path;
|
|
104843
|
+
const overlayMuseDir = path.join(overlayRoot, MUSE_CONFIG_DIRNAME);
|
|
104844
|
+
yield* fileSystem.remove(path.join(overlayMuseDir, MUSE_SETTINGS_FILENAME), { force: true }).pipe(Effect.ignore);
|
|
104845
|
+
const entries = yield* fileSystem.readDirectory(overlayMuseDir).pipe(Effect.orElseSucceed(() => []));
|
|
104846
|
+
for (const entry of entries) {
|
|
104847
|
+
const entryPath = path.join(overlayMuseDir, entry);
|
|
104848
|
+
if (!(yield* fileSystem.readLink(entryPath).pipe(Effect.as(true), Effect.orElseSucceed(() => false)))) continue;
|
|
104849
|
+
yield* fileSystem.remove(entryPath, { force: true }).pipe(Effect.ignore);
|
|
104850
|
+
}
|
|
104851
|
+
yield* fileSystem.remove(overlayMuseDir).pipe(Effect.ignore);
|
|
104852
|
+
yield* fileSystem.remove(overlayRoot).pipe(Effect.ignore);
|
|
104853
|
+
});
|
|
104223
104854
|
//#endregion
|
|
104224
104855
|
//#region src/provider/muse/MuseExecRuntime.ts
|
|
104225
104856
|
/**
|
|
104226
104857
|
* MuseExecRuntime — one `muse exec --json` child process per turn.
|
|
104227
104858
|
*
|
|
104228
|
-
*
|
|
104229
|
-
*
|
|
104230
|
-
*
|
|
104231
|
-
*
|
|
104232
|
-
*
|
|
104859
|
+
* On the `exec` path a turn is a process, and the thread is rebuilt from the
|
|
104860
|
+
* CLI's own event-sourced session log when the next turn passes
|
|
104861
|
+
* `--session-id`. (Muse Code 1.1.1 does have a long-lived surface —
|
|
104862
|
+
* `muse serve`, the MSP session host — which this driver does not use yet.)
|
|
104863
|
+
* This module owns spawning, line framing, interruption, and exit
|
|
104864
|
+
* classification; it knows nothing about canonical runtime events.
|
|
104233
104865
|
*
|
|
104234
104866
|
* @module provider/muse/MuseExecRuntime
|
|
104235
104867
|
*/
|
|
@@ -104473,11 +105105,12 @@ function nonNegativeInt(value) {
|
|
|
104473
105105
|
*
|
|
104474
105106
|
* Consequences that are visible to users, and deliberate:
|
|
104475
105107
|
*
|
|
104476
|
-
* - **No interactive approvals.**
|
|
104477
|
-
* approval prompt
|
|
104478
|
-
*
|
|
104479
|
-
*
|
|
104480
|
-
* a
|
|
105108
|
+
* - **No interactive approvals.** `muse exec` has no channel to answer an
|
|
105109
|
+
* approval prompt — Muse Code 1.1.1 serves approvals over MSP
|
|
105110
|
+
* (`muse serve`), which this adapter does not speak — so the adapter runs
|
|
105111
|
+
* with approval disabled and lets Muse's OS sandbox do the containing.
|
|
105112
|
+
* `respondToRequest` fails rather than pretending to route a decision, and
|
|
105113
|
+
* `approval-required` threads get a `config.warning`.
|
|
104481
105114
|
* - **No provider-side rollback.** Muse can fork a session interactively but
|
|
104482
105115
|
* exposes nothing headless.
|
|
104483
105116
|
* - **Tool arguments are not streamed.** They exist only in the on-disk
|
|
@@ -104510,6 +105143,11 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104510
105143
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
104511
105144
|
const path = yield* Path.Path;
|
|
104512
105145
|
const serverConfig = yield* Effect.service(ServerConfig$1);
|
|
105146
|
+
/**
|
|
105147
|
+
* Where a thread's generated Muse configuration lives. One directory per
|
|
105148
|
+
* thread because the bearer token inside it is per session.
|
|
105149
|
+
*/
|
|
105150
|
+
const overlayRootFor = (threadId) => path.join(serverConfig.stateDir, "muse", "config", threadId);
|
|
104513
105151
|
const sessions = /* @__PURE__ */ new Map();
|
|
104514
105152
|
const runtimeEventPubSub = yield* PubSub.unbounded();
|
|
104515
105153
|
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
@@ -104617,14 +105255,36 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104617
105255
|
}
|
|
104618
105256
|
const turnId = TurnId.make(yield* randomUUIDv4);
|
|
104619
105257
|
const model = input.modelSelection?.model ?? ctx.session.model;
|
|
105258
|
+
const reasoningEffort = input.modelSelection?.instanceId === boundInstanceId ? museReasoningEffort(getModelSelectionStringOptionValue(input.modelSelection, "reasoningEffort")) : void 0;
|
|
104620
105259
|
const args = buildMuseExecArgs({
|
|
104621
105260
|
sessionId: ctx.museSessionId,
|
|
104622
105261
|
prompt,
|
|
104623
105262
|
model,
|
|
105263
|
+
reasoningEffort,
|
|
104624
105264
|
baseUrl: museSettings.baseUrl || void 0,
|
|
104625
105265
|
runtimeMode: ctx.session.runtimeMode,
|
|
104626
105266
|
imagePaths
|
|
104627
105267
|
});
|
|
105268
|
+
const mcpSession = readMcpProviderSession(input.threadId);
|
|
105269
|
+
const overlay = mcpSession ? yield* prepareMuseConfigOverlay({
|
|
105270
|
+
overlayRoot: overlayRootFor(input.threadId),
|
|
105271
|
+
sourceConfigHome: resolveXdgConfigHome(environment),
|
|
105272
|
+
mcpServer: {
|
|
105273
|
+
name: P4CODE_MCP_SERVER_NAME,
|
|
105274
|
+
url: mcpSession.endpoint,
|
|
105275
|
+
authorizationHeader: mcpSession.authorizationHeader
|
|
105276
|
+
},
|
|
105277
|
+
externalServers: options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers
|
|
105278
|
+
}).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.orElseSucceed(() => void 0)) : void 0;
|
|
105279
|
+
if (overlay && overlay.skippedServers.length > 0) yield* Effect.logWarning("Muse cannot express these MCP servers", {
|
|
105280
|
+
threadId: input.threadId,
|
|
105281
|
+
servers: overlay.skippedServers,
|
|
105282
|
+
transport: "sse"
|
|
105283
|
+
});
|
|
105284
|
+
const turnEnvironment = {
|
|
105285
|
+
...environment,
|
|
105286
|
+
...overlay?.environment
|
|
105287
|
+
};
|
|
104628
105288
|
const state = makeMuseExecTurnState();
|
|
104629
105289
|
const mappingContext = {
|
|
104630
105290
|
provider: PROVIDER$1,
|
|
@@ -104655,54 +105315,74 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104655
105315
|
...model ? { model } : {}
|
|
104656
105316
|
};
|
|
104657
105317
|
const turnScope = yield* Scope.make();
|
|
104658
|
-
|
|
105318
|
+
let runStarted = false;
|
|
105319
|
+
let settled = false;
|
|
105320
|
+
let outcome;
|
|
105321
|
+
/**
|
|
105322
|
+
* Release everything the turn holds, exactly once, whatever ended it:
|
|
105323
|
+
* a clean exit, a typed failure, a defect, or an interrupt from a
|
|
105324
|
+
* stopped thread. Leaving `activeTurn` or a `running` session behind
|
|
105325
|
+
* would wedge the thread — every later `sendTurn` would refuse with
|
|
105326
|
+
* "a Muse turn is already running" until the server restarted — so
|
|
105327
|
+
* this runs from an `ensuring` finalizer rather than the happy path.
|
|
105328
|
+
*/
|
|
105329
|
+
const finishTurn = Effect.suspend(() => {
|
|
105330
|
+
if (settled) return Effect.void;
|
|
105331
|
+
settled = true;
|
|
105332
|
+
return Effect.gen(function* () {
|
|
105333
|
+
ctx.activeTurn = void 0;
|
|
105334
|
+
yield* Scope.close(turnScope, Exit.void);
|
|
105335
|
+
if (runStarted && state.terminal === void 0) if (outcome === void 0 || outcome.kind === "interrupted") yield* publish({
|
|
105336
|
+
type: "turn.aborted",
|
|
105337
|
+
...yield* makeEventStamp(),
|
|
105338
|
+
provider: PROVIDER$1,
|
|
105339
|
+
providerInstanceId: boundInstanceId,
|
|
105340
|
+
threadId: input.threadId,
|
|
105341
|
+
turnId,
|
|
105342
|
+
payload: { reason: "interrupted" }
|
|
105343
|
+
});
|
|
105344
|
+
else {
|
|
105345
|
+
const detail = outcome.kind === "failed" ? outcome.stderr || `\`muse exec\` exited with code ${outcome.exitCode}.` : "`muse exec` ended without a terminal event.";
|
|
105346
|
+
yield* publish({
|
|
105347
|
+
type: "turn.completed",
|
|
105348
|
+
...yield* makeEventStamp(),
|
|
105349
|
+
provider: PROVIDER$1,
|
|
105350
|
+
providerInstanceId: boundInstanceId,
|
|
105351
|
+
threadId: input.threadId,
|
|
105352
|
+
turnId,
|
|
105353
|
+
payload: {
|
|
105354
|
+
state: "failed",
|
|
105355
|
+
errorMessage: detail
|
|
105356
|
+
}
|
|
105357
|
+
});
|
|
105358
|
+
}
|
|
105359
|
+
if (runStarted) yield* publishTokenUsage(ctx, turnId);
|
|
105360
|
+
const updatedAt = yield* nowIso;
|
|
105361
|
+
const { activeTurnId: _activeTurnId, ...readySession } = ctx.session;
|
|
105362
|
+
ctx.session = {
|
|
105363
|
+
...readySession,
|
|
105364
|
+
status: "ready",
|
|
105365
|
+
updatedAt
|
|
105366
|
+
};
|
|
105367
|
+
});
|
|
105368
|
+
}).pipe(Effect.ignore);
|
|
105369
|
+
yield* Effect.acquireUseRelease(runMuseExec({
|
|
104659
105370
|
threadId: input.threadId,
|
|
104660
105371
|
binaryPath: museSettings.binaryPath || "muse",
|
|
104661
105372
|
args,
|
|
104662
105373
|
cwd: ctx.cwd,
|
|
104663
|
-
environment,
|
|
105374
|
+
environment: turnEnvironment,
|
|
104664
105375
|
onLine: handleLine
|
|
104665
|
-
}).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() =>
|
|
104666
|
-
|
|
104667
|
-
|
|
104668
|
-
run,
|
|
104669
|
-
state
|
|
104670
|
-
};
|
|
104671
|
-
const outcome = yield* run.awaitOutcome;
|
|
104672
|
-
yield* Scope.close(turnScope, Exit.void);
|
|
104673
|
-
ctx.activeTurn = void 0;
|
|
104674
|
-
if (state.terminal === void 0) if (outcome.kind === "interrupted") yield* publish({
|
|
104675
|
-
type: "turn.aborted",
|
|
104676
|
-
...yield* makeEventStamp(),
|
|
104677
|
-
provider: PROVIDER$1,
|
|
104678
|
-
providerInstanceId: boundInstanceId,
|
|
104679
|
-
threadId: input.threadId,
|
|
104680
|
-
turnId,
|
|
104681
|
-
payload: { reason: "interrupted" }
|
|
104682
|
-
});
|
|
104683
|
-
else {
|
|
104684
|
-
const detail = outcome.kind === "failed" ? outcome.stderr || `\`muse exec\` exited with code ${outcome.exitCode}.` : "`muse exec` ended without a terminal event.";
|
|
104685
|
-
yield* publish({
|
|
104686
|
-
type: "turn.completed",
|
|
104687
|
-
...yield* makeEventStamp(),
|
|
104688
|
-
provider: PROVIDER$1,
|
|
104689
|
-
providerInstanceId: boundInstanceId,
|
|
104690
|
-
threadId: input.threadId,
|
|
105376
|
+
}).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() => finishTurn), Effect.tap((run) => Effect.sync(() => {
|
|
105377
|
+
runStarted = true;
|
|
105378
|
+
ctx.activeTurn = {
|
|
104691
105379
|
turnId,
|
|
104692
|
-
|
|
104693
|
-
|
|
104694
|
-
|
|
104695
|
-
|
|
104696
|
-
|
|
104697
|
-
}
|
|
104698
|
-
yield* publishTokenUsage(ctx, turnId);
|
|
104699
|
-
const updatedAt = yield* nowIso;
|
|
104700
|
-
const { activeTurnId: _activeTurnId, ...readySession } = ctx.session;
|
|
104701
|
-
ctx.session = {
|
|
104702
|
-
...readySession,
|
|
104703
|
-
status: "ready",
|
|
104704
|
-
updatedAt
|
|
104705
|
-
};
|
|
105380
|
+
run,
|
|
105381
|
+
state
|
|
105382
|
+
};
|
|
105383
|
+
}))), (run) => Effect.gen(function* () {
|
|
105384
|
+
outcome = yield* run.awaitOutcome;
|
|
105385
|
+
}), () => finishTurn);
|
|
104706
105386
|
return {
|
|
104707
105387
|
threadId: input.threadId,
|
|
104708
105388
|
turnId,
|
|
@@ -104777,6 +105457,7 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104777
105457
|
yield* ctx.activeTurn.run.interrupt.pipe(Effect.ignore);
|
|
104778
105458
|
ctx.activeTurn = void 0;
|
|
104779
105459
|
}
|
|
105460
|
+
yield* clearMuseConfigOverlay(overlayRootFor(ctx.threadId)).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.ignore);
|
|
104780
105461
|
const updatedAt = yield* nowIso;
|
|
104781
105462
|
ctx.session = {
|
|
104782
105463
|
...ctx.session,
|
|
@@ -104831,7 +105512,17 @@ const MUSE_PRESENTATION = {
|
|
|
104831
105512
|
requiresNewThreadForModelChange: false,
|
|
104832
105513
|
supportedRuntimeModes: ["auto", "full-access"]
|
|
104833
105514
|
};
|
|
104834
|
-
const
|
|
105515
|
+
const MUSE_CAPABILITIES = createModelCapabilities({ optionDescriptors: [{
|
|
105516
|
+
id: "reasoningEffort",
|
|
105517
|
+
label: "Reasoning",
|
|
105518
|
+
type: "select",
|
|
105519
|
+
options: MUSE_REASONING_EFFORTS.map((effort) => ({
|
|
105520
|
+
id: effort,
|
|
105521
|
+
label: effort.charAt(0).toUpperCase() + effort.slice(1),
|
|
105522
|
+
...effort === "high" ? { isDefault: true } : {}
|
|
105523
|
+
})),
|
|
105524
|
+
currentValue: MUSE_DEFAULT_REASONING_EFFORT
|
|
105525
|
+
}] });
|
|
104835
105526
|
const VERSION_PROBE_TIMEOUT_MS = 4e3;
|
|
104836
105527
|
/** Models Meta documents for Muse Code; used until the CLI caches a catalog. */
|
|
104837
105528
|
const MUSE_BUILT_IN_MODELS = [
|
|
@@ -104839,30 +105530,30 @@ const MUSE_BUILT_IN_MODELS = [
|
|
|
104839
105530
|
slug: "muse-spark-1.3",
|
|
104840
105531
|
name: "Muse Spark 1.3",
|
|
104841
105532
|
isCustom: false,
|
|
104842
|
-
capabilities:
|
|
105533
|
+
capabilities: MUSE_CAPABILITIES
|
|
104843
105534
|
},
|
|
104844
105535
|
{
|
|
104845
105536
|
slug: "muse-spark-1.3-contributor",
|
|
104846
105537
|
name: "Muse Spark 1.3 (contributor)",
|
|
104847
105538
|
isCustom: false,
|
|
104848
105539
|
isDefault: true,
|
|
104849
|
-
capabilities:
|
|
105540
|
+
capabilities: MUSE_CAPABILITIES
|
|
104850
105541
|
},
|
|
104851
105542
|
{
|
|
104852
105543
|
slug: "muse-spark-1.2",
|
|
104853
105544
|
name: "Muse Spark 1.2",
|
|
104854
105545
|
isCustom: false,
|
|
104855
|
-
capabilities:
|
|
105546
|
+
capabilities: MUSE_CAPABILITIES
|
|
104856
105547
|
},
|
|
104857
105548
|
{
|
|
104858
105549
|
slug: "muse-spark-1.2-contributor",
|
|
104859
105550
|
name: "Muse Spark 1.2 (contributor)",
|
|
104860
105551
|
isCustom: false,
|
|
104861
|
-
capabilities:
|
|
105552
|
+
capabilities: MUSE_CAPABILITIES
|
|
104862
105553
|
}
|
|
104863
105554
|
];
|
|
104864
105555
|
function museModelsFromSettings(customModels, builtInModels = MUSE_BUILT_IN_MODELS) {
|
|
104865
|
-
return providerModelsFromSettings(builtInModels, customModels ?? [],
|
|
105556
|
+
return providerModelsFromSettings(builtInModels, customModels ?? [], MUSE_CAPABILITIES);
|
|
104866
105557
|
}
|
|
104867
105558
|
const discoverMuseModels = Effect.fn("discoverMuseModels")(function* (environment) {
|
|
104868
105559
|
return (yield* readMuseModelCatalog(environment)).map((model) => ({
|
|
@@ -104870,7 +105561,7 @@ const discoverMuseModels = Effect.fn("discoverMuseModels")(function* (environmen
|
|
|
104870
105561
|
name: model.name,
|
|
104871
105562
|
isCustom: false,
|
|
104872
105563
|
...model.isDefault ? { isDefault: true } : {},
|
|
104873
|
-
capabilities:
|
|
105564
|
+
capabilities: MUSE_CAPABILITIES
|
|
104874
105565
|
}));
|
|
104875
105566
|
});
|
|
104876
105567
|
/**
|
|
@@ -105066,6 +105757,7 @@ const MuseDriver = {
|
|
|
105066
105757
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
105067
105758
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
105068
105759
|
const path = yield* Path.Path;
|
|
105760
|
+
const serverConfig = yield* ServerConfig$1;
|
|
105069
105761
|
const serverSettings = yield* ServerSettingsService;
|
|
105070
105762
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
105071
105763
|
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
@@ -105088,9 +105780,16 @@ const MuseDriver = {
|
|
|
105088
105780
|
});
|
|
105089
105781
|
const adapter = yield* makeMuseAdapter(effectiveConfig, {
|
|
105090
105782
|
environment: processEnv,
|
|
105091
|
-
instanceId
|
|
105783
|
+
instanceId,
|
|
105784
|
+
resolveMcpServers: (yield* McpRegistry).resolveForSession
|
|
105092
105785
|
});
|
|
105093
105786
|
const textGeneration = yield* makeMuseTextGeneration(effectiveConfig, processEnv);
|
|
105787
|
+
const checkProvider = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => [])).pipe(Effect.flatMap((disabledSkills) => checkMuseProviderStatus(effectiveConfig, processEnv).pipe(Effect.flatMap((draft) => withProviderSkills(draft, {
|
|
105788
|
+
driver: DRIVER_KIND$1,
|
|
105789
|
+
cwd: serverConfig.cwd,
|
|
105790
|
+
environment: processEnv,
|
|
105791
|
+
disabledSkills
|
|
105792
|
+
})))));
|
|
105094
105793
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
105095
105794
|
const snapshot = yield* makeManagedServerProvider({
|
|
105096
105795
|
maintenanceCapabilities,
|
|
@@ -105098,7 +105797,7 @@ const MuseDriver = {
|
|
|
105098
105797
|
streamSettings: snapshotSettings.streamSettings,
|
|
105099
105798
|
haveSettingsChanged: haveProviderSnapshotSettingsChanged,
|
|
105100
105799
|
initialSnapshot: (settings) => buildInitialMuseProviderSnapshot(settings.provider).pipe(Effect.map(stampIdentity)),
|
|
105101
|
-
checkProvider:
|
|
105800
|
+
checkProvider: checkProvider.pipe(Effect.map(stampIdentity), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path)),
|
|
105102
105801
|
enrichSnapshot: ({ settings, snapshot: currentSnapshot, publishSnapshot }) => enrichMuseSnapshot({
|
|
105103
105802
|
snapshot: currentSnapshot,
|
|
105104
105803
|
maintenanceCapabilities,
|
|
@@ -106849,6 +107548,8 @@ const BUILT_IN_DRIVERS = [
|
|
|
106849
107548
|
defaultConfig: () => decodeOpenCodeSettings({}),
|
|
106850
107549
|
create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () {
|
|
106851
107550
|
const openCodeRuntime = yield* OpenCodeRuntime;
|
|
107551
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
107552
|
+
const path = yield* Path.Path;
|
|
106852
107553
|
const serverConfig = yield* ServerConfig$1;
|
|
106853
107554
|
const httpClient = yield* HttpClient.HttpClient;
|
|
106854
107555
|
const serverSettings = yield* ServerSettingsService;
|
|
@@ -106879,7 +107580,12 @@ const BUILT_IN_DRIVERS = [
|
|
|
106879
107580
|
...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
|
|
106880
107581
|
});
|
|
106881
107582
|
const textGeneration = yield* makeOpenCodeTextGeneration(effectiveConfig, processEnv);
|
|
106882
|
-
const checkProvider = checkOpenCodeProviderStatus(effectiveConfig, serverConfig.cwd, processEnv).pipe(Effect.
|
|
107583
|
+
const checkProvider = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => [])).pipe(Effect.flatMap((disabledSkills) => checkOpenCodeProviderStatus(effectiveConfig, serverConfig.cwd, processEnv).pipe(Effect.flatMap((draft) => withProviderSkills(draft, {
|
|
107584
|
+
driver: DRIVER_KIND,
|
|
107585
|
+
cwd: serverConfig.cwd,
|
|
107586
|
+
environment: processEnv,
|
|
107587
|
+
disabledSkills
|
|
107588
|
+
})))), Effect.map(stampIdentity), Effect.provideService(OpenCodeRuntime, openCodeRuntime), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path));
|
|
106883
107589
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
106884
107590
|
const snapshot = yield* makeManagedServerProvider({
|
|
106885
107591
|
maintenanceCapabilities,
|
|
@@ -111039,7 +111745,13 @@ const make$4 = Effect.gen(function* () {
|
|
|
111039
111745
|
projects: project ? [project] : [],
|
|
111040
111746
|
chatWorkspaceDir: serverConfig.chatWorkspaceDir
|
|
111041
111747
|
});
|
|
111042
|
-
|
|
111748
|
+
const [sharedUserDir, sharedProjectDir] = yield* sharedSkillDirectories(cwd ?? void 0);
|
|
111749
|
+
return [
|
|
111750
|
+
...sharedUserDir !== void 0 && sharedUserDir !== userSkillsDir ? [sharedUserDir] : [],
|
|
111751
|
+
userSkillsDir,
|
|
111752
|
+
...sharedProjectDir === void 0 ? [] : [sharedProjectDir],
|
|
111753
|
+
...cwd ? [path.join(cwd, ".claude", "skills")] : []
|
|
111754
|
+
];
|
|
111043
111755
|
});
|
|
111044
111756
|
const resolveThread = Effect.fnUntraced(function* (threadId) {
|
|
111045
111757
|
return yield* projectionSnapshotQuery.getThreadDetailById(threadId).pipe(Effect.map(Option.getOrUndefined));
|