chainlesschain 0.162.71 → 0.162.73
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/package.json +3 -2
- package/src/assets/web-panel/assets/{AIOps-pes7_jGr.js → AIOps-DTI_-iZ_.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DiHWdeH1.js → ActionButton-CtpbGicW.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C51UX-V_.js → Analytics-C1HwMd7E.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BE6LJLw9.js → AppLayout-Db953rqB.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-CtTkLTe2.js → Audit-CuIi9Vup.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-lmpVwPxc.js → Backup-DwR7Wlve.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Dr7DR1E1.js → BaseInput-B5wNMp7S.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DXph6klA.js → Chat-DivalHgk.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-RefrXxqp.js → ChatBubbleRenderer-BwPSr02C.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BF0aS5R9.js → Checkbox-CJrXPCJH.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B-oHWNTV.js → Codegen-BieZvOkC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B6KdyRTE.js → Col-BzvPsQgb.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D5ac0KyO.js → Community-CWcAMlpQ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dc61O2eQ.js → Compact-Bj_BLBpw.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-a48qvNmd.js → Compliance-DKvo0WAi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CIF9lSRj.js → Cowork-DRajk8JO.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-C_gkf4xd.js → Cron-DPz9MoUo.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJt15PzW.js → Crosschain-BpSuvmns.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D8I_okEj.js → DID-Co98G00H.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-O4q-qCF2.js → Dashboard-8TSG0W7f.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-flRziiaw.js → Dropdown-CKViu7w3.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CnnkfGPw.js → EmailListRenderer-BqfdADvJ.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CYAJpGqd.js → FamilyGuardDashboard-aWu1imGZ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Dm3WqzQ_.js → Federation-w4YpT8EN.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CpAIter6.js → FormItemContext-CAk4CXid.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-ChzXcyuN.js → GenericCardRenderer-MoJw4J2L.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DElmMsL4.js → Git-HNU_a9Jz.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-EWmgn9io.js → Governance-BkIXWYUS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-VtOR_sef.js → Inference-CnquB88h.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-OMqlGkMR.js → KnowledgeGraph-PGSMF-uq.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CTgYDsTZ.js → Logs-DCc-nYzu.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DZIUTbn8.js → Marketplace-CyGX8XEt.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DWeOErCA.js → McpTools-DBkZ5yUL.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-DwFBp-ev.js → Memory-DmYLRCwo.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D42xBfE3.js → MobileBridge-CszJjeNc.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-BHCHauUl.js → MobileProjects-DWgtDsta.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CabnNsyS.js → Mtc-Bmt_Pmg4.js} +3 -3
- package/src/assets/web-panel/assets/{MtcAudit-Ddx9Awu5.js → MtcAudit-BmG0ys0l.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CJpYBZ8f.js → Multisig-DNd5iMmD.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BvPJskVG.js → NLProgramming-yxbNl09B.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-92ELvJM2.js → Notes-CQ0qAzI4.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CXz0SUzP.js → NotificationSettings-CIzkE4ao.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-4iWRkMr-.js → OrderTableRenderer-e1VMUxTi.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DblqJPeY.js → Organization-BNBIlf_R.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CHfS3HwD.js → Overflow-C5GzzJTq.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DIg0pLIG.js → P2P-DjL2jIED.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BhD3DGjk.js → PdhVaultBrowser-C2EYDmpW.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Ct7bwrz8.js → Permissions-BVGCXxDp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DG9c1eas.js → PersonalDataHub-B1xBro16.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-BwQnEqG0.js → Pipeline-DIYWiDnt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BvWaf-Ba.js → Privacy--Qx3QdO1.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BFzMHtiW.js → ProjectInit-DYOEcV04.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DDpokSpH.js → ProjectSettings-BcOx5-KS.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-3IL8lyPl.js → Projects-CO69txk4.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-C6BsEzSR.js → Providers-Bj8pCgzo.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Mh6690Ey.js → QuickAsk-Cukpq-4s.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CJbZ6wgA.js → Recommend-DJB5wUfM.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-GDcyl6iQ.js → Reputation-B9t0-nT0.js} +1 -1
- package/src/assets/web-panel/assets/{Row-6p4DKFSi.js → Row-4cniRgxC.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BLhrkoDq.js → RssFeed-Y4db_T9t.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Ckiyt35t.js → Search-DggZDzlc.js} +1 -1
- package/src/assets/web-panel/assets/{Security-qhh-B5Qt.js → Security-C67A6kED.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D7a2OwKm.js → Services-BocBjvaa.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CSMxupKd.js → Skeleton-C9jI-iOy.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CGpMng8w.js → Skills-2baFHKOW.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CYtaU3Ar.js → Sla-CnowXeRb.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BTCsu1uM.js → SpeechSettings-DWGBkmUt.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Cnzx2L5I.js → SyncSettings-B89qwCgX.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-C1_WvNc0.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Ccdx_C3w.js → Templates-P8dBhbxa.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ZN8c86j3.js → Tenant-DHo0pxrm.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-Dr-HvjH-.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-CjgirE9d.js → TimelineRenderer-DM7dOaUR.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-QoRpi6Wf.js → Tokens-DnJxqOPM.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CEQKJkhs.js → Trigger-C-FLgMzr.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-UTuAzV1q.js → Trust-CIT_RVqA.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-JDs9I7fl.js → UkeySign-Cg9VS6y6.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BACYQQSd.js → VideoEditing-BzoxE8mA.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C-SKluhH.js → Wallet-ZkmCII8R.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-9chizTFy.js → WebAuthn-CptQdp3u.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-0mF-LAUo.js → WorkflowEditor-Vkz28khO.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Di7QINiP.js → chat-DXdmvvEA.js} +1 -1
- package/src/assets/web-panel/assets/{colors-SBmhHMM9.js → colors-De7FCj5l.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BmbETbNj.js → compact-item-DnT3XzME.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C7hFnsJ7.js → createContext-ECZfCCqd.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-gaA8VqZf.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CFtthKDD.js → hasIn-Uodg6pAt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRBc1Ewn.js → index-1dWvOqru.js} +1 -1
- package/src/assets/web-panel/assets/{index-WjZVyLn7.js → index-BC8SOuGi.js} +1 -1
- package/src/assets/web-panel/assets/{index-CovTrPpI.js → index-BUMNJRCl.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSnTUPQx.js → index-Bg1RsfAJ.js} +3 -3
- package/src/assets/web-panel/assets/{index-NxxQTlPJ.js → index-Blo85ZKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClIp4Vin.js → index-BtJ8BHU0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4etIqbf.js → index-C01WM7Pk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWfObC0n.js → index-C4KhsGXo.js} +1 -1
- package/src/assets/web-panel/assets/{index-sz0w-D-C.js → index-C6JgjkY7.js} +1 -1
- package/src/assets/web-panel/assets/{index-lRVjtXeH.js → index-C9_Cnvha.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCcCs7LJ.js → index-CCguvASR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6tZzsb9.js → index-CLzPxAp4.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0yO3vWh.js → index-CMuqxCVw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcawAm_i.js → index-COTabJQd.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cib-RTws.js → index-CPjI3AGE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C07kpleB.js → index-CaDZ-LMg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEYnWiq1.js → index-CbbfcGeJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgXQwqXr.js → index-CcLDjTgI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2CN7coN.js → index-CffZBdA1.js} +1 -1
- package/src/assets/web-panel/assets/{index-PN02VlUB.js → index-CwXP0KcT.js} +1 -1
- package/src/assets/web-panel/assets/{index-_hO8-EnW.js → index-D2YOclGO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk4ez5rf.js → index-DBsl2SuZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6vNUp6c.js → index-DCvmjxu9.js} +1 -1
- package/src/assets/web-panel/assets/{index-sA4vr5WV.js → index-DGwgEqwl.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI2xH1fU.js → index-DOIeo8Qs.js} +1 -1
- package/src/assets/web-panel/assets/{index-_a2NG3iP.js → index-DPcpxSQt.js} +1 -1
- package/src/assets/web-panel/assets/index-DTpmiTGK.js +1 -0
- package/src/assets/web-panel/assets/{index-CWDk3nDZ.js → index-Db33l8Ix.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNpb8m5a.js → index-Dbfj4QL5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dww6gCzI.js → index-Dd9Bx3tJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bpo7UVJe.js → index-Dkvf5upf.js} +1 -1
- package/src/assets/web-panel/assets/index-DwpvMYUU.js +1 -0
- package/src/assets/web-panel/assets/{index-BT2uOwmA.js → index-Dy0AT-uc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2fhXmhW.js → index-FU8Zo5cp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcRTX_WO.js → index-JVwNC4yP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dps9xdE8.js → index-JqSxr1A7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D134XFWR.js → index-KV6dXwKC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8CeUlN0.js → index-LwNszM-5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcBhsVYn.js → index-cHnmjGqB.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B1zCwkvT.js → initDefaultProps-B3H1sf95.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BDGIV9KA.js → motion-DreYO5eb.js} +1 -1
- package/src/assets/web-panel/assets/{move-BITqgluK.js → move-B0OVU04M.js} +1 -1
- package/src/assets/web-panel/assets/{omit-uD97QKBF.js → omit-PcEEXHLZ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CZpXhSIE.js → pickAttrs-DH6-hDkF.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ByUvT_08.js → placementArrow-DFamUSEw.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQG9LlBs.js → responsiveObserve-DT1grAyh.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bx9TyeEE.js → slide-41lCsY6d.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-XWaMqYM8.js → statusUtils-FzJ7j2T7.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BZ-nMyDB.js → styleChecker-D0Zvn50h.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D_iwA3vx.js → useFlexGapSupport-BTxsJE5n.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DuvW_aRX.js → useFs-DRVttEr3.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BO7orS0d.js → usePersonalDataHub-BSe7NVv0.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cc_fqAL4.js → vnode-D8O7tUXh.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BGkBp90M.js → zoom-CMQc1RZ4.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +12 -0
- package/src/commands/cowork.js +8 -0
- package/src/commands/crosschain.js +32 -4
- package/src/commands/init.js +10 -10
- package/src/commands/loop.js +9 -3
- package/src/commands/memory.js +6 -4
- package/src/commands/orchestrate.js +5 -2
- package/src/commands/video.js +5 -1
- package/src/lib/agents.js +16 -5
- package/src/lib/cowork-workflow.js +357 -136
- package/src/lib/micro-compact.js +52 -0
- package/src/lib/output-styles.js +41 -7
- package/src/lib/permission-rules.cjs +39 -0
- package/src/lib/project-root.cjs +87 -0
- package/src/lib/settings-hooks.cjs +18 -6
- package/src/lib/settings-loader.cjs +12 -5
- package/src/lib/skill-loader.js +62 -43
- package/src/lib/slash-commands.js +18 -2
- package/src/repl/agent-repl.js +228 -20
- package/src/repl/chat-repl.js +4 -2
- package/src/repl/permission-tier.js +60 -0
- package/src/repl/stream-decision.js +16 -0
- package/src/repl/think-command.js +36 -0
- package/src/runtime/agent-core.js +67 -10
- package/src/runtime/file-ref-expander.js +209 -18
- package/src/runtime/headless-runner.js +3 -3
- package/src/runtime/headless-stream.js +16 -3
- package/src/runtime/mcp-config.js +78 -1
- package/src/assets/web-panel/assets/Tasks-BhnGtqaZ.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BfgM0oyN.js +0 -3
- package/src/assets/web-panel/assets/devWarning-gNvbyy4j.js +0 -1
- package/src/assets/web-panel/assets/index-BNXpMnIY.js +0 -1
- package/src/assets/web-panel/assets/index-gfHxqT1Q.js +0 -1
|
@@ -625,6 +625,206 @@ export async function runLoopStep({
|
|
|
625
625
|
};
|
|
626
626
|
}
|
|
627
627
|
|
|
628
|
+
// ─── Step node + no-barrier pipeline ──────────────────────────────────────────
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Run a single step to completion, resolving its when-gate / loop / forEach /
|
|
632
|
+
* plain task exactly as the batch executor does, and storing results (including
|
|
633
|
+
* a forEach parent aggregate) in `resultsById`. Returns `{ outcomes, failed }`:
|
|
634
|
+
* `outcomes` are the entries to add to the run's step list (forEach contributes
|
|
635
|
+
* its children, not the parent); `failed` is true when any non-skipped outcome
|
|
636
|
+
* is not "completed". Used by the no-barrier pipeline scheduler.
|
|
637
|
+
*/
|
|
638
|
+
export async function runStepNode(step, ctx) {
|
|
639
|
+
const { resultsById, cwd, llmOptions, onStepStart, onStepComplete } = ctx;
|
|
640
|
+
const recordId = step.id;
|
|
641
|
+
const single = (status, summary) => {
|
|
642
|
+
const o = { id: recordId, status, taskId: null, result: { summary } };
|
|
643
|
+
resultsById.set(recordId, o);
|
|
644
|
+
return {
|
|
645
|
+
outcomes: [o],
|
|
646
|
+
failed: status !== "completed" && status !== "skipped",
|
|
647
|
+
};
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
let runThis;
|
|
651
|
+
try {
|
|
652
|
+
runThis = shouldRunStep(step, resultsById);
|
|
653
|
+
} catch (err) {
|
|
654
|
+
return single("failed", err.message);
|
|
655
|
+
}
|
|
656
|
+
if (!runThis) return single("skipped", "when-condition false");
|
|
657
|
+
|
|
658
|
+
if (isLoopStep(step)) {
|
|
659
|
+
if (onStepStart) onStepStart({ stepId: recordId, message: step.message });
|
|
660
|
+
const o = await runLoopStep({
|
|
661
|
+
step,
|
|
662
|
+
recordId,
|
|
663
|
+
cwd,
|
|
664
|
+
llmOptions,
|
|
665
|
+
resultsById,
|
|
666
|
+
});
|
|
667
|
+
resultsById.set(recordId, o);
|
|
668
|
+
if (onStepComplete) onStepComplete(o);
|
|
669
|
+
return {
|
|
670
|
+
outcomes: [o],
|
|
671
|
+
failed: o.status !== "completed" && o.status !== "skipped",
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (step.forEach !== undefined) {
|
|
676
|
+
let items;
|
|
677
|
+
try {
|
|
678
|
+
items = resolveForEachItems(step.forEach, resultsById);
|
|
679
|
+
} catch (err) {
|
|
680
|
+
return single("failed", err.message);
|
|
681
|
+
}
|
|
682
|
+
if (items.length === 0) return single("skipped", "forEach items empty");
|
|
683
|
+
const children = await Promise.all(
|
|
684
|
+
items.map(async (item, k) => {
|
|
685
|
+
const childId = `${recordId}[${k}]`;
|
|
686
|
+
const withItem = substituteItem(step.message, item);
|
|
687
|
+
const msg = substitutePlaceholders(withItem, resultsById);
|
|
688
|
+
if (onStepStart) onStepStart({ stepId: childId, message: msg });
|
|
689
|
+
const r = await runStepWithRetry({
|
|
690
|
+
step,
|
|
691
|
+
message: msg,
|
|
692
|
+
cwd,
|
|
693
|
+
llmOptions,
|
|
694
|
+
});
|
|
695
|
+
const co = outcomeFromRetry(childId, r);
|
|
696
|
+
resultsById.set(childId, co);
|
|
697
|
+
if (onStepComplete) onStepComplete(co);
|
|
698
|
+
return co;
|
|
699
|
+
}),
|
|
700
|
+
);
|
|
701
|
+
const allOk = children.every((c) => c.status === "completed");
|
|
702
|
+
const anyOk = children.some((c) => c.status === "completed");
|
|
703
|
+
resultsById.set(recordId, {
|
|
704
|
+
id: recordId,
|
|
705
|
+
status: allOk ? "completed" : anyOk ? "partial" : "failed",
|
|
706
|
+
taskId: null,
|
|
707
|
+
result: {
|
|
708
|
+
summary: children.map((c) => c.result?.summary ?? "").join("\n"),
|
|
709
|
+
children: children.length,
|
|
710
|
+
},
|
|
711
|
+
});
|
|
712
|
+
return {
|
|
713
|
+
outcomes: children,
|
|
714
|
+
failed: children.some((c) => c.status !== "completed"),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const message = substitutePlaceholders(step.message, resultsById);
|
|
719
|
+
if (onStepStart) onStepStart({ stepId: recordId, message });
|
|
720
|
+
const r = await runStepWithRetry({ step, message, cwd, llmOptions });
|
|
721
|
+
const o = outcomeFromRetry(recordId, r);
|
|
722
|
+
resultsById.set(recordId, o);
|
|
723
|
+
if (onStepComplete) onStepComplete(o);
|
|
724
|
+
return { outcomes: [o], failed: o.status !== "completed" };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* No-barrier pipeline scheduler: start each step the instant *its own*
|
|
729
|
+
* dependencies finish, rather than waiting for the whole dependency level.
|
|
730
|
+
* Up to `maxParallel` step nodes run concurrently. On failure with
|
|
731
|
+
* `continueOnError` off, no new steps are scheduled (in-flight ones finish) and
|
|
732
|
+
* the rest are marked skipped. Produces the same outcome set as the batch
|
|
733
|
+
* executor — only the wall-clock idle between levels is removed.
|
|
734
|
+
*/
|
|
735
|
+
export async function runPipeline({
|
|
736
|
+
steps,
|
|
737
|
+
resultsById,
|
|
738
|
+
maxParallel = 4,
|
|
739
|
+
continueOnError = false,
|
|
740
|
+
cwd,
|
|
741
|
+
llmOptions = {},
|
|
742
|
+
onStepStart,
|
|
743
|
+
onStepComplete,
|
|
744
|
+
}) {
|
|
745
|
+
const limit = Math.max(1, Math.floor(maxParallel) || 1);
|
|
746
|
+
const remainingDeps = new Map(
|
|
747
|
+
steps.map((s) => [s.id, new Set(s.dependsOn || [])]),
|
|
748
|
+
);
|
|
749
|
+
const dependents = new Map(steps.map((s) => [s.id, []]));
|
|
750
|
+
for (const s of steps) {
|
|
751
|
+
for (const d of s.dependsOn || []) {
|
|
752
|
+
if (dependents.has(d)) dependents.get(d).push(s.id);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const scheduled = new Set();
|
|
756
|
+
const stepOutcomes = [];
|
|
757
|
+
let anyFailure = false;
|
|
758
|
+
let halted = false;
|
|
759
|
+
let active = 0;
|
|
760
|
+
|
|
761
|
+
await new Promise((resolve) => {
|
|
762
|
+
function pump() {
|
|
763
|
+
if (!halted) {
|
|
764
|
+
for (const s of steps) {
|
|
765
|
+
if (active >= limit) break;
|
|
766
|
+
if (scheduled.has(s.id)) continue;
|
|
767
|
+
if (remainingDeps.get(s.id).size !== 0) continue;
|
|
768
|
+
scheduled.add(s.id);
|
|
769
|
+
active++;
|
|
770
|
+
launch(s);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (active === 0 && (halted || scheduled.size === steps.length)) {
|
|
774
|
+
for (const s of steps) {
|
|
775
|
+
if (scheduled.has(s.id)) continue;
|
|
776
|
+
const o = {
|
|
777
|
+
id: s.id,
|
|
778
|
+
status: "skipped",
|
|
779
|
+
taskId: null,
|
|
780
|
+
result: { summary: "skipped due to earlier failure" },
|
|
781
|
+
};
|
|
782
|
+
resultsById.set(s.id, o);
|
|
783
|
+
stepOutcomes.push(o);
|
|
784
|
+
scheduled.add(s.id);
|
|
785
|
+
}
|
|
786
|
+
resolve();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
async function launch(step) {
|
|
791
|
+
let res;
|
|
792
|
+
try {
|
|
793
|
+
res = await runStepNode(step, {
|
|
794
|
+
resultsById,
|
|
795
|
+
cwd,
|
|
796
|
+
llmOptions,
|
|
797
|
+
onStepStart,
|
|
798
|
+
onStepComplete,
|
|
799
|
+
});
|
|
800
|
+
} catch (err) {
|
|
801
|
+
const o = {
|
|
802
|
+
id: step.id,
|
|
803
|
+
status: "failed",
|
|
804
|
+
taskId: null,
|
|
805
|
+
result: { summary: `Step threw: ${err.message}` },
|
|
806
|
+
};
|
|
807
|
+
resultsById.set(step.id, o);
|
|
808
|
+
res = { outcomes: [o], failed: true };
|
|
809
|
+
}
|
|
810
|
+
stepOutcomes.push(...res.outcomes);
|
|
811
|
+
active--;
|
|
812
|
+
if (res.failed) {
|
|
813
|
+
anyFailure = true;
|
|
814
|
+
if (!continueOnError) halted = true;
|
|
815
|
+
}
|
|
816
|
+
for (const depId of dependents.get(step.id) || []) {
|
|
817
|
+
remainingDeps.get(depId).delete(step.id);
|
|
818
|
+
}
|
|
819
|
+
pump();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
pump();
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
return { stepOutcomes, anyFailure };
|
|
826
|
+
}
|
|
827
|
+
|
|
628
828
|
// ─── Execution ───────────────────────────────────────────────────────────────
|
|
629
829
|
|
|
630
830
|
/**
|
|
@@ -646,8 +846,12 @@ export async function runLoopStep({
|
|
|
646
846
|
* @param {object} options
|
|
647
847
|
* @param {object} options.workflow - Workflow definition
|
|
648
848
|
* @param {string} [options.cwd] - Working directory for history
|
|
649
|
-
* @param {number} [options.maxParallel] - Max parallel steps per batch
|
|
849
|
+
* @param {number} [options.maxParallel] - Max parallel steps (per batch, or
|
|
850
|
+
* concurrent step nodes in pipeline mode)
|
|
650
851
|
* @param {boolean} [options.continueOnError] - Keep running after a failure
|
|
852
|
+
* @param {boolean} [options.pipeline] - No-barrier scheduling: start each step
|
|
853
|
+
* as soon as its own deps finish instead of waiting for the dependency level.
|
|
854
|
+
* Defaults to `workflow.pipeline ?? false`. Same outcomes, less idle wait.
|
|
651
855
|
* @param {object} [options.llmOptions] - Forwarded to each task
|
|
652
856
|
* @param {function} [options.onStepStart]
|
|
653
857
|
* @param {function} [options.onStepComplete]
|
|
@@ -668,6 +872,7 @@ export async function executeWorkflow(options = {}) {
|
|
|
668
872
|
llmOptions = {},
|
|
669
873
|
onStepStart,
|
|
670
874
|
onStepComplete,
|
|
875
|
+
pipeline,
|
|
671
876
|
} = options;
|
|
672
877
|
|
|
673
878
|
const { valid, errors } = validateWorkflow(workflow);
|
|
@@ -678,79 +883,54 @@ export async function executeWorkflow(options = {}) {
|
|
|
678
883
|
);
|
|
679
884
|
}
|
|
680
885
|
|
|
681
|
-
const
|
|
886
|
+
const usePipeline = pipeline ?? workflow.pipeline ?? false;
|
|
682
887
|
const resultsById = new Map();
|
|
683
|
-
const stepOutcomes = [];
|
|
684
888
|
const startedAt = new Date(_deps.now()).toISOString();
|
|
685
|
-
let
|
|
889
|
+
let stepOutcomes;
|
|
890
|
+
let anyFailure;
|
|
686
891
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
892
|
+
if (usePipeline) {
|
|
893
|
+
({ stepOutcomes, anyFailure } = await runPipeline({
|
|
894
|
+
steps: workflow.steps,
|
|
895
|
+
resultsById,
|
|
896
|
+
maxParallel,
|
|
897
|
+
continueOnError,
|
|
898
|
+
cwd,
|
|
899
|
+
llmOptions,
|
|
900
|
+
onStepStart,
|
|
901
|
+
onStepComplete,
|
|
902
|
+
}));
|
|
903
|
+
} else {
|
|
904
|
+
stepOutcomes = [];
|
|
905
|
+
anyFailure = false;
|
|
906
|
+
const batches = planBatches(workflow.steps);
|
|
907
|
+
for (const batch of batches) {
|
|
908
|
+
// Respect maxParallel by slicing batch into chunks
|
|
909
|
+
const chunks = [];
|
|
910
|
+
for (let i = 0; i < batch.length; i += maxParallel) {
|
|
911
|
+
chunks.push(batch.slice(i, i + maxParallel));
|
|
912
|
+
}
|
|
693
913
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
try {
|
|
713
|
-
runThis = shouldRunStep(step, resultsById);
|
|
714
|
-
} catch (err) {
|
|
715
|
-
anyFailure = true;
|
|
716
|
-
const outcome = {
|
|
717
|
-
id: step.id,
|
|
718
|
-
status: "failed",
|
|
719
|
-
taskId: null,
|
|
720
|
-
result: { summary: err.message },
|
|
721
|
-
};
|
|
722
|
-
resultsById.set(step.id, outcome);
|
|
723
|
-
preOutcomes.push(outcome);
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
if (!runThis) {
|
|
727
|
-
const outcome = {
|
|
728
|
-
id: step.id,
|
|
729
|
-
status: "skipped",
|
|
730
|
-
taskId: null,
|
|
731
|
-
result: { summary: "when-condition false" },
|
|
732
|
-
};
|
|
733
|
-
resultsById.set(step.id, outcome);
|
|
734
|
-
preOutcomes.push(outcome);
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
// loop node — runs its body repeatedly; per-iteration substitution
|
|
738
|
-
// happens inside runLoopStep, so push the raw template.
|
|
739
|
-
if (isLoopStep(step)) {
|
|
740
|
-
runnable.push({
|
|
741
|
-
step,
|
|
742
|
-
message: step.message,
|
|
743
|
-
recordId: step.id,
|
|
744
|
-
parentId: null,
|
|
745
|
-
isLoop: true,
|
|
746
|
-
});
|
|
747
|
-
continue;
|
|
748
|
-
}
|
|
749
|
-
// forEach-expansion
|
|
750
|
-
if (step.forEach !== undefined) {
|
|
751
|
-
let items;
|
|
914
|
+
for (const chunk of chunks) {
|
|
915
|
+
// Expand forEach / when into concrete tasks for this chunk
|
|
916
|
+
const runnable = []; // { step, message, recordId, parentId }
|
|
917
|
+
const preOutcomes = []; // outcomes produced synchronously (skipped)
|
|
918
|
+
for (const step of chunk) {
|
|
919
|
+
if (anyFailure && !continueOnError) {
|
|
920
|
+
const outcome = {
|
|
921
|
+
id: step.id,
|
|
922
|
+
status: "skipped",
|
|
923
|
+
taskId: null,
|
|
924
|
+
result: { summary: "skipped due to earlier failure" },
|
|
925
|
+
};
|
|
926
|
+
resultsById.set(step.id, outcome);
|
|
927
|
+
preOutcomes.push(outcome);
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
// when-gate
|
|
931
|
+
let runThis = true;
|
|
752
932
|
try {
|
|
753
|
-
|
|
933
|
+
runThis = shouldRunStep(step, resultsById);
|
|
754
934
|
} catch (err) {
|
|
755
935
|
anyFailure = true;
|
|
756
936
|
const outcome = {
|
|
@@ -763,91 +943,132 @@ export async function executeWorkflow(options = {}) {
|
|
|
763
943
|
preOutcomes.push(outcome);
|
|
764
944
|
continue;
|
|
765
945
|
}
|
|
766
|
-
if (
|
|
946
|
+
if (!runThis) {
|
|
767
947
|
const outcome = {
|
|
768
948
|
id: step.id,
|
|
769
949
|
status: "skipped",
|
|
770
950
|
taskId: null,
|
|
771
|
-
result: { summary: "
|
|
951
|
+
result: { summary: "when-condition false" },
|
|
772
952
|
};
|
|
773
953
|
resultsById.set(step.id, outcome);
|
|
774
954
|
preOutcomes.push(outcome);
|
|
775
955
|
continue;
|
|
776
956
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
const msg = substitutePlaceholders(withItem, resultsById);
|
|
957
|
+
// loop node — runs its body repeatedly; per-iteration substitution
|
|
958
|
+
// happens inside runLoopStep, so push the raw template.
|
|
959
|
+
if (isLoopStep(step)) {
|
|
781
960
|
runnable.push({
|
|
782
961
|
step,
|
|
783
|
-
message:
|
|
784
|
-
recordId:
|
|
785
|
-
parentId:
|
|
962
|
+
message: step.message,
|
|
963
|
+
recordId: step.id,
|
|
964
|
+
parentId: null,
|
|
965
|
+
isLoop: true,
|
|
786
966
|
});
|
|
967
|
+
continue;
|
|
787
968
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
969
|
+
// forEach-expansion
|
|
970
|
+
if (step.forEach !== undefined) {
|
|
971
|
+
let items;
|
|
972
|
+
try {
|
|
973
|
+
items = resolveForEachItems(step.forEach, resultsById);
|
|
974
|
+
} catch (err) {
|
|
975
|
+
anyFailure = true;
|
|
976
|
+
const outcome = {
|
|
977
|
+
id: step.id,
|
|
978
|
+
status: "failed",
|
|
979
|
+
taskId: null,
|
|
980
|
+
result: { summary: err.message },
|
|
981
|
+
};
|
|
982
|
+
resultsById.set(step.id, outcome);
|
|
983
|
+
preOutcomes.push(outcome);
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (items.length === 0) {
|
|
987
|
+
const outcome = {
|
|
988
|
+
id: step.id,
|
|
989
|
+
status: "skipped",
|
|
990
|
+
taskId: null,
|
|
991
|
+
result: { summary: "forEach items empty" },
|
|
992
|
+
};
|
|
993
|
+
resultsById.set(step.id, outcome);
|
|
994
|
+
preOutcomes.push(outcome);
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
for (let k = 0; k < items.length; k++) {
|
|
998
|
+
const childId = `${step.id}[${k}]`;
|
|
999
|
+
const withItem = substituteItem(step.message, items[k]);
|
|
1000
|
+
const msg = substitutePlaceholders(withItem, resultsById);
|
|
1001
|
+
runnable.push({
|
|
1002
|
+
step,
|
|
1003
|
+
message: msg,
|
|
1004
|
+
recordId: childId,
|
|
1005
|
+
parentId: step.id,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
continue;
|
|
814
1009
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return outcome;
|
|
819
|
-
},
|
|
820
|
-
);
|
|
1010
|
+
const message = substitutePlaceholders(step.message, resultsById);
|
|
1011
|
+
runnable.push({ step, message, recordId: step.id, parentId: null });
|
|
1012
|
+
}
|
|
821
1013
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1014
|
+
const promises = runnable.map(
|
|
1015
|
+
async ({ step, message, recordId, isLoop }) => {
|
|
1016
|
+
if (onStepStart) onStepStart({ stepId: recordId, message });
|
|
1017
|
+
let outcome;
|
|
1018
|
+
if (isLoop) {
|
|
1019
|
+
outcome = await runLoopStep({
|
|
1020
|
+
step,
|
|
1021
|
+
recordId,
|
|
1022
|
+
cwd,
|
|
1023
|
+
llmOptions,
|
|
1024
|
+
resultsById,
|
|
1025
|
+
});
|
|
1026
|
+
} else {
|
|
1027
|
+
const r = await runStepWithRetry({
|
|
1028
|
+
step,
|
|
1029
|
+
message,
|
|
1030
|
+
cwd,
|
|
1031
|
+
llmOptions,
|
|
1032
|
+
});
|
|
1033
|
+
outcome = outcomeFromRetry(recordId, r);
|
|
1034
|
+
}
|
|
1035
|
+
if (outcome.status !== "completed") anyFailure = true;
|
|
1036
|
+
resultsById.set(recordId, outcome);
|
|
1037
|
+
if (onStepComplete) onStepComplete(outcome);
|
|
1038
|
+
return outcome;
|
|
845
1039
|
},
|
|
846
|
-
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
const results = await Promise.all(promises);
|
|
1043
|
+
stepOutcomes.push(...preOutcomes, ...results);
|
|
1044
|
+
|
|
1045
|
+
// Aggregate forEach children into a parent entry so downstream
|
|
1046
|
+
// `${step.<parent>.summary}` references still work.
|
|
1047
|
+
const byParent = new Map();
|
|
1048
|
+
for (let k = 0; k < runnable.length; k++) {
|
|
1049
|
+
const r = runnable[k];
|
|
1050
|
+
if (!r.parentId) continue;
|
|
1051
|
+
if (!byParent.has(r.parentId)) byParent.set(r.parentId, []);
|
|
1052
|
+
byParent.get(r.parentId).push(results[k]);
|
|
1053
|
+
}
|
|
1054
|
+
for (const [parentId, children] of byParent) {
|
|
1055
|
+
const allOk = children.every((c) => c.status === "completed");
|
|
1056
|
+
const anyOk = children.some((c) => c.status === "completed");
|
|
1057
|
+
const status = allOk ? "completed" : anyOk ? "partial" : "failed";
|
|
1058
|
+
resultsById.set(parentId, {
|
|
1059
|
+
id: parentId,
|
|
1060
|
+
status,
|
|
1061
|
+
taskId: null,
|
|
1062
|
+
result: {
|
|
1063
|
+
summary: children.map((c) => c.result?.summary ?? "").join("\n"),
|
|
1064
|
+
children: children.length,
|
|
1065
|
+
},
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
847
1068
|
}
|
|
848
|
-
}
|
|
849
1069
|
|
|
850
|
-
|
|
1070
|
+
if (anyFailure && !continueOnError) break;
|
|
1071
|
+
}
|
|
851
1072
|
}
|
|
852
1073
|
|
|
853
1074
|
const finishedAt = new Date(_deps.now()).toISOString();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Micro-compaction (Claude-Code `/microcompact` parity). Where full compaction
|
|
3
|
+
* summarizes the whole history when it grows large, micro-compaction is
|
|
4
|
+
* surgical: it trims the bulkiest OLD items — large tool results — in place,
|
|
5
|
+
* keeping the conversation flow and the most-recent messages verbatim. Because
|
|
6
|
+
* it only SHORTENS a tool message's content (never removes the message), it can
|
|
7
|
+
* never orphan a tool_call→tool_result pair, so it's safe to run any time.
|
|
8
|
+
*
|
|
9
|
+
* Pure → unit-testable.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const DEFAULT_KEEP_RECENT = 6; // last N messages kept verbatim
|
|
13
|
+
const DEFAULT_MAX_TOOL_CHARS = 400; // trim older tool results longer than this
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {Array} messages OpenAI-shaped messages ({role, content, …}).
|
|
17
|
+
* @param {object} [opts] { keepRecent, maxToolChars }
|
|
18
|
+
* @returns {{ messages: Array, stats: {trimmed:number, saved:number, kept:number} }}
|
|
19
|
+
* A NEW array (originals untouched); `saved` ≈ characters freed.
|
|
20
|
+
*/
|
|
21
|
+
export function microCompact(messages, opts = {}) {
|
|
22
|
+
if (!Array.isArray(messages)) {
|
|
23
|
+
return { messages, stats: { trimmed: 0, saved: 0, kept: 0 } };
|
|
24
|
+
}
|
|
25
|
+
const keepRecent = Number.isFinite(opts.keepRecent)
|
|
26
|
+
? opts.keepRecent
|
|
27
|
+
: DEFAULT_KEEP_RECENT;
|
|
28
|
+
const maxChars = Number.isFinite(opts.maxToolChars)
|
|
29
|
+
? opts.maxToolChars
|
|
30
|
+
: DEFAULT_MAX_TOOL_CHARS;
|
|
31
|
+
// messages at index >= cutoff are "recent" and never touched.
|
|
32
|
+
const cutoff = Math.max(0, messages.length - keepRecent);
|
|
33
|
+
|
|
34
|
+
let trimmed = 0;
|
|
35
|
+
let saved = 0;
|
|
36
|
+
const out = messages.map((m, i) => {
|
|
37
|
+
if (i >= cutoff) return m; // recent → verbatim
|
|
38
|
+
if (!m || m.role !== "tool") return m; // only old tool results
|
|
39
|
+
const content = typeof m.content === "string" ? m.content : "";
|
|
40
|
+
if (content.length <= maxChars) return m; // small enough already
|
|
41
|
+
trimmed += 1;
|
|
42
|
+
saved += content.length - maxChars;
|
|
43
|
+
const head = content.slice(0, maxChars);
|
|
44
|
+
return {
|
|
45
|
+
...m,
|
|
46
|
+
content: `${head}\n… [tool result trimmed — ${content.length} chars; older context dropped to save space]`,
|
|
47
|
+
_microCompacted: true,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return { messages: out, stats: { trimmed, saved, kept: keepRecent } };
|
|
52
|
+
}
|
package/src/lib/output-styles.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import fsDefault from "node:fs";
|
|
18
18
|
import pathDefault from "node:path";
|
|
19
19
|
import { homedir as homedirDefault } from "node:os";
|
|
20
|
+
import { projectRootBase } from "./project-root.cjs";
|
|
20
21
|
|
|
21
22
|
export const _deps = {
|
|
22
23
|
fs: fsDefault,
|
|
@@ -46,7 +47,8 @@ export const BUILTIN_OUTPUT_STYLES = Object.freeze({
|
|
|
46
47
|
},
|
|
47
48
|
learning: {
|
|
48
49
|
name: "learning",
|
|
49
|
-
description:
|
|
50
|
+
description:
|
|
51
|
+
"Collaborative — leaves small instructive pieces for the user.",
|
|
50
52
|
body: [
|
|
51
53
|
"## Output style: Learning",
|
|
52
54
|
"Work collaboratively. When a small, well-scoped piece of the task would be",
|
|
@@ -82,11 +84,31 @@ function parseFrontmatter(content) {
|
|
|
82
84
|
/** Directories scanned for style files (project first, then personal). */
|
|
83
85
|
function styleDirs(cwd, home) {
|
|
84
86
|
const { path } = _deps;
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
+
const dirs = [
|
|
88
|
+
{
|
|
89
|
+
dir: path.join(cwd, ".chainlesschain", "output-styles"),
|
|
90
|
+
scope: "project",
|
|
91
|
+
},
|
|
87
92
|
{ dir: path.join(cwd, ".claude", "output-styles"), scope: "project" },
|
|
88
|
-
{ dir: path.join(home, ".claude", "output-styles"), scope: "personal" },
|
|
89
93
|
];
|
|
94
|
+
// Subdirectory run: also scan the project-root `.claude` (closest cwd wins on
|
|
95
|
+
// a name clash via the reverse + last-write-wins in discoverOutputStyles).
|
|
96
|
+
const root = projectRootBase(cwd, { fs: _deps.fs, path });
|
|
97
|
+
if (root) {
|
|
98
|
+
dirs.push({
|
|
99
|
+
dir: path.join(root, ".chainlesschain", "output-styles"),
|
|
100
|
+
scope: "project",
|
|
101
|
+
});
|
|
102
|
+
dirs.push({
|
|
103
|
+
dir: path.join(root, ".claude", "output-styles"),
|
|
104
|
+
scope: "project",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
dirs.push({
|
|
108
|
+
dir: path.join(home, ".claude", "output-styles"),
|
|
109
|
+
scope: "personal",
|
|
110
|
+
});
|
|
111
|
+
return dirs;
|
|
90
112
|
}
|
|
91
113
|
|
|
92
114
|
/** Discover all styles: built-ins + files (a file shadows a built-in by name). */
|
|
@@ -155,7 +177,11 @@ export function settingsDefaultOutputStyle(cwd = process.cwd(), opts = {}) {
|
|
|
155
177
|
try {
|
|
156
178
|
if (!fs.existsSync(f)) continue;
|
|
157
179
|
const data = JSON.parse(fs.readFileSync(f, "utf-8"));
|
|
158
|
-
if (
|
|
180
|
+
if (
|
|
181
|
+
data &&
|
|
182
|
+
typeof data.outputStyle === "string" &&
|
|
183
|
+
data.outputStyle.trim()
|
|
184
|
+
) {
|
|
159
185
|
value = data.outputStyle.trim();
|
|
160
186
|
}
|
|
161
187
|
} catch {
|
|
@@ -170,8 +196,16 @@ export function settingsDefaultOutputStyle(cwd = process.cwd(), opts = {}) {
|
|
|
170
196
|
* Precedence: explicit name → settings.json `outputStyle` → none.
|
|
171
197
|
* Returns `{ name, body }` (body may be "" for `default`) or null if unresolved.
|
|
172
198
|
*/
|
|
173
|
-
export function resolveOutputStyle(
|
|
174
|
-
|
|
199
|
+
export function resolveOutputStyle(
|
|
200
|
+
explicitName,
|
|
201
|
+
cwd = process.cwd(),
|
|
202
|
+
opts = {},
|
|
203
|
+
) {
|
|
204
|
+
const name = (
|
|
205
|
+
explicitName ||
|
|
206
|
+
settingsDefaultOutputStyle(cwd, opts) ||
|
|
207
|
+
""
|
|
208
|
+
).trim();
|
|
175
209
|
if (!name) return null;
|
|
176
210
|
const style = getOutputStyle(name, cwd, opts);
|
|
177
211
|
if (!style) return { name, body: "", missing: true };
|