@runfusion/fusion 0.49.0 → 0.50.0
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.js +36386 -9279
- package/dist/client/.vite/manifest.json +241 -241
- package/dist/client/assets/{AgentDetailView-B8_IDafM.js → AgentDetailView-D2oYUApl.js} +5 -5
- package/dist/client/assets/{AgentPermissionPolicyEditor-D4U6puHs.js → AgentPermissionPolicyEditor-DGgx46f3.js} +1 -1
- package/dist/client/assets/AgentsView-Dhf5Vq_T.js +29 -0
- package/dist/client/assets/ChatView-DpgpxHva.js +19 -0
- package/dist/client/assets/CommandCenter-B9q-zM9p.css +1 -0
- package/dist/client/assets/CommandCenter-DzQhGq_J.js +71 -0
- package/dist/client/assets/{DevServerView-C1qppz4E.js → DevServerView-DjJYp6Jj.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CW_Q-Bup.js → DirectoryPicker-CYPYp9T4.js} +1 -1
- package/dist/client/assets/{DocumentsView-DGxrLknh.js → DocumentsView-CeQVRVdp.js} +1 -1
- package/dist/client/assets/{EvalsView-BMU01K7i.js → EvalsView-D250mVR5.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-B5Mwwwbz.js → ExperimentalAgentOnboardingModal-B1EEY_bl.js} +1 -1
- package/dist/client/assets/{GoalsView-B9Vwqp8k.js → GoalsView-MftVwE9g.js} +1 -1
- package/dist/client/assets/{InsightsView-C9fVBn7l.js → InsightsView-CbWItYe8.js} +1 -1
- package/dist/client/assets/{MemoryView-Chm5uzjZ.js → MemoryView-Ct0nOTES.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BhKzgNHh.js → PiExtensionsManager-DSQwngX-.js} +1 -1
- package/dist/client/assets/{PluginManager-pEIpbosd.js → PluginManager-DqLxCiNg.js} +1 -1
- package/dist/client/assets/{PullRequestView-DG1gPFRJ.js → PullRequestView-D0WgwdAi.js} +1 -1
- package/dist/client/assets/{ResearchView-wy7oLnPg.js → ResearchView-CJQL9XKC.js} +1 -1
- package/dist/client/assets/{SecretsView-Bid0TP7K.js → SecretsView-ChN9aTbi.js} +1 -1
- package/dist/client/assets/{SessionTerminal-B3XCQFRu.js → SessionTerminal-BVon9kbr.js} +1 -1
- package/dist/client/assets/SettingsModal-B4WANQnu.js +21 -0
- package/dist/client/assets/{SettingsModal-BMpP5CkL.js → SettingsModal-CIC5jhgo.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-C_wMNV3H.js → SetupWizardModal-C6EGjmbd.js} +2 -2
- package/dist/client/assets/{ShadcnColorPicker-CXxr3ynz.js → ShadcnColorPicker-4e_aXoGX.js} +1 -1
- package/dist/client/assets/{SkillsView-DcNtAxQI.js → SkillsView-Dz8dDwfP.js} +1 -1
- package/dist/client/assets/{SkillsView-Cfs7HkwW.css → SkillsView-tAW7TTCz.css} +1 -1
- package/dist/client/assets/{TodoView-Bt5ZY51H.js → TodoView-DvJFzoOv.js} +1 -1
- package/dist/client/assets/WorkflowNodeEditor-DrdTCstS.js +8 -0
- package/dist/client/assets/{agentSkills-DlTZq-3b.js → agentSkills-B2dD5Nm5.js} +1 -1
- package/dist/client/assets/{app-DH6H4_Pi.js → app-BYq4S7Cb.js} +3 -3
- package/dist/client/assets/{app-B7XunYYI.js → app-D27ewSC0.js} +3 -3
- package/dist/client/assets/{app-BgStK3QE.js → app-DFLDkeOE.js} +3 -3
- package/dist/client/assets/{app-BV3tiBJo.js → app-OPrfgjjn.js} +3 -3
- package/dist/client/assets/{app-Cl4147h6.js → app-dATBczl3.js} +3 -3
- package/dist/client/assets/{app-ChEDGcMv.js → app-k1K9zAjp.js} +3 -3
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DPevXfXn.js → architectureDiagram-3BPJPVTR-ClO0MnzR.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-iREXBnwN.js → blockDiagram-GPEHLZMM-C4cyy4WE.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Ds4gntrv.js → c4Diagram-AAUBKEIU-NHPC9TJo.js} +1 -1
- package/dist/client/assets/channel-Q2WKocEP.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-DI8WjOJS.js → chunk-2J33WTMH-CGYIKTVR.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-BSvj2r_a.js → chunk-4BX2VUAB-BkCCD0tE.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DXqk_68P.js → chunk-55IACEB6-Be3yOIDe.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-CbVz6DAi.js → chunk-727SXJPM-DxjyMaQQ.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-O8p3lAmi.js → chunk-AQP2D5EJ-BC0PjN0W.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-8KJJYFeG.js → chunk-FMBD7UC4-DoWxmM8k.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-D_zeaF-K.js → chunk-ND2GUHAM-EOpuc9pP.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-IzXnlcXm.js → chunk-QZHKN3VN-DiRyx9QZ.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CPFl2Aqd.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CPFl2Aqd.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-DyB8nAG8.js → cose-bilkent-S5V4N54A-CfVoUUxT.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BkFeh-yy.js → dagre-BM42HDAG-BfILvxLb.js} +1 -1
- package/dist/client/assets/{dashboard-view-zm0LyiGH.js → dashboard-view-C9YIcx-O.js} +2 -2
- package/dist/client/assets/{dashboard-view-Ddw7AZEw.js → dashboard-view-wFC1pe4d.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D6ccxFD_.js → diagram-2AECGRRQ-6mdmbH-5.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-pYELzWoc.js → diagram-5GNKFQAL-CY7MMwO1.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-DiX408pG.js → diagram-KO2AKTUF-D05XnQqd.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CEZO6Pzz.js → diagram-LMA3HP47-B18FTOJG.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C4wPGJ9_.js → diagram-OG6HWLK6-xmXlYf5m.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-UVoBlCdP.js → erDiagram-TEJ5UH35-4XlgVLCs.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-BHBVkZYH.js → flowDiagram-I6XJVG4X-BYl3ps7f.js} +1 -1
- package/dist/client/assets/{folder-open-BS746XqG.js → folder-open-BH6FwmO7.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-JhbzzuUH.js → ganttDiagram-6RSMTGT7-4vxuH9Dx.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-Bf9CFASb.js → gitGraphDiagram-PVQCEYII-BESmFgXT.js} +1 -1
- package/dist/client/assets/index-B9jtBwFy.css +1 -0
- package/dist/client/assets/index-DFeBJ3GM.js +1302 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-ODZX4ugg.js → infoDiagram-5YYISTIA-DvjdjtwB.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BZ4EmuqN.js → ishikawaDiagram-YF4QCWOH-Cis5IoEm.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DjPguSQp.js → journeyDiagram-JHISSGLW-C20pB1W8.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-CDlWdfMu.js → kanban-definition-UN3LZRKU-CiVfCeU3.js} +1 -1
- package/dist/client/assets/{mermaid.core-rI3nyVx8.js → mermaid.core-CW0vCXA6.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BduuAfgW.js → mindmap-definition-RKZ34NQL-E5rPHUdw.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-Df_vjilh.js → pieDiagram-4H26LBE5-DVIFbPjH.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DXVZfmAq.js → quadrantDiagram-W4KKPZXB-C8qPRe_x.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-UJTrQjTm.js → requirementDiagram-4Y6WPE33-DQ-pwXh3.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DDERoUXV.js → sankeyDiagram-5OEKKPKP-BnghLuUc.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DaULWS9L.js → sequenceDiagram-3UESZ5HK-ChlGxGVk.js} +1 -1
- package/dist/client/assets/{shield-alert-BKmP809x.js → shield-alert-BfOnDO6O.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-tbFQ1nvS.js → stateDiagram-AJRCARHV-ByUvNEch.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DQ0cOptx.js → stateDiagram-v2-BHNVJYJU-Df38ITSP.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BE_RPHyh.js → timeline-definition-PNZ67QCA-bsAGc83D.js} +1 -1
- package/dist/client/assets/{upload-CKPEXc39.js → upload-BK1crTqn.js} +1 -1
- package/dist/client/assets/{users-BFmWR-bN.js → users-DW8jC2Z9.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-TwZvt3sz.js → vennDiagram-CIIHVFJN-BBIj6LFN.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-C6ZavOUS.js → wardley-L42UT6IY-De6JFroy.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B2i11Jb0.js → wardleyDiagram-YWT4CUSO-P1LOLx0_.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DbfolZx7.js → xychartDiagram-2RQKCTM6-v1G4ki6U.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +32507 -5725
- package/dist/pi-claude-cli/index.ts +12 -16
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +26 -1
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +250 -19
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/skill/fusion/references/engine-tools.md +1 -1
- package/dist/client/assets/AgentsView-CafZRACA.js +0 -29
- package/dist/client/assets/ChatView-CZv_hIdK.js +0 -19
- package/dist/client/assets/CommandCenter-B1P49x__.css +0 -1
- package/dist/client/assets/CommandCenter-D9qV7yF_.js +0 -71
- package/dist/client/assets/SettingsModal-Dptq-qYL.js +0 -21
- package/dist/client/assets/WorkflowNodeEditor-AwHxfoz1.js +0 -8
- package/dist/client/assets/channel-FTiNgAnt.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-sIVsNnoj.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-sIVsNnoj.js +0 -1
- package/dist/client/assets/index-BNXyfteR.css +0 -1
- package/dist/client/assets/index-DZGZRdnt.js +0 -1302
|
@@ -25,10 +25,10 @@ import {
|
|
|
25
25
|
} from "./src/mcp-config.js";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* FNXC:pi-claude-cli 2026-06-27-06:39:
|
|
29
|
+
* Route A drives Claude through the ACP bridge only when `FUSION_CLAUDE_ACP=1`
|
|
30
|
+
* AND a bridge binary path is injected via `FUSION_CLAUDE_ACP_BRIDGE`.
|
|
31
|
+
* OFF by default: the live `claude -p` path stays untouched until soak.
|
|
32
32
|
*/
|
|
33
33
|
function resolveAcpBridgePath(): string | undefined {
|
|
34
34
|
if (process.env.FUSION_CLAUDE_ACP !== "1") return undefined;
|
|
@@ -52,14 +52,12 @@ process.on("exit", killAllProcesses);
|
|
|
52
52
|
const PROVIDER_ID = "pi-claude-cli";
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* factory returns immediately and other requests keep flowing; the result
|
|
62
|
-
* is logged once on first run and reused thereafter.
|
|
55
|
+
* FNXC:pi-claude-cli 2026-06-27-06:39:
|
|
56
|
+
* The factory runs per `createFnAgent` call, which the dashboard does for each
|
|
57
|
+
* chat message. Synchronous `execSync` presence + auth probes froze the Node
|
|
58
|
+
* event loop while `claude` cold-started, so memoize the probes as an async
|
|
59
|
+
* Promise: the factory returns immediately, the result logs once, and later
|
|
60
|
+
* calls reuse it.
|
|
63
61
|
*/
|
|
64
62
|
let cliValidationPromise: Promise<void> | undefined;
|
|
65
63
|
|
|
@@ -257,8 +255,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
257
255
|
parameters: Record<string, unknown>;
|
|
258
256
|
}> }).tools;
|
|
259
257
|
|
|
260
|
-
// Route A
|
|
261
|
-
// is on AND a bridge path is injected. OFF by default → `-p` path below.
|
|
258
|
+
// FNXC:pi-claude-cli 2026-06-27-06:39: Route A drives Claude through the ACP bridge only when the kill-switch is on AND a bridge path is injected. OFF by default → `-p` path below.
|
|
262
259
|
const bridgePath = resolveAcpBridgePath();
|
|
263
260
|
if (bridgePath) {
|
|
264
261
|
const toolDefs = resolveToolDefs(pi, contextTools);
|
|
@@ -268,8 +265,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
268
265
|
...options,
|
|
269
266
|
bridgePath,
|
|
270
267
|
mcpServers: buildAcpMcpServers(toolDefs, hash, userMcpServers),
|
|
271
|
-
// Forward only HOME/PATH so the bridged `claude` authenticates from the
|
|
272
|
-
// login/keychain session (R17); never inherited process.env or API keys.
|
|
268
|
+
// FNXC:pi-claude-cli 2026-06-27-06:39: Forward only HOME/PATH so the bridged `claude` authenticates from the login/keychain session (R17); never inherited process.env or API keys.
|
|
273
269
|
bridgeEnv: { HOME: process.env.HOME, PATH: process.env.PATH },
|
|
274
270
|
});
|
|
275
271
|
}
|
|
@@ -2172,6 +2172,12 @@ interface EnsureGitRepositoryOptions {
|
|
|
2172
2172
|
}
|
|
2173
2173
|
declare function ensureGitRepositoryForProjectPath(projectPath: string, options?: EnsureGitRepositoryOptions): Promise<GitRepositoryEnsureOutcome>;
|
|
2174
2174
|
|
|
2175
|
+
type RunningAgentCountSource = (projectIds: readonly string[]) => Promise<Record<string, number>> | Record<string, number>;
|
|
2176
|
+
interface RunningAgentCounts {
|
|
2177
|
+
currentlyActive: number;
|
|
2178
|
+
projectsActive: Record<string, number>;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2175
2181
|
interface CentralCoreEvents {
|
|
2176
2182
|
"project:registered": [project: RegisteredProject];
|
|
2177
2183
|
"project:reattached": [project: RegisteredProject, reason: string];
|
|
@@ -2391,6 +2397,9 @@ declare class CentralCore extends EventEmitter<CentralCoreEvents> {
|
|
|
2391
2397
|
getDefaultProjectId(): Promise<string | undefined>;
|
|
2392
2398
|
setDefaultProjectId(projectId: string | null): Promise<void>;
|
|
2393
2399
|
getGlobalConcurrencyState(): Promise<GlobalConcurrencyState>;
|
|
2400
|
+
getLiveRunningAgentCounts(options?: {
|
|
2401
|
+
source?: RunningAgentCountSource;
|
|
2402
|
+
}): Promise<RunningAgentCounts>;
|
|
2394
2403
|
updateGlobalConcurrency(updates: Partial<Pick<GlobalConcurrencyState, "globalMaxConcurrent" | "currentlyActive" | "queuedCount">>): Promise<GlobalConcurrencyState>;
|
|
2395
2404
|
acquireGlobalSlot(projectId: string): Promise<boolean>;
|
|
2396
2405
|
releaseGlobalSlot(projectId: string): Promise<void>;
|
|
@@ -2696,6 +2705,13 @@ declare class TaskStore extends EventEmitter<TaskStoreEvents> {
|
|
|
2696
2705
|
reason: string;
|
|
2697
2706
|
}>;
|
|
2698
2707
|
}>;
|
|
2708
|
+
reconcilePhantomCommittedReservations(): Promise<{
|
|
2709
|
+
reconciled: string[];
|
|
2710
|
+
skipped: Array<{
|
|
2711
|
+
id: string;
|
|
2712
|
+
reason: string;
|
|
2713
|
+
}>;
|
|
2714
|
+
}>;
|
|
2699
2715
|
private readTaskJson;
|
|
2700
2716
|
private writeTaskJsonFile;
|
|
2701
2717
|
private atomicCreateTaskJson;
|
|
@@ -2720,6 +2736,7 @@ declare class TaskStore extends EventEmitter<TaskStoreEvents> {
|
|
|
2720
2736
|
private writeConfig;
|
|
2721
2737
|
resolveLocalNodeIdForTaskAllocation(): Promise<string>;
|
|
2722
2738
|
private createTaskWithDistributedReservation;
|
|
2739
|
+
private rollbackFailedDistributedReservationCreate;
|
|
2723
2740
|
private taskDir;
|
|
2724
2741
|
private artifactRegistryDir;
|
|
2725
2742
|
private static artifactStoredName;
|
|
@@ -2741,6 +2758,10 @@ declare class TaskStore extends EventEmitter<TaskStoreEvents> {
|
|
|
2741
2758
|
prompt?: string;
|
|
2742
2759
|
applyDefaultWorkflowSteps?: boolean;
|
|
2743
2760
|
invokeTaskCreatedHook?: boolean;
|
|
2761
|
+
reservationCommit?: {
|
|
2762
|
+
reservationId: string;
|
|
2763
|
+
nodeId: string;
|
|
2764
|
+
};
|
|
2744
2765
|
}): Promise<Task>;
|
|
2745
2766
|
applyReplicatedTaskCreate(payload: MeshReplicatedTaskCreatePayload): Promise<MeshReplicatedTaskApplyResult>;
|
|
2746
2767
|
private _createTaskInternal;
|
|
@@ -3989,6 +4010,7 @@ declare class Database {
|
|
|
3989
4010
|
private applyMigration;
|
|
3990
4011
|
private hasTable;
|
|
3991
4012
|
isFts5CorruptionError(error: unknown): boolean;
|
|
4013
|
+
reindexMessages(): void;
|
|
3992
4014
|
private getTableColumns;
|
|
3993
4015
|
private hasColumn;
|
|
3994
4016
|
private tableExists;
|
|
@@ -4363,6 +4385,7 @@ interface WorkflowStep {
|
|
|
4363
4385
|
prompt: string;
|
|
4364
4386
|
toolMode?: WorkflowStepToolMode;
|
|
4365
4387
|
skillName?: string;
|
|
4388
|
+
requiresBrowser?: boolean;
|
|
4366
4389
|
scriptName?: string;
|
|
4367
4390
|
enabled: boolean;
|
|
4368
4391
|
defaultOn?: boolean;
|
|
@@ -4372,7 +4395,7 @@ interface WorkflowStep {
|
|
|
4372
4395
|
createdAt: string;
|
|
4373
4396
|
updatedAt: string;
|
|
4374
4397
|
}
|
|
4375
|
-
type NtfyNotificationEvent = "in-review" | "merged" | "failed" | "awaiting-approval" | "awaiting-user-review" | "planning-awaiting-input" | "gridlock" | "board-stall-unrecovered" | "db-corruption-detected" | "fallback-used" | "memory-dreams-processed" | "token-budget" | "message:agent-to-user" | "message:agent-to-agent" | "message:room" | "oauth-token-expired" | "task-created" | "workflow-notify";
|
|
4398
|
+
type NtfyNotificationEvent = "in-review" | "merged" | "failed" | "awaiting-approval" | "awaiting-user-review" | "planning-awaiting-input" | "cli-agent-awaiting-input" | "gridlock" | "board-stall-unrecovered" | "db-corruption-detected" | "fallback-used" | "memory-dreams-processed" | "token-budget" | "message:agent-to-user" | "message:agent-to-agent" | "message:room" | "oauth-token-expired" | "task-created" | "workflow-notify";
|
|
4376
4399
|
interface NotificationProviderConfig {
|
|
4377
4400
|
id: string;
|
|
4378
4401
|
name: string;
|
|
@@ -5652,6 +5675,7 @@ interface ProjectSettings {
|
|
|
5652
5675
|
verificationCommandTimeoutMs?: number;
|
|
5653
5676
|
scopeVerificationToChangedFiles?: boolean;
|
|
5654
5677
|
requirePlanApproval?: boolean;
|
|
5678
|
+
planApprovalMode?: "workflow" | "auto-approve-all" | "require-all";
|
|
5655
5679
|
ephemeralAgentsEnabled?: boolean;
|
|
5656
5680
|
agentProvisioning?: {
|
|
5657
5681
|
approvalMode?: AgentProvisioningApprovalMode;
|
|
@@ -6428,6 +6452,7 @@ interface Agent {
|
|
|
6428
6452
|
totalOutputTokens?: number;
|
|
6429
6453
|
lastError?: string;
|
|
6430
6454
|
pendingApprovalCount?: number;
|
|
6455
|
+
taskColumn?: string;
|
|
6431
6456
|
instructionsPath?: string;
|
|
6432
6457
|
instructionsText?: string;
|
|
6433
6458
|
soul?: string;
|
|
@@ -50,6 +50,18 @@ var COMPOUND_ENGINEERING_SKILLS = [
|
|
|
50
50
|
enabled: true,
|
|
51
51
|
triggerPatterns: ["do the work", "implement", "execute the plan", "finish this feature"]
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
/*
|
|
55
|
+
* FNXC:CompoundEngineering 2026-06-27-00:00:
|
|
56
|
+
* FN-7144 bundles ce-doc-review because ce-plan and ce-brainstorm invoke it as a named markdown document-review skill, and the built-in CE workflow now exposes it as an optional advisory stage after planning.
|
|
57
|
+
*/
|
|
58
|
+
skillId: "ce-doc-review",
|
|
59
|
+
name: "ce-doc-review",
|
|
60
|
+
description: "Review CE markdown plan documents for coherence, feasibility, scope alignment, and safe markdown-only fixes.",
|
|
61
|
+
skillFiles: ["skills/ce-doc-review/SKILL.md"],
|
|
62
|
+
enabled: true,
|
|
63
|
+
triggerPatterns: ["doc review", "document review", "review the plan", "pressure-test the requirements"]
|
|
64
|
+
},
|
|
53
65
|
{
|
|
54
66
|
skillId: "ce-code-review",
|
|
55
67
|
name: "ce-code-review",
|
|
@@ -105,9 +117,23 @@ var COMPOUND_ENGINEERING_SKILLS = [
|
|
|
105
117
|
];
|
|
106
118
|
|
|
107
119
|
// ../../plugins/fusion-plugin-compound-engineering/src/skill-installation.ts
|
|
108
|
-
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
120
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
109
121
|
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
110
122
|
import { fileURLToPath } from "node:url";
|
|
123
|
+
|
|
124
|
+
// ../../plugins/fusion-plugin-compound-engineering/src/upstream-provenance.ts
|
|
125
|
+
var CE_UPSTREAM_PROVENANCE = {
|
|
126
|
+
repo: "EveryInc/compound-engineering-plugin",
|
|
127
|
+
repoUrl: "https://github.com/EveryInc/compound-engineering-plugin",
|
|
128
|
+
releaseTag: "compound-engineering-v3.15.0",
|
|
129
|
+
releaseUrl: "https://github.com/EveryInc/compound-engineering-plugin/releases/tag/compound-engineering-v3.15.0",
|
|
130
|
+
sourceTarballUrl: "https://github.com/EveryInc/compound-engineering-plugin/archive/refs/tags/compound-engineering-v3.15.0.tar.gz",
|
|
131
|
+
commit: "2bbdbfb1d4287db95af407808b53266988ada974",
|
|
132
|
+
tarballSha256: "fce13e71bd709f8f572bf167c6af3753fc3fde0309c8f878498c78cb391c0b14",
|
|
133
|
+
vendoredAt: "2026-06-26"
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// ../../plugins/fusion-plugin-compound-engineering/src/skill-installation.ts
|
|
111
137
|
function resolveBundledSkillsRoot() {
|
|
112
138
|
const here = fileURLToPath(import.meta.url);
|
|
113
139
|
const dir = dirname(here);
|
|
@@ -143,17 +169,54 @@ function assertValidSkillSource(skillId, sourceDir) {
|
|
|
143
169
|
);
|
|
144
170
|
}
|
|
145
171
|
}
|
|
172
|
+
var SKILL_INSTALL_PROVENANCE_FILE = ".fusion-ce-upstream-provenance.json";
|
|
173
|
+
function installProvenancePath(targetRoot) {
|
|
174
|
+
return join(targetRoot, SKILL_INSTALL_PROVENANCE_FILE);
|
|
175
|
+
}
|
|
176
|
+
function isCurrentInstalledProvenance(targetRoot) {
|
|
177
|
+
try {
|
|
178
|
+
const marker = JSON.parse(readFileSync(installProvenancePath(targetRoot), "utf-8"));
|
|
179
|
+
return marker.releaseTag === CE_UPSTREAM_PROVENANCE.releaseTag && marker.tarballSha256 === CE_UPSTREAM_PROVENANCE.tarballSha256;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function writeInstalledProvenance(targetRoot) {
|
|
185
|
+
mkdirSync(targetRoot, { recursive: true });
|
|
186
|
+
writeFileSync(
|
|
187
|
+
installProvenancePath(targetRoot),
|
|
188
|
+
`${JSON.stringify(
|
|
189
|
+
{
|
|
190
|
+
repo: CE_UPSTREAM_PROVENANCE.repo,
|
|
191
|
+
releaseTag: CE_UPSTREAM_PROVENANCE.releaseTag,
|
|
192
|
+
commit: CE_UPSTREAM_PROVENANCE.commit,
|
|
193
|
+
tarballSha256: CE_UPSTREAM_PROVENANCE.tarballSha256,
|
|
194
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
195
|
+
},
|
|
196
|
+
null,
|
|
197
|
+
2
|
|
198
|
+
)}
|
|
199
|
+
`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
146
202
|
function installBundledCeSkills(options = {}) {
|
|
147
203
|
const targetRoot = options.targetRoot ? resolve(options.targetRoot) : resolveDefaultInstallTargetRoot();
|
|
148
204
|
assertPluginLocalTarget(targetRoot);
|
|
149
205
|
const sourceRoot = options.sourceRoot ? resolve(options.sourceRoot) : resolveBundledSkillsRoot();
|
|
206
|
+
const installIsCurrent = isCurrentInstalledProvenance(targetRoot);
|
|
150
207
|
const results = COMPOUND_ENGINEERING_SKILLS.map((skill) => {
|
|
151
208
|
const sourceDir = join(sourceRoot, skill.skillId);
|
|
152
209
|
const targetDir = join(targetRoot, skill.skillId);
|
|
153
210
|
try {
|
|
154
211
|
assertValidSkillSource(skill.skillId, sourceDir);
|
|
155
212
|
if (existsSync(targetDir)) {
|
|
156
|
-
|
|
213
|
+
if (installIsCurrent) {
|
|
214
|
+
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "skipped", reason: "current install preserved" };
|
|
215
|
+
}
|
|
216
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
217
|
+
mkdirSync(targetRoot, { recursive: true });
|
|
218
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
219
|
+
return { skillId: skill.skillId, sourceDir, targetDir, outcome: "refreshed", reason: "stale install refreshed" };
|
|
157
220
|
}
|
|
158
221
|
mkdirSync(targetRoot, { recursive: true });
|
|
159
222
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
@@ -168,6 +231,9 @@ function installBundledCeSkills(options = {}) {
|
|
|
168
231
|
};
|
|
169
232
|
}
|
|
170
233
|
});
|
|
234
|
+
if (results.every((result) => result.outcome !== "error")) {
|
|
235
|
+
writeInstalledProvenance(targetRoot);
|
|
236
|
+
}
|
|
171
237
|
return { targetRoot, results };
|
|
172
238
|
}
|
|
173
239
|
function isPluginLocalPath(p) {
|
|
@@ -175,7 +241,7 @@ function isPluginLocalPath(p) {
|
|
|
175
241
|
}
|
|
176
242
|
|
|
177
243
|
// ../../plugins/fusion-plugin-compound-engineering/src/agent-installation.ts
|
|
178
|
-
import { cpSync as cpSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
244
|
+
import { cpSync as cpSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
179
245
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
180
246
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
181
247
|
function resolveBundledAgentsRoot() {
|
|
@@ -207,10 +273,41 @@ function assertValidAgentSource(agentId, sourceFile) {
|
|
|
207
273
|
throw new Error(`Bundled agent def '${agentId}' at ${sourceFile} is missing a frontmatter 'name:' field`);
|
|
208
274
|
}
|
|
209
275
|
}
|
|
276
|
+
var AGENT_INSTALL_PROVENANCE_FILE = ".fusion-ce-upstream-provenance.json";
|
|
277
|
+
function installProvenancePath2(targetRoot) {
|
|
278
|
+
return join2(targetRoot, AGENT_INSTALL_PROVENANCE_FILE);
|
|
279
|
+
}
|
|
280
|
+
function isCurrentInstalledProvenance2(targetRoot) {
|
|
281
|
+
try {
|
|
282
|
+
const marker = JSON.parse(readFileSync2(installProvenancePath2(targetRoot), "utf-8"));
|
|
283
|
+
return marker.releaseTag === CE_UPSTREAM_PROVENANCE.releaseTag && marker.tarballSha256 === CE_UPSTREAM_PROVENANCE.tarballSha256;
|
|
284
|
+
} catch {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function writeInstalledProvenance2(targetRoot) {
|
|
289
|
+
mkdirSync2(targetRoot, { recursive: true });
|
|
290
|
+
writeFileSync2(
|
|
291
|
+
installProvenancePath2(targetRoot),
|
|
292
|
+
`${JSON.stringify(
|
|
293
|
+
{
|
|
294
|
+
repo: CE_UPSTREAM_PROVENANCE.repo,
|
|
295
|
+
releaseTag: CE_UPSTREAM_PROVENANCE.releaseTag,
|
|
296
|
+
commit: CE_UPSTREAM_PROVENANCE.commit,
|
|
297
|
+
tarballSha256: CE_UPSTREAM_PROVENANCE.tarballSha256,
|
|
298
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
299
|
+
},
|
|
300
|
+
null,
|
|
301
|
+
2
|
|
302
|
+
)}
|
|
303
|
+
`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
210
306
|
function installBundledCeAgents(options = {}) {
|
|
211
307
|
const targetRoot = options.targetRoot ? resolve2(options.targetRoot) : resolveDefaultAgentsInstallTargetRoot();
|
|
212
308
|
assertPluginLocalAgentsTarget(targetRoot);
|
|
213
309
|
const sourceRoot = options.sourceRoot ? resolve2(options.sourceRoot) : resolveBundledAgentsRoot();
|
|
310
|
+
const installIsCurrent = isCurrentInstalledProvenance2(targetRoot);
|
|
214
311
|
const sourceFiles = existsSync2(sourceRoot) ? readdirSync(sourceRoot).filter((f) => f.endsWith(".md")) : [];
|
|
215
312
|
const results = sourceFiles.map((file) => {
|
|
216
313
|
const agentId = file.replace(/\.md$/, "");
|
|
@@ -219,7 +316,13 @@ function installBundledCeAgents(options = {}) {
|
|
|
219
316
|
try {
|
|
220
317
|
assertValidAgentSource(agentId, sourceFile);
|
|
221
318
|
if (existsSync2(targetFile)) {
|
|
222
|
-
|
|
319
|
+
if (installIsCurrent) {
|
|
320
|
+
return { agentId, sourceFile, targetFile, outcome: "skipped", reason: "current install preserved" };
|
|
321
|
+
}
|
|
322
|
+
rmSync2(targetFile, { force: true });
|
|
323
|
+
mkdirSync2(targetRoot, { recursive: true });
|
|
324
|
+
cpSync2(sourceFile, targetFile);
|
|
325
|
+
return { agentId, sourceFile, targetFile, outcome: "refreshed", reason: "stale install refreshed" };
|
|
223
326
|
}
|
|
224
327
|
mkdirSync2(targetRoot, { recursive: true });
|
|
225
328
|
cpSync2(sourceFile, targetFile);
|
|
@@ -234,6 +337,9 @@ function installBundledCeAgents(options = {}) {
|
|
|
234
337
|
};
|
|
235
338
|
}
|
|
236
339
|
});
|
|
340
|
+
if (results.length > 0 && results.every((result) => result.outcome !== "error")) {
|
|
341
|
+
writeInstalledProvenance2(targetRoot);
|
|
342
|
+
}
|
|
237
343
|
return { targetRoot, results };
|
|
238
344
|
}
|
|
239
345
|
function isPluginLocalAgentsPath(p) {
|
|
@@ -318,7 +424,7 @@ function ensureCeSchema(db) {
|
|
|
318
424
|
}
|
|
319
425
|
|
|
320
426
|
// ../../plugins/fusion-plugin-compound-engineering/src/session/orchestrator.ts
|
|
321
|
-
import { mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
|
|
427
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
322
428
|
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3 } from "node:path";
|
|
323
429
|
|
|
324
430
|
// ../../plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts
|
|
@@ -529,13 +635,17 @@ var STAGE_DEFINITIONS = [
|
|
|
529
635
|
artifactGlob: "docs/ideation/**/*.md"
|
|
530
636
|
},
|
|
531
637
|
{
|
|
638
|
+
/*
|
|
639
|
+
* FNXC:CompoundEngineering 2026-06-27-00:21:
|
|
640
|
+
* Brainstorm and plan keep separate Fusion stage IDs and bundled skill IDs for session, pipeline, and board back-compat, but CE v3.15.0 aliases their durable artifact to one unified docs/plans plan. ce-brainstorm writes the requirements-only unified plan that ce-plan later enriches in place to implementation-ready.
|
|
641
|
+
*/
|
|
532
642
|
stageId: "brainstorm",
|
|
533
643
|
order: 300,
|
|
534
644
|
skillId: "ce-brainstorm",
|
|
535
|
-
artifactLocation: "docs/
|
|
645
|
+
artifactLocation: "docs/plans/",
|
|
536
646
|
icon: "Sparkles",
|
|
537
647
|
label: "Brainstorm",
|
|
538
|
-
artifactGlob: "docs/
|
|
648
|
+
artifactGlob: "docs/plans/**/*.md"
|
|
539
649
|
},
|
|
540
650
|
{
|
|
541
651
|
stageId: "plan",
|
|
@@ -891,6 +1001,29 @@ var CeTurnTimeoutError = class extends Error {
|
|
|
891
1001
|
function resolveStageSkillPaths() {
|
|
892
1002
|
return [resolveDefaultInstallTargetRoot()];
|
|
893
1003
|
}
|
|
1004
|
+
function checkStageSkillResolution(stage, skillPaths = resolveStageSkillPaths()) {
|
|
1005
|
+
const expectedSkillMdPaths = skillPaths.map((root) => join3(root, stage.skillId, "SKILL.md"));
|
|
1006
|
+
return {
|
|
1007
|
+
skillId: stage.skillId,
|
|
1008
|
+
expectedSkillMdPaths,
|
|
1009
|
+
found: expectedSkillMdPaths.some((skillMd) => existsSync3(skillMd))
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function warnIfStageSkillMissing(logger, stage, additionalSkillPaths = resolveStageSkillPaths()) {
|
|
1013
|
+
const guard = checkStageSkillResolution(stage, additionalSkillPaths);
|
|
1014
|
+
if (!guard.found) {
|
|
1015
|
+
logger.warn(
|
|
1016
|
+
`Compound Engineering stage '${stage.stageId}' requested skill '${stage.skillId}', but no SKILL.md was found on the plugin-local discovery paths. The session will still request the skill so the engine can resolve it if installation completes, but the current install appears missing.`,
|
|
1017
|
+
{
|
|
1018
|
+
stageId: stage.stageId,
|
|
1019
|
+
skillId: stage.skillId,
|
|
1020
|
+
expectedSkillMdPaths: guard.expectedSkillMdPaths,
|
|
1021
|
+
additionalSkillPaths
|
|
1022
|
+
}
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
return guard;
|
|
1026
|
+
}
|
|
894
1027
|
function buildStageSystemPrompt(stage) {
|
|
895
1028
|
return [
|
|
896
1029
|
`You are running the Compound Engineering "${stage.stageId}" stage.`,
|
|
@@ -955,12 +1088,14 @@ var CeOrchestrator = class {
|
|
|
955
1088
|
buildSessionOptions(stage, sessionId) {
|
|
956
1089
|
const defaultProvider = getDefaultProvider(this.ctx.settings);
|
|
957
1090
|
const defaultModelId = getDefaultModelId(this.ctx.settings);
|
|
1091
|
+
const additionalSkillPaths = resolveStageSkillPaths();
|
|
1092
|
+
warnIfStageSkillMissing(this.ctx.logger, stage, additionalSkillPaths);
|
|
958
1093
|
return {
|
|
959
1094
|
cwd: this.projectRoot,
|
|
960
1095
|
systemPrompt: buildStageSystemPrompt(stage),
|
|
961
1096
|
tools: "coding",
|
|
962
1097
|
requestedSkillNames: [stage.skillId],
|
|
963
|
-
additionalSkillPaths
|
|
1098
|
+
additionalSkillPaths,
|
|
964
1099
|
onProgress: (event) => this.handleProgress(sessionId, event),
|
|
965
1100
|
...defaultProvider ? { defaultProvider } : {},
|
|
966
1101
|
...defaultModelId ? { defaultModelId } : {}
|
|
@@ -1450,7 +1585,7 @@ var CeOrchestrator = class {
|
|
|
1450
1585
|
const target = location.endsWith("/") ? join3(location, `${session.stage}-${session.id}.md`) : location;
|
|
1451
1586
|
const abs = isAbsolute3(target) ? target : join3(this.projectRoot, target);
|
|
1452
1587
|
mkdirSync3(dirname3(abs), { recursive: true });
|
|
1453
|
-
|
|
1588
|
+
writeFileSync3(abs, content, "utf-8");
|
|
1454
1589
|
return abs;
|
|
1455
1590
|
}
|
|
1456
1591
|
extractArtifactContent(data) {
|
|
@@ -1657,7 +1792,17 @@ function createSessionRoutes() {
|
|
|
1657
1792
|
}
|
|
1658
1793
|
|
|
1659
1794
|
// ../../plugins/fusion-plugin-compound-engineering/src/artifacts/discovery.ts
|
|
1660
|
-
import {
|
|
1795
|
+
import {
|
|
1796
|
+
accessSync,
|
|
1797
|
+
closeSync,
|
|
1798
|
+
constants,
|
|
1799
|
+
lstatSync,
|
|
1800
|
+
openSync,
|
|
1801
|
+
readFileSync as readFileSync3,
|
|
1802
|
+
readSync,
|
|
1803
|
+
readdirSync as readdirSync2,
|
|
1804
|
+
realpathSync
|
|
1805
|
+
} from "node:fs";
|
|
1661
1806
|
import { isAbsolute as isAbsolute4, join as join4, relative, sep as sep3 } from "node:path";
|
|
1662
1807
|
var CONVENTIONAL_LOCATIONS = [
|
|
1663
1808
|
{ stage: "strategy", label: "Strategy", path: "STRATEGY.md", kind: "file" },
|
|
@@ -1668,6 +1813,7 @@ var CONVENTIONAL_LOCATIONS = [
|
|
|
1668
1813
|
{ stage: "concepts", label: "Concepts", path: "CONCEPTS.md", kind: "file" }
|
|
1669
1814
|
];
|
|
1670
1815
|
var MAX_ARTIFACT_BYTES = 2e6;
|
|
1816
|
+
var FRONTMATTER_PREFIX_BYTES = 8 * 1024;
|
|
1671
1817
|
function toPosix(p) {
|
|
1672
1818
|
return p.split(sep3).join("/");
|
|
1673
1819
|
}
|
|
@@ -1681,6 +1827,19 @@ function isWithin(root, locationAbs, candidate) {
|
|
|
1681
1827
|
function makeId(stage, relPath) {
|
|
1682
1828
|
return `${stage}:${relPath}`;
|
|
1683
1829
|
}
|
|
1830
|
+
function isRealPathWithin(root, locationAbs, candidate) {
|
|
1831
|
+
try {
|
|
1832
|
+
const realRoot = realpathSync(root);
|
|
1833
|
+
const realLocation = realpathSync(locationAbs);
|
|
1834
|
+
const realCandidate = realpathSync(candidate);
|
|
1835
|
+
return isWithin(realRoot, realLocation, realCandidate);
|
|
1836
|
+
} catch {
|
|
1837
|
+
return false;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
function symlinkError(stage, relPath) {
|
|
1841
|
+
return makeError(stage, relPath, "Symlink artifacts are not allowed in CE discovery");
|
|
1842
|
+
}
|
|
1684
1843
|
function makeError(stage, relPath, message) {
|
|
1685
1844
|
return {
|
|
1686
1845
|
id: makeId(stage, relPath),
|
|
@@ -1691,17 +1850,66 @@ function makeError(stage, relPath, message) {
|
|
|
1691
1850
|
error: message
|
|
1692
1851
|
};
|
|
1693
1852
|
}
|
|
1853
|
+
function emptyMetadata() {
|
|
1854
|
+
return { artifactContract: null, artifactReadiness: null, productContractSource: null };
|
|
1855
|
+
}
|
|
1856
|
+
function cleanYamlScalar(value) {
|
|
1857
|
+
const trimmed = value.trim();
|
|
1858
|
+
if (!trimmed) return null;
|
|
1859
|
+
const commentStart = trimmed.indexOf(" #");
|
|
1860
|
+
const withoutComment = commentStart >= 0 ? trimmed.slice(0, commentStart).trim() : trimmed;
|
|
1861
|
+
return withoutComment.replace(/^['\"]|['\"]$/g, "") || null;
|
|
1862
|
+
}
|
|
1863
|
+
function parseArtifactMetadata(prefix) {
|
|
1864
|
+
const metadata = emptyMetadata();
|
|
1865
|
+
if (!prefix.startsWith("---")) return metadata;
|
|
1866
|
+
const end = prefix.indexOf("\n---", 3);
|
|
1867
|
+
if (end < 0) return metadata;
|
|
1868
|
+
const frontmatter = prefix.slice(3, end).split(/\r?\n/);
|
|
1869
|
+
for (const line of frontmatter) {
|
|
1870
|
+
const match = /^\s*([A-Za-z0-9_-]+)\s*:\s*(.*?)\s*$/.exec(line);
|
|
1871
|
+
if (!match) continue;
|
|
1872
|
+
const value = cleanYamlScalar(match[2]);
|
|
1873
|
+
if (match[1] === "artifact_contract") metadata.artifactContract = value;
|
|
1874
|
+
if (match[1] === "artifact_readiness") metadata.artifactReadiness = value;
|
|
1875
|
+
if (match[1] === "product_contract_source") metadata.productContractSource = value;
|
|
1876
|
+
}
|
|
1877
|
+
return metadata;
|
|
1878
|
+
}
|
|
1879
|
+
function readMetadataPrefix(abs, size) {
|
|
1880
|
+
let fd;
|
|
1881
|
+
try {
|
|
1882
|
+
fd = openSync(abs, "r");
|
|
1883
|
+
const buffer = Buffer.alloc(Math.min(size, FRONTMATTER_PREFIX_BYTES));
|
|
1884
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, 0);
|
|
1885
|
+
return parseArtifactMetadata(buffer.toString("utf8", 0, bytesRead));
|
|
1886
|
+
} catch {
|
|
1887
|
+
return emptyMetadata();
|
|
1888
|
+
} finally {
|
|
1889
|
+
if (fd !== void 0) {
|
|
1890
|
+
try {
|
|
1891
|
+
closeSync(fd);
|
|
1892
|
+
} catch {
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1694
1897
|
function readArtifactEntry(stage, root, locationAbs, abs, relPath) {
|
|
1695
1898
|
const name = relPath.split("/").pop() ?? relPath;
|
|
1696
1899
|
if (!isWithin(root, locationAbs, abs)) {
|
|
1697
1900
|
return makeError(stage, relPath, "Path is outside its conventional location");
|
|
1698
1901
|
}
|
|
1699
1902
|
try {
|
|
1700
|
-
const st =
|
|
1903
|
+
const st = lstatSync(abs);
|
|
1904
|
+
if (st.isSymbolicLink()) return symlinkError(stage, relPath);
|
|
1905
|
+
if (!isRealPathWithin(root, locationAbs, abs)) {
|
|
1906
|
+
return makeError(stage, relPath, "Path is outside its conventional location");
|
|
1907
|
+
}
|
|
1701
1908
|
if (st.size > MAX_ARTIFACT_BYTES) {
|
|
1702
1909
|
return makeError(stage, relPath, `Artifact too large to read (${st.size} bytes)`);
|
|
1703
1910
|
}
|
|
1704
1911
|
accessSync(abs, constants.R_OK);
|
|
1912
|
+
const metadata = readMetadataPrefix(abs, st.size);
|
|
1705
1913
|
return {
|
|
1706
1914
|
id: makeId(stage, relPath),
|
|
1707
1915
|
stage,
|
|
@@ -1709,6 +1917,7 @@ function readArtifactEntry(stage, root, locationAbs, abs, relPath) {
|
|
|
1709
1917
|
name,
|
|
1710
1918
|
size: st.size,
|
|
1711
1919
|
updatedAt: st.mtimeMs,
|
|
1920
|
+
...metadata,
|
|
1712
1921
|
kind: "artifact"
|
|
1713
1922
|
};
|
|
1714
1923
|
} catch (err) {
|
|
@@ -1728,13 +1937,17 @@ function discoverLocation(root, loc) {
|
|
|
1728
1937
|
let present = false;
|
|
1729
1938
|
let st;
|
|
1730
1939
|
try {
|
|
1731
|
-
st =
|
|
1940
|
+
st = lstatSync(locationAbs);
|
|
1732
1941
|
present = true;
|
|
1733
1942
|
} catch {
|
|
1734
1943
|
return { stage: loc.stage, label: loc.label, present: false, entries: [] };
|
|
1735
1944
|
}
|
|
1736
1945
|
if (loc.kind === "file") {
|
|
1737
|
-
if (st.
|
|
1946
|
+
if (st.isSymbolicLink()) {
|
|
1947
|
+
entries.push(symlinkError(loc.stage, toPosix(loc.path)));
|
|
1948
|
+
} else if (!isRealPathWithin(root, locationAbs, locationAbs)) {
|
|
1949
|
+
entries.push(makeError(loc.stage, toPosix(loc.path), "Path is outside its conventional location"));
|
|
1950
|
+
} else if (st.isFile()) {
|
|
1738
1951
|
entries.push(readArtifactEntry(loc.stage, root, locationAbs, locationAbs, toPosix(loc.path)));
|
|
1739
1952
|
} else {
|
|
1740
1953
|
entries.push(
|
|
@@ -1749,6 +1962,14 @@ function discoverLocation(root, loc) {
|
|
|
1749
1962
|
}
|
|
1750
1963
|
let names = [];
|
|
1751
1964
|
try {
|
|
1965
|
+
if (st.isSymbolicLink()) {
|
|
1966
|
+
entries.push(symlinkError(loc.stage, toPosix(loc.path)));
|
|
1967
|
+
return { stage: loc.stage, label: loc.label, present, entries: sortEntries(entries) };
|
|
1968
|
+
}
|
|
1969
|
+
if (!isRealPathWithin(root, locationAbs, locationAbs)) {
|
|
1970
|
+
entries.push(makeError(loc.stage, toPosix(loc.path), "Path is outside its conventional location"));
|
|
1971
|
+
return { stage: loc.stage, label: loc.label, present, entries: sortEntries(entries) };
|
|
1972
|
+
}
|
|
1752
1973
|
if (!st.isDirectory()) {
|
|
1753
1974
|
entries.push(
|
|
1754
1975
|
makeError(
|
|
@@ -1771,11 +1992,15 @@ function discoverLocation(root, loc) {
|
|
|
1771
1992
|
const relPath = toPosix(join4(loc.path, childName));
|
|
1772
1993
|
let childStat;
|
|
1773
1994
|
try {
|
|
1774
|
-
childStat =
|
|
1995
|
+
childStat = lstatSync(abs);
|
|
1775
1996
|
} catch (err) {
|
|
1776
1997
|
entries.push(makeError(loc.stage, relPath, err instanceof Error ? err.message : String(err)));
|
|
1777
1998
|
continue;
|
|
1778
1999
|
}
|
|
2000
|
+
if (childStat.isSymbolicLink()) {
|
|
2001
|
+
entries.push(symlinkError(loc.stage, relPath));
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
1779
2004
|
if (!childStat.isFile()) continue;
|
|
1780
2005
|
entries.push(readArtifactEntry(loc.stage, root, locationAbs, abs, relPath));
|
|
1781
2006
|
}
|
|
@@ -1818,7 +2043,9 @@ function readArtifactById(projectRoot, id) {
|
|
|
1818
2043
|
let mtimeMs;
|
|
1819
2044
|
let size;
|
|
1820
2045
|
try {
|
|
1821
|
-
const st =
|
|
2046
|
+
const st = lstatSync(abs);
|
|
2047
|
+
if (st.isSymbolicLink()) return { error: "Symlink artifacts are not allowed in CE discovery" };
|
|
2048
|
+
if (!isRealPathWithin(projectRoot, locationAbs, abs)) return void 0;
|
|
1822
2049
|
if (!st.isFile()) return { error: "Artifact is not a readable file" };
|
|
1823
2050
|
if (st.size > MAX_ARTIFACT_BYTES) return { error: `Artifact too large to read (${st.size} bytes)` };
|
|
1824
2051
|
mtimeMs = st.mtimeMs;
|
|
@@ -1841,6 +2068,7 @@ function readArtifactById(projectRoot, id) {
|
|
|
1841
2068
|
name,
|
|
1842
2069
|
size,
|
|
1843
2070
|
updatedAt: mtimeMs,
|
|
2071
|
+
...parseArtifactMetadata(content.slice(0, FRONTMATTER_PREFIX_BYTES)),
|
|
1844
2072
|
kind: "artifact"
|
|
1845
2073
|
},
|
|
1846
2074
|
content
|
|
@@ -2120,12 +2348,14 @@ var plugin = definePlugin({
|
|
|
2120
2348
|
// Install the bundled, pinned ce-* SKILL.md files into a plugin-local,
|
|
2121
2349
|
// discoverable directory on load. The engine ingests
|
|
2122
2350
|
// PluginSkillContribution only as a name; physical discovery requires the
|
|
2123
|
-
// files to exist on a path it scans (U2 finding). Install is
|
|
2124
|
-
//
|
|
2351
|
+
// files to exist on a path it scans (U2 finding). Install is provenance-aware:
|
|
2352
|
+
// current plugin-local copies are skipped, stale/unmarked copies are refreshed,
|
|
2353
|
+
// and global ~/.claude/skills is never touched.
|
|
2125
2354
|
onLoad: async (ctx) => {
|
|
2126
2355
|
try {
|
|
2127
2356
|
const { targetRoot, results } = installBundledCeSkills();
|
|
2128
2357
|
const installed = results.filter((r) => r.outcome === "installed").length;
|
|
2358
|
+
const refreshed = results.filter((r) => r.outcome === "refreshed").length;
|
|
2129
2359
|
const errored = results.filter((r) => r.outcome === "error");
|
|
2130
2360
|
if (errored.length > 0) {
|
|
2131
2361
|
ctx.logger.warn(
|
|
@@ -2133,7 +2363,7 @@ var plugin = definePlugin({
|
|
|
2133
2363
|
);
|
|
2134
2364
|
}
|
|
2135
2365
|
ctx.logger.info(
|
|
2136
|
-
`Compound Engineering skills ready \u2014 installed=${installed} target=${targetRoot}`
|
|
2366
|
+
`Compound Engineering skills ready \u2014 installed=${installed} refreshed=${refreshed} target=${targetRoot}`
|
|
2137
2367
|
);
|
|
2138
2368
|
ctx.emitEvent("compound-engineering:skills-installed", { targetRoot, results });
|
|
2139
2369
|
} catch (error) {
|
|
@@ -2143,6 +2373,7 @@ var plugin = definePlugin({
|
|
|
2143
2373
|
try {
|
|
2144
2374
|
const { targetRoot, results } = installBundledCeAgents();
|
|
2145
2375
|
const installed = results.filter((r) => r.outcome === "installed").length;
|
|
2376
|
+
const refreshed = results.filter((r) => r.outcome === "refreshed").length;
|
|
2146
2377
|
const errored = results.filter((r) => r.outcome === "error");
|
|
2147
2378
|
if (errored.length > 0) {
|
|
2148
2379
|
ctx.logger.warn(
|
|
@@ -2150,7 +2381,7 @@ var plugin = definePlugin({
|
|
|
2150
2381
|
);
|
|
2151
2382
|
}
|
|
2152
2383
|
ctx.logger.info(
|
|
2153
|
-
`Compound Engineering agent personas ready \u2014 installed=${installed} target=${targetRoot}`
|
|
2384
|
+
`Compound Engineering agent personas ready \u2014 installed=${installed} refreshed=${refreshed} target=${targetRoot}`
|
|
2154
2385
|
);
|
|
2155
2386
|
ctx.emitEvent("compound-engineering:agents-installed", { targetRoot, results });
|
|
2156
2387
|
} catch (error) {
|