@p4code/cli 0.4.13 → 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 +786 -143
- package/dist/client/assets/{DiffPanel-DWdPV3K3.js → DiffPanel-MM5YsTnU.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-Djvz8ttF.js → FilePreviewPanel-iQlsalIt.js} +2 -2
- package/dist/client/assets/{PreviewPanel-DbeIwgC3.js → PreviewPanel-Bf3g60N9.js} +2 -2
- package/dist/client/assets/{PullRequestCodeTab-cFyorjex.js → PullRequestCodeTab-CN7XcZJK.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-Ddmzci4v.js → fileCommentAnnotations-84VIRBlS.js} +2 -2
- package/dist/client/assets/{index-CBqRo4NV.js → index-C1TVFsV3.js} +12 -6
- package/dist/client/assets/{renderFileChildren--IZ7ujV7.js → renderFileChildren-C6gKA6Vk.js} +2 -2
- package/dist/client/assets/{toggle-group-U3M0YUkk.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,
|
|
@@ -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,
|
|
@@ -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,90 +104163,6 @@ const GrokDriver = {
|
|
|
103640
104163
|
};
|
|
103641
104164
|
})
|
|
103642
104165
|
};
|
|
103643
|
-
/**
|
|
103644
|
-
* Reasoning levels `muse exec --reasoning-effort` accepts, in the CLI's own
|
|
103645
|
-
* order (`muse exec --help`, Muse Code 1.1.1). `high` is the CLI default, so
|
|
103646
|
-
* it is the default here too.
|
|
103647
|
-
*/
|
|
103648
|
-
const MUSE_REASONING_EFFORTS = [
|
|
103649
|
-
"none",
|
|
103650
|
-
"minimal",
|
|
103651
|
-
"low",
|
|
103652
|
-
"medium",
|
|
103653
|
-
"high",
|
|
103654
|
-
"xhigh",
|
|
103655
|
-
"max",
|
|
103656
|
-
"ultra"
|
|
103657
|
-
];
|
|
103658
|
-
const MUSE_DEFAULT_REASONING_EFFORT = "high";
|
|
103659
|
-
/**
|
|
103660
|
-
* Keep a selection Muse cannot parse out of the argv. `reasoningEffort` is a
|
|
103661
|
-
* shared option id — Codex publishes one too — so a selection left over from
|
|
103662
|
-
* another provider can reach this driver, and an unknown level makes
|
|
103663
|
-
* `muse exec` reject the whole invocation.
|
|
103664
|
-
*/
|
|
103665
|
-
function museReasoningEffort(value) {
|
|
103666
|
-
return value !== void 0 && MUSE_REASONING_EFFORTS.includes(value) ? value : void 0;
|
|
103667
|
-
}
|
|
103668
|
-
/**
|
|
103669
|
-
* Build the argument vector for one turn.
|
|
103670
|
-
*
|
|
103671
|
-
* Approval policy is the load-bearing decision here. Muse Code 1.1.1 does
|
|
103672
|
-
* express approval as a policy — `--approval-mode untrusted|on-request|never`,
|
|
103673
|
-
* plus named `--permission-profile` documents — but a headless `exec` run
|
|
103674
|
-
* still has no channel to answer a prompt: approvals are served over MSP
|
|
103675
|
-
* (`muse serve`), not on the `exec` stream. So the only two postures `exec`
|
|
103676
|
-
* can honour end to end are `--disable-approval` (keep the OS sandbox) and
|
|
103677
|
-
* `--yolo` (drop approval and the sandbox, and trust the workspace). P4Code
|
|
103678
|
-
* owns the user-facing runtime mode, so it maps onto those two:
|
|
103679
|
-
*
|
|
103680
|
-
* - `full-access` → `--yolo`
|
|
103681
|
-
* - everything else → `--disable-approval --trust-workspace`, leaving the
|
|
103682
|
-
* sandbox to contain what runs.
|
|
103683
|
-
*
|
|
103684
|
-
* `approval-required` cannot be honoured over `exec`; the adapter emits a
|
|
103685
|
-
* `config.warning` for it rather than silently pretending otherwise. Serving
|
|
103686
|
-
* a real approval prompt means moving the driver onto MSP.
|
|
103687
|
-
*
|
|
103688
|
-
* Flags here were verified against Muse Code 1.1.1-R2514.1 (`muse exec
|
|
103689
|
-
* --help`).
|
|
103690
|
-
*/
|
|
103691
|
-
function buildMuseExecArgs(input) {
|
|
103692
|
-
const args = [
|
|
103693
|
-
"exec",
|
|
103694
|
-
"--json",
|
|
103695
|
-
"--session-id",
|
|
103696
|
-
input.sessionId
|
|
103697
|
-
];
|
|
103698
|
-
if (input.model) args.push("--model", input.model);
|
|
103699
|
-
if (input.reasoningEffort) args.push("--reasoning-effort", input.reasoningEffort);
|
|
103700
|
-
if (input.baseUrl) args.push("--base-url", input.baseUrl);
|
|
103701
|
-
if (input.runtimeMode === "full-access") args.push("--yolo");
|
|
103702
|
-
else {
|
|
103703
|
-
args.push("--disable-approval", "--trust-workspace");
|
|
103704
|
-
switch (input.sandboxMode) {
|
|
103705
|
-
case "danger-full-access":
|
|
103706
|
-
args.push("--disable-sandbox");
|
|
103707
|
-
break;
|
|
103708
|
-
case "read-only":
|
|
103709
|
-
args.push("--disable-write", "--disable-shell");
|
|
103710
|
-
break;
|
|
103711
|
-
default: break;
|
|
103712
|
-
}
|
|
103713
|
-
}
|
|
103714
|
-
for (const imagePath of input.imagePaths ?? []) args.push("--image", imagePath);
|
|
103715
|
-
args.push(input.prompt);
|
|
103716
|
-
return args;
|
|
103717
|
-
}
|
|
103718
|
-
/** `muse --version` argv, used by the provider status probe. */
|
|
103719
|
-
const MUSE_VERSION_ARGS = ["--version"];
|
|
103720
|
-
/**
|
|
103721
|
-
* Whether a runtime mode asks for interactive approvals that Muse Code
|
|
103722
|
-
* cannot deliver headlessly.
|
|
103723
|
-
*/
|
|
103724
|
-
function museApprovalsUnsupported(runtimeMode) {
|
|
103725
|
-
return runtimeMode === "approval-required";
|
|
103726
|
-
}
|
|
103727
104166
|
//#endregion
|
|
103728
104167
|
//#region src/provider/muse/MuseExecEvents.ts
|
|
103729
104168
|
/**
|
|
@@ -104255,6 +104694,163 @@ const makeMuseTextGeneration = (museSettings, environment = process.env) => Effe
|
|
|
104255
104694
|
})
|
|
104256
104695
|
};
|
|
104257
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
|
+
});
|
|
104258
104854
|
//#endregion
|
|
104259
104855
|
//#region src/provider/muse/MuseExecRuntime.ts
|
|
104260
104856
|
/**
|
|
@@ -104547,6 +105143,11 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104547
105143
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
104548
105144
|
const path = yield* Path.Path;
|
|
104549
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);
|
|
104550
105151
|
const sessions = /* @__PURE__ */ new Map();
|
|
104551
105152
|
const runtimeEventPubSub = yield* PubSub.unbounded();
|
|
104552
105153
|
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
@@ -104664,6 +105265,26 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104664
105265
|
runtimeMode: ctx.session.runtimeMode,
|
|
104665
105266
|
imagePaths
|
|
104666
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
|
+
};
|
|
104667
105288
|
const state = makeMuseExecTurnState();
|
|
104668
105289
|
const mappingContext = {
|
|
104669
105290
|
provider: PROVIDER$1,
|
|
@@ -104750,7 +105371,7 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104750
105371
|
binaryPath: museSettings.binaryPath || "muse",
|
|
104751
105372
|
args,
|
|
104752
105373
|
cwd: ctx.cwd,
|
|
104753
|
-
environment,
|
|
105374
|
+
environment: turnEnvironment,
|
|
104754
105375
|
onLine: handleLine
|
|
104755
105376
|
}).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() => finishTurn), Effect.tap((run) => Effect.sync(() => {
|
|
104756
105377
|
runStarted = true;
|
|
@@ -104836,6 +105457,7 @@ function makeMuseAdapter(museSettings, options) {
|
|
|
104836
105457
|
yield* ctx.activeTurn.run.interrupt.pipe(Effect.ignore);
|
|
104837
105458
|
ctx.activeTurn = void 0;
|
|
104838
105459
|
}
|
|
105460
|
+
yield* clearMuseConfigOverlay(overlayRootFor(ctx.threadId)).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.ignore);
|
|
104839
105461
|
const updatedAt = yield* nowIso;
|
|
104840
105462
|
ctx.session = {
|
|
104841
105463
|
...ctx.session,
|
|
@@ -105135,6 +105757,7 @@ const MuseDriver = {
|
|
|
105135
105757
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
105136
105758
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
105137
105759
|
const path = yield* Path.Path;
|
|
105760
|
+
const serverConfig = yield* ServerConfig$1;
|
|
105138
105761
|
const serverSettings = yield* ServerSettingsService;
|
|
105139
105762
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
105140
105763
|
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
@@ -105157,9 +105780,16 @@ const MuseDriver = {
|
|
|
105157
105780
|
});
|
|
105158
105781
|
const adapter = yield* makeMuseAdapter(effectiveConfig, {
|
|
105159
105782
|
environment: processEnv,
|
|
105160
|
-
instanceId
|
|
105783
|
+
instanceId,
|
|
105784
|
+
resolveMcpServers: (yield* McpRegistry).resolveForSession
|
|
105161
105785
|
});
|
|
105162
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
|
+
})))));
|
|
105163
105793
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
105164
105794
|
const snapshot = yield* makeManagedServerProvider({
|
|
105165
105795
|
maintenanceCapabilities,
|
|
@@ -105167,7 +105797,7 @@ const MuseDriver = {
|
|
|
105167
105797
|
streamSettings: snapshotSettings.streamSettings,
|
|
105168
105798
|
haveSettingsChanged: haveProviderSnapshotSettingsChanged,
|
|
105169
105799
|
initialSnapshot: (settings) => buildInitialMuseProviderSnapshot(settings.provider).pipe(Effect.map(stampIdentity)),
|
|
105170
|
-
checkProvider:
|
|
105800
|
+
checkProvider: checkProvider.pipe(Effect.map(stampIdentity), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path)),
|
|
105171
105801
|
enrichSnapshot: ({ settings, snapshot: currentSnapshot, publishSnapshot }) => enrichMuseSnapshot({
|
|
105172
105802
|
snapshot: currentSnapshot,
|
|
105173
105803
|
maintenanceCapabilities,
|
|
@@ -106918,6 +107548,8 @@ const BUILT_IN_DRIVERS = [
|
|
|
106918
107548
|
defaultConfig: () => decodeOpenCodeSettings({}),
|
|
106919
107549
|
create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () {
|
|
106920
107550
|
const openCodeRuntime = yield* OpenCodeRuntime;
|
|
107551
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
107552
|
+
const path = yield* Path.Path;
|
|
106921
107553
|
const serverConfig = yield* ServerConfig$1;
|
|
106922
107554
|
const httpClient = yield* HttpClient.HttpClient;
|
|
106923
107555
|
const serverSettings = yield* ServerSettingsService;
|
|
@@ -106948,7 +107580,12 @@ const BUILT_IN_DRIVERS = [
|
|
|
106948
107580
|
...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
|
|
106949
107581
|
});
|
|
106950
107582
|
const textGeneration = yield* makeOpenCodeTextGeneration(effectiveConfig, processEnv);
|
|
106951
|
-
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));
|
|
106952
107589
|
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
106953
107590
|
const snapshot = yield* makeManagedServerProvider({
|
|
106954
107591
|
maintenanceCapabilities,
|
|
@@ -111108,7 +111745,13 @@ const make$4 = Effect.gen(function* () {
|
|
|
111108
111745
|
projects: project ? [project] : [],
|
|
111109
111746
|
chatWorkspaceDir: serverConfig.chatWorkspaceDir
|
|
111110
111747
|
});
|
|
111111
|
-
|
|
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
|
+
];
|
|
111112
111755
|
});
|
|
111113
111756
|
const resolveThread = Effect.fnUntraced(function* (threadId) {
|
|
111114
111757
|
return yield* projectionSnapshotQuery.getThreadDetailById(threadId).pipe(Effect.map(Option.getOrUndefined));
|