@runfusion/fusion 0.73.0-beta.2 → 0.73.0-beta.4
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/agent-browser.mjs +8 -0
- package/dist/bin.js +7664 -11386
- package/dist/child-process-worker.js +4292 -8374
- package/dist/client/.vite/manifest.json +266 -256
- package/dist/client/assets/{AgentDetailView-CUoHZPZr.js → AgentDetailView-BBOL6AgZ.js} +3 -3
- package/dist/client/assets/{AgentPermissionPolicyEditor-DKoHJIlF.js → AgentPermissionPolicyEditor-DV7OPUhR.js} +1 -1
- package/dist/client/assets/{AgentsView-CaBo-FHV.js → AgentsView-B6xoAHyV.js} +4 -4
- package/dist/client/assets/ChatView-DwVjnxM8.js +8 -0
- package/dist/client/assets/{CommandCenter-wgiEIVuC.js → CommandCenter-DYWaoYFD.js} +9 -9
- package/dist/client/assets/DevServerView-BY5up-NA.js +1 -0
- package/dist/client/assets/{DirectoryPicker-B7YwgF53.js → DirectoryPicker-fM8MJa2r.js} +1 -1
- package/dist/client/assets/DocumentsView-D2KxsPG_.js +1 -0
- package/dist/client/assets/{EvalsView-BjyqxMS_.js → EvalsView-U8dOvTRM.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-_PMSa_gN.js → ExperimentalAgentOnboardingModal-C27y8Y-1.js} +1 -1
- package/dist/client/assets/{GoalsView-BzLA8GX9.js → GoalsView-D-2wmy-O.js} +1 -1
- package/dist/client/assets/{InsightsView-Cb_tUr1V.js → InsightsView-zLyuQL_l.js} +2 -2
- package/dist/client/assets/{MemoryView-CRxOPCQq.js → MemoryView-D-tSn48u.js} +2 -2
- package/dist/client/assets/{PiExtensionsManager-XQ5rWJT3.js → PiExtensionsManager-DvwmvGEY.js} +2 -2
- package/dist/client/assets/PluginManager-BACOwQAN.js +1 -0
- package/dist/client/assets/{PullRequestView-CX6fScVe.js → PullRequestView-DULyv21u.js} +2 -2
- package/dist/client/assets/{ReportModal-BSCk5ER1.css → ReportModal-BuhhqtXJ.css} +1 -1
- package/dist/client/assets/ReportModal-CUlKMFWa.js +21 -0
- package/dist/client/assets/{ResearchView-DgKzxRUL.js → ResearchView-DM_O3IFc.js} +2 -2
- package/dist/client/assets/{SecretsView-C83SIrjR.js → SecretsView-Bq3u_nAf.js} +1 -1
- package/dist/client/assets/SessionTerminal-D00ByR6U.js +2 -0
- package/dist/client/assets/SettingsModal-Bb3pIxiX.js +21 -0
- package/dist/client/assets/SettingsModal-CMLHZBhX.css +1 -0
- package/dist/client/assets/SettingsModal-QRaBE1ds.js +1 -0
- package/dist/client/assets/{SettingsTextareaRow-BKGsmZ7C.js → SettingsTextareaRow-CHOJ-qHz.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-DIb4q-VT.js → SetupWizardModal-DriQyd81.js} +2 -2
- package/dist/client/assets/{SkillsView-D1Zxh1iX.js → SkillsView-BtDyujiZ.js} +1 -1
- package/dist/client/assets/{TodoView-CGIcE6Yr.js → TodoView-BNHUnz50.js} +2 -2
- package/dist/client/assets/{WorkflowNodeEditor-BtWrziOX.css → WorkflowNodeEditor-BNgkFJ_P.css} +1 -1
- package/dist/client/assets/WorkflowNodeEditor-BXikFpra.js +8 -0
- package/dist/client/assets/agent-import-generation-B2kYEm1O.js +1 -0
- package/dist/client/assets/app-B_HrdDXZ.js +13 -0
- package/dist/client/assets/{app-BsIXfnu-.js → app-C6yo-M_n.js} +1 -1
- package/dist/client/assets/{app-B-IdUeIu.js → app-CH8ZgPm4.js} +1 -1
- package/dist/client/assets/{app-D9ktpVhR.js → app-D4DpgDss.js} +1 -1
- package/dist/client/assets/{app-nBTNvNKK.js → app-Qv0blCyY.js} +1 -1
- package/dist/client/assets/{app-C8muVNUU.js → app-kFdtajPy.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-Dv83GkUE.js → architectureDiagram-3BPJPVTR-B-Efjj4Z.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-B_j-RJOz.js → blockDiagram-GPEHLZMM-CaOVxrlM.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Cy3f-SD1.js → c4Diagram-AAUBKEIU-D8aYt5F1.js} +1 -1
- package/dist/client/assets/channel-5bPK6pTS.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-CPolddUJ.js → chunk-2J33WTMH-VSDT0J0r.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-BAGPgwkc.js → chunk-4BX2VUAB-Chx1wQgD.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-dzYFOH0q.js → chunk-55IACEB6-MlqjhIJg.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-ul9hGhiR.js → chunk-727SXJPM-BheQNUi8.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-C75yqe4-.js → chunk-AQP2D5EJ-C5EoJhfJ.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BwiLAyup.js → chunk-FMBD7UC4-B8_8qP3j.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-CVv1sLhy.js → chunk-ND2GUHAM-BuglCGRx.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-D1c-k3xL.js → chunk-QZHKN3VN-B7_06dxp.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-Dv9RQDqG.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dv9RQDqG.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-DosMsFd6.js → cose-bilkent-S5V4N54A-Cm-ZOycx.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-9os-QBXe.js → dagre-BM42HDAG-Dj_Gwjpv.js} +1 -1
- package/dist/client/assets/{dashboard-view-CNVTxyWE.js → dashboard-view-B4CRL5Fy.js} +1 -1
- package/dist/client/assets/{dashboard-view-Bn7iL770.js → dashboard-view-noD9p0Zs.js} +1 -1
- package/dist/client/assets/{dashboard-view-iwAS1HTp.js → dashboard-view-pXXSUxG9.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-ChjuJgA6.js → diagram-2AECGRRQ-C_9BfShy.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-Cq10aB4z.js → diagram-5GNKFQAL-Cmg2qpCj.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CKjyrzjg.js → diagram-KO2AKTUF-2FNo2HXb.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DxCc1BsH.js → diagram-LMA3HP47-DgnVeCp-.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-DJWEkDsR.js → diagram-OG6HWLK6-iAIR50HH.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-gVkDYC92.js → erDiagram-TEJ5UH35-Da4I04eN.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-1lw1mQRQ.js → flowDiagram-I6XJVG4X-Bv9r2T0m.js} +1 -1
- package/dist/client/assets/{folder-open-Nmr7nRmN.js → folder-open-CwWtrDh6.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-Yzq4WZRo.js → ganttDiagram-6RSMTGT7-BMeO84U_.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-cFR9Gv8n.js → gitGraphDiagram-PVQCEYII-CWDh_RIb.js} +1 -1
- package/dist/client/assets/index-CB3mYxAB.css +1 -0
- package/dist/client/assets/index-CE7C_XsS.js +2661 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BbRiTnD3.js → infoDiagram-5YYISTIA-BAE4KtCL.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DD4i2Znk.js → ishikawaDiagram-YF4QCWOH-C_iXAuOy.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-qHPO2M-C.js → journeyDiagram-JHISSGLW-BnxSHwDo.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-EuFfgxUv.js → kanban-definition-UN3LZRKU-DYNRm3Nu.js} +1 -1
- package/dist/client/assets/{mermaid.core-Cru9Vzsy.js → mermaid.core-B3hvDDep.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-mCvtfapj.js → mindmap-definition-RKZ34NQL-s7KBEuPD.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BSc_a5Dz.js → pieDiagram-4H26LBE5-Cy1_IPUD.js} +1 -1
- package/dist/client/assets/{puzzle-Cz66CEWW.js → puzzle-DWc6gFQ7.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Um2SLb_d.js → quadrantDiagram-W4KKPZXB-DqgVGp41.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-B94evN7g.js → requirementDiagram-4Y6WPE33-CA5-TDeF.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BH7NLX-K.js → sankeyDiagram-5OEKKPKP-Cuvi3RgE.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DusrBGQp.js → sequenceDiagram-3UESZ5HK-Da3GfmGP.js} +1 -1
- package/dist/client/assets/{shield-alert-CcQuaRHN.js → shield-alert-_iY63ED4.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-CVnY93Xy.js → standing-instructions-template-CCd2YY9c.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-GRjL9YlX.js → stateDiagram-AJRCARHV-CZ_I9ENR.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-BBsv6ppQ.js → stateDiagram-v2-BHNVJYJU-BomoRVhY.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DC6UeqjY.js → timeline-definition-PNZ67QCA-C3CYvIuR.js} +1 -1
- package/dist/client/assets/{upload-CjJp7lEX.js → upload-D0RrO65v.js} +1 -1
- package/dist/client/assets/{users-DgimRYHz.js → users-CGszBY2v.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-C272zK9h.js → vennDiagram-CIIHVFJN-By9fi8NW.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-KkRF-2j9.js → wardley-L42UT6IY-DErnXPkI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B4brtKRt.js → wardleyDiagram-YWT4CUSO-DFfXZVPk.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6--PFSKt1s.js → xychartDiagram-2RQKCTM6-9Y5oZ5mi.js} +1 -1
- package/dist/client/index.html +4 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +4516 -8539
- package/dist/migrations/0000_initial.sql +2 -0
- package/dist/migrations/0026_bigint_counters.sql +85 -14
- package/dist/migrations/0033_fn-8505_wedge_notification.sql +5 -0
- package/dist/plugin-sdk/index.js +1 -0
- package/dist/plugins/.fusion-ce-agents/.fusion-ce-upstream-provenance.json +7 -0
- package/dist/plugins/.fusion-ce-agents/ce-adversarial-document-reviewer.md +115 -0
- package/dist/plugins/.fusion-ce-agents/ce-adversarial-reviewer.md +111 -0
- package/dist/plugins/.fusion-ce-agents/ce-agent-native-planning-strategist.md +71 -0
- package/dist/plugins/.fusion-ce-agents/ce-agent-native-reviewer.md +181 -0
- package/dist/plugins/.fusion-ce-agents/ce-ankane-readme-writer.md +50 -0
- package/dist/plugins/.fusion-ce-agents/ce-api-contract-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-architecture-strategist.md +53 -0
- package/dist/plugins/.fusion-ce-agents/ce-best-practices-researcher.md +122 -0
- package/dist/plugins/.fusion-ce-agents/ce-code-simplicity-reviewer.md +87 -0
- package/dist/plugins/.fusion-ce-agents/ce-coherence-reviewer.md +73 -0
- package/dist/plugins/.fusion-ce-agents/ce-correctness-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-data-integrity-guardian.md +75 -0
- package/dist/plugins/.fusion-ce-agents/ce-data-migration-reviewer.md +119 -0
- package/dist/plugins/.fusion-ce-agents/ce-deployment-verification-agent.md +164 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-implementation-reviewer.md +94 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-iterator.md +197 -0
- package/dist/plugins/.fusion-ce-agents/ce-design-lens-reviewer.md +56 -0
- package/dist/plugins/.fusion-ce-agents/ce-feasibility-reviewer.md +65 -0
- package/dist/plugins/.fusion-ce-agents/ce-figma-design-sync.md +172 -0
- package/dist/plugins/.fusion-ce-agents/ce-framework-docs-researcher.md +100 -0
- package/dist/plugins/.fusion-ce-agents/ce-git-history-analyzer.md +47 -0
- package/dist/plugins/.fusion-ce-agents/ce-issue-intelligence-analyst.md +207 -0
- package/dist/plugins/.fusion-ce-agents/ce-julik-frontend-races-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-learnings-researcher.md +254 -0
- package/dist/plugins/.fusion-ce-agents/ce-maintainability-reviewer.md +77 -0
- package/dist/plugins/.fusion-ce-agents/ce-pattern-recognition-specialist.md +62 -0
- package/dist/plugins/.fusion-ce-agents/ce-performance-oracle.md +115 -0
- package/dist/plugins/.fusion-ce-agents/ce-performance-reviewer.md +54 -0
- package/dist/plugins/.fusion-ce-agents/ce-pr-comment-resolver.md +63 -0
- package/dist/plugins/.fusion-ce-agents/ce-previous-comments-reviewer.md +68 -0
- package/dist/plugins/.fusion-ce-agents/ce-product-lens-reviewer.md +92 -0
- package/dist/plugins/.fusion-ce-agents/ce-project-standards-reviewer.md +84 -0
- package/dist/plugins/.fusion-ce-agents/ce-reliability-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-repo-research-analyst.md +263 -0
- package/dist/plugins/.fusion-ce-agents/ce-scope-guardian-reviewer.md +79 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-lens-reviewer.md +48 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-reviewer.md +54 -0
- package/dist/plugins/.fusion-ce-agents/ce-security-sentinel.md +98 -0
- package/dist/plugins/.fusion-ce-agents/ce-session-historian.md +89 -0
- package/dist/plugins/.fusion-ce-agents/ce-slack-researcher.md +133 -0
- package/dist/plugins/.fusion-ce-agents/ce-spec-flow-analyzer.md +87 -0
- package/dist/plugins/.fusion-ce-agents/ce-swift-ios-reviewer.md +107 -0
- package/dist/plugins/.fusion-ce-agents/ce-testing-reviewer.md +52 -0
- package/dist/plugins/.fusion-ce-agents/ce-web-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/.fusion-ce-upstream-provenance.json +7 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/SKILL.md +318 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/brainstorm-sections.md +354 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/handoff.md +172 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/html-rendering.md +662 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/synthesis-summary.md +271 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/universal-brainstorming.md +71 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/references/visual-probes.md +128 -0
- package/dist/plugins/.fusion-ce-skills/ce-brainstorm/scripts/visual-probe-server.js +419 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/SKILL.md +821 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/action-class-rubric.md +26 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/bulk-preview.md +112 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/cross-model-review.md +63 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/diff-scope.md +41 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/findings-schema.json +137 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/persona-catalog.md +63 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/adversarial-reviewer.md +102 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/api-contract-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/correctness-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +44 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/maintainability-reviewer.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/project-standards-reviewer.md +75 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/reliability-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/security-reviewer.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/personas/testing-reviewer.md +43 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/review-output-template.md +170 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/subagent-template.md +199 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/tracker-defer.md +149 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/validator-template.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/references/walkthrough.md +249 -0
- package/dist/plugins/.fusion-ce-skills/ce-code-review/scripts/cross-model-adversarial-review.sh +218 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit/SKILL.md +105 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/SKILL.md +134 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/branch-creation.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-commit-push-pr/references/pr-description-writing.md +115 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/SKILL.md +712 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/assets/resolution-template.md +94 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/agents/session-historian.md +83 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/concepts-vocabulary.md +78 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/schema.yaml +231 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/references/yaml-schema.md +118 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/session-history/extract-skeleton.py +570 -0
- package/dist/plugins/.fusion-ce-skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/SKILL.md +257 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/anti-patterns.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/defense-in-depth.md +35 -0
- package/dist/plugins/.fusion-ce-skills/ce-debug/references/investigation-techniques.md +374 -0
- package/dist/plugins/.fusion-ce-skills/ce-doc-review/SKILL.md +70 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/SKILL.md +401 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/divergent-ideation.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/html-rendering.md +662 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/ideation-sections.md +191 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/post-ideation-workflow.md +166 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/universal-ideation.md +107 -0
- package/dist/plugins/.fusion-ce-skills/ce-ideate/references/web-research-cache.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/SKILL.md +858 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/learnings-researcher.md +247 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/repo-research-analyst.md +256 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/slack-researcher.md +127 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/agents/web-researcher.md +121 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/approach-altitude.md +55 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/deepening-workflow.md +259 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/html-rendering.md +668 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/markdown-rendering.md +236 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-handoff.md +126 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/plan-sections.md +405 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/synthesis-summary.md +396 -0
- package/dist/plugins/.fusion-ce-skills/ce-plan/references/universal-planning.md +168 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/SKILL.md +53 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +56 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +106 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/full-mode.md +283 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/references/targeted-mode.md +45 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-pr-comments +159 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +76 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +33 -0
- package/dist/plugins/.fusion-ce-skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/SKILL.md +97 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/references/interview.md +143 -0
- package/dist/plugins/.fusion-ce-skills/ce-strategy/references/strategy-template.md +89 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/SKILL.md +429 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/agents/figma-design-sync.md +165 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/execution-engines.md +85 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/non-code-execution.md +23 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/review-findings-followup.md +104 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/shipping-workflow.md +133 -0
- package/dist/plugins/.fusion-ce-skills/ce-work/references/tracker-defer.md +149 -0
- package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-3.js +11266 -0
- package/dist/plugins/fusion-plugin-compound-engineering/.bundled.reload-4.js +11266 -0
- package/dist/plugins/fusion-plugin-dependency-graph/.bundled.reload-1.js +8206 -0
- package/dist/plugins/fusion-plugin-grok-runtime/.bundled.reload-2.js +26623 -0
- package/package.json +6 -3
- package/skill/fusion/references/engine-tools.md +6 -2
- package/dist/client/assets/ChatView-Bv0J5p5U.js +0 -8
- package/dist/client/assets/DevServerView-Ue9XG_4H.js +0 -1
- package/dist/client/assets/DocumentsView-C1Ptwcv5.js +0 -1
- package/dist/client/assets/PluginManager-CXPSlWxs.js +0 -1
- package/dist/client/assets/ReportModal-JhZZXZlj.js +0 -21
- package/dist/client/assets/SessionTerminal-BrK3psiC.js +0 -2
- package/dist/client/assets/SettingsModal-Bby9vLGx.js +0 -21
- package/dist/client/assets/SettingsModal-DVLqY1-7.js +0 -1
- package/dist/client/assets/SettingsModal-DXArgTTx.css +0 -1
- package/dist/client/assets/WorkflowNodeEditor-BXUC0lim.js +0 -8
- package/dist/client/assets/app-DUszvars.js +0 -13
- package/dist/client/assets/channel-BuhC8kaT.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-vpRR5WOg.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-vpRR5WOg.js +0 -1
- package/dist/client/assets/index-Cg9ahVtV.js +0 -2661
- package/dist/client/assets/index-uhXHk1ek.css +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Visual Probes
|
|
2
|
+
|
|
3
|
+
Use visual probes when a brainstorm decision is faster to judge by seeing a rough artifact than by reading prose. A visual probe is a disposable decision sketch, not a prototype, implementation plan, UI spec, or design deliverable.
|
|
4
|
+
|
|
5
|
+
## Trigger
|
|
6
|
+
|
|
7
|
+
Use this reference only when the next question has a specific visual decision:
|
|
8
|
+
|
|
9
|
+
- behavior shape: "Which annotation or drawing behavior feels right?"
|
|
10
|
+
- layout shape: "Which navigation structure matches the workflow?"
|
|
11
|
+
- flow shape: "Where should this decision point sit?"
|
|
12
|
+
- state shape: "Which empty/loading/error state communicates the right thing?"
|
|
13
|
+
- diagram shape: "Which relationship or system boundary is clearer?"
|
|
14
|
+
|
|
15
|
+
Do not use a visual probe for product goals, scope boundaries, success criteria, evidence probes, tradeoff prose, or technical decisions that are easier to discuss in chat.
|
|
16
|
+
|
|
17
|
+
## Offer
|
|
18
|
+
|
|
19
|
+
Ask once at the decision point. Do not enable a session-wide mode.
|
|
20
|
+
|
|
21
|
+
Use the platform's blocking question tool for the opt-in when available (`AskUserQuestion`, `request_user_input`, `ask_user`, or equivalent). Use a plain chat question only when no interactive question tool exists or the tool errors. The opt-in should have two clear options:
|
|
22
|
+
|
|
23
|
+
- Visual sketch — create rough options in a local browser
|
|
24
|
+
- Text description — keep the decision in chat
|
|
25
|
+
|
|
26
|
+
Use this wording:
|
|
27
|
+
|
|
28
|
+
> This decision may be easier to judge visually. I can either sketch rough options in a local browser so you can react to the shape, or keep it in chat and describe the options textually, which is faster but lower-fidelity. Which do you prefer?
|
|
29
|
+
|
|
30
|
+
The text path must be credible. If you cannot explain the decision clearly in text, you do not understand it well enough to sketch it.
|
|
31
|
+
|
|
32
|
+
If the user chooses text, continue in chat and do not re-offer for the same decision. If they choose visual, proceed below.
|
|
33
|
+
|
|
34
|
+
## Visual Path
|
|
35
|
+
|
|
36
|
+
Create the cheapest artifact that answers the current question. Optimize for fast feedback, not polish.
|
|
37
|
+
|
|
38
|
+
Allowed:
|
|
39
|
+
|
|
40
|
+
- rough behavior sketches
|
|
41
|
+
- low-fidelity wireframes
|
|
42
|
+
- state comparisons
|
|
43
|
+
- flow diagrams
|
|
44
|
+
- simple A/B/C visual contrasts
|
|
45
|
+
- disposable interaction demos only when behavior itself is the decision
|
|
46
|
+
|
|
47
|
+
Avoid:
|
|
48
|
+
|
|
49
|
+
- polished branding
|
|
50
|
+
- final colors or typography
|
|
51
|
+
- component-library precision
|
|
52
|
+
- pixel-perfect layout
|
|
53
|
+
- production-like implementation
|
|
54
|
+
- unnecessary animation
|
|
55
|
+
- details that imply exact UI commitments
|
|
56
|
+
|
|
57
|
+
Label the artifact as directional. State what the user should judge and what they should ignore.
|
|
58
|
+
|
|
59
|
+
## Display Helper
|
|
60
|
+
|
|
61
|
+
Use the bundled display-only helper when the current platform can run a bundled skill script:
|
|
62
|
+
|
|
63
|
+
- Helper: `scripts/visual-probe-server.js`
|
|
64
|
+
- Resolve the helper path relative to the loaded `ce-brainstorm` skill directory before running it. Do not resolve it from the user's project CWD.
|
|
65
|
+
- Start: `node <resolved-helper-path> start --root /tmp/compound-engineering/ce-brainstorm-visual/<run-id>`
|
|
66
|
+
- Start foreground: `node <resolved-helper-path> start --root /tmp/compound-engineering/ce-brainstorm-visual/<run-id> --foreground`
|
|
67
|
+
- Status: `node <resolved-helper-path> status --root /tmp/compound-engineering/ce-brainstorm-visual/<run-id>`
|
|
68
|
+
- Stop: `node <resolved-helper-path> stop --root /tmp/compound-engineering/ce-brainstorm-visual/<run-id>`
|
|
69
|
+
|
|
70
|
+
The helper creates `screens/` and `state/`, serves the newest `.html` file in `screens/`, writes `state/display-info.json`, and exposes `/version` so the browser can poll for screen changes. The browser reloads only when the newest screen changes; it must not continually reload on a timer. `/version` polling does not count as activity, so an abandoned browser tab cannot keep the server alive forever. Detached servers monitor the owning harness process when it can be resolved, and all servers exit after an idle timeout. The helper has no click tracking or browser-to-agent event path.
|
|
71
|
+
|
|
72
|
+
If the helper path is unavailable or the platform cannot display a local URL cleanly, say so briefly and use the text path. Do not build a custom event system or long-lived server to compensate during the brainstorm.
|
|
73
|
+
|
|
74
|
+
## Launch Mode by Platform
|
|
75
|
+
|
|
76
|
+
The server is the same everywhere; only the launch mode changes.
|
|
77
|
+
|
|
78
|
+
- **Claude Code / Claude desktop app:** detached `start` is the default path. If the app opens localhost URLs, show the returned URL and continue. If the browser surface is unavailable, use the text path.
|
|
79
|
+
- **Codex CLI / Codex app:** if detached processes are reaped or the URL dies after the tool call, use `start --foreground` through the platform's long-running/background terminal mechanism. If there is no stable browser surface, use the text path.
|
|
80
|
+
- **Plain terminal UI:** print the returned URL for the user to open manually. If opening a browser would interrupt the flow, keep the decision in chat.
|
|
81
|
+
- **Remote or containerized sessions:** if `localhost` is not reachable from the user's browser, start with `--host 0.0.0.0` and tell the user which host/port to open. If that cannot be made clear, use the text path.
|
|
82
|
+
|
|
83
|
+
Never force the visual path because a local server exists. The user chose visual to understand the decision faster; if the platform plumbing gets in the way, switch back to text.
|
|
84
|
+
|
|
85
|
+
## Post-Artifact Feedback
|
|
86
|
+
|
|
87
|
+
After showing the visual artifact, use the platform's blocking question tool for bounded artifact feedback when available. This is still chat-based feedback, not browser event capture.
|
|
88
|
+
|
|
89
|
+
Use a bounded interactive question when the expected response is a small choice set:
|
|
90
|
+
|
|
91
|
+
- A/B/C/D option selection
|
|
92
|
+
- visual direction vs mix
|
|
93
|
+
- choose one layout/state/behavior
|
|
94
|
+
- accept one option with requested tweaks
|
|
95
|
+
|
|
96
|
+
Include a free-text fallback option when the tool supports it. Use plain chat only when feedback is genuinely open critique, no interactive question tool exists, or the tool errors.
|
|
97
|
+
|
|
98
|
+
Good post-artifact prompt:
|
|
99
|
+
|
|
100
|
+
> Which direction best matches what you want? Pick A, B, C, D, or mix, and use the free-text fallback for anything that feels off. Judge the behavior shape, not the exact styling.
|
|
101
|
+
|
|
102
|
+
Do not ask the user to click inside the browser artifact. The question tool is for the chat/session response after the artifact is visible.
|
|
103
|
+
|
|
104
|
+
## Interaction Contract
|
|
105
|
+
|
|
106
|
+
The browser/artifact is display-only. Feedback happens in chat.
|
|
107
|
+
|
|
108
|
+
Do not add click tracking, selected states, event ingestion, forms, analytics, or "submit" affordances in v1. Do not ask the user to click an option. Ask them to look at the artifact and reply in chat with the choice, mix, or correction.
|
|
109
|
+
|
|
110
|
+
If no interactive question tool is available, use this plain-chat fallback after showing the artifact:
|
|
111
|
+
|
|
112
|
+
> I’m showing three rough options. Reply here with A, B, C, or "mix", plus anything that feels off. Judge the behavior shape, not the exact styling.
|
|
113
|
+
|
|
114
|
+
The user's chat response is authoritative. The visual artifact is supporting context only.
|
|
115
|
+
|
|
116
|
+
## File Placement
|
|
117
|
+
|
|
118
|
+
Use OS temp by default because visual probes are disposable scratch:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
/tmp/compound-engineering/ce-brainstorm-visual/<run-id>/
|
|
122
|
+
screens/
|
|
123
|
+
001-<decision>.html
|
|
124
|
+
state/
|
|
125
|
+
display-info.json
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use `.context/compound-engineering/ce-brainstorm-visual/<run-id>/` only when the user explicitly wants to inspect, preserve, or curate the sketches after the session. The requirements-only unified plan in `docs/plans/` is the durable artifact.
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* global console, process, setInterval, setTimeout */
|
|
3
|
+
import { execFileSync, spawn } from "node:child_process"
|
|
4
|
+
import fs from "node:fs"
|
|
5
|
+
import http from "node:http"
|
|
6
|
+
import path from "node:path"
|
|
7
|
+
import { fileURLToPath } from "node:url"
|
|
8
|
+
|
|
9
|
+
const scriptPath = fileURLToPath(import.meta.url)
|
|
10
|
+
const DEFAULT_HOST = "127.0.0.1"
|
|
11
|
+
const DEFAULT_URL_HOST = "localhost"
|
|
12
|
+
const IDLE_TIMEOUT_MS = Number(process.env.CE_VISUAL_PROBE_IDLE_TIMEOUT_MS) || 30 * 60 * 1000
|
|
13
|
+
const LIFECYCLE_CHECK_MS = Number(process.env.CE_VISUAL_PROBE_LIFECYCLE_CHECK_MS) || 60 * 1000
|
|
14
|
+
|
|
15
|
+
function usage() {
|
|
16
|
+
return [
|
|
17
|
+
"Usage:",
|
|
18
|
+
" node visual-probe-server.js start --root <dir> [--host 127.0.0.1] [--port 0] [--foreground] [--owner-pid <pid>]",
|
|
19
|
+
" node visual-probe-server.js stop --root <dir>",
|
|
20
|
+
" node visual-probe-server.js status --root <dir>",
|
|
21
|
+
].join("\n")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const command = argv[2]
|
|
26
|
+
const options = {
|
|
27
|
+
command,
|
|
28
|
+
host: DEFAULT_HOST,
|
|
29
|
+
port: 0,
|
|
30
|
+
foreground: false,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (let i = 3; i < argv.length; i++) {
|
|
34
|
+
const arg = argv[i]
|
|
35
|
+
if (arg === "--root") {
|
|
36
|
+
options.root = argv[++i]
|
|
37
|
+
} else if (arg === "--host") {
|
|
38
|
+
options.host = argv[++i]
|
|
39
|
+
} else if (arg === "--port") {
|
|
40
|
+
options.port = Number(argv[++i])
|
|
41
|
+
} else if (arg === "--foreground") {
|
|
42
|
+
options.foreground = true
|
|
43
|
+
} else if (arg === "--owner-pid") {
|
|
44
|
+
options.ownerPid = Number(argv[++i])
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`Unknown argument: ${arg}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!["start", "serve", "stop", "status"].includes(command)) {
|
|
51
|
+
throw new Error(usage())
|
|
52
|
+
}
|
|
53
|
+
if (!options.root) {
|
|
54
|
+
throw new Error("--root is required")
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535) {
|
|
57
|
+
throw new Error("--port must be an integer from 0 to 65535")
|
|
58
|
+
}
|
|
59
|
+
if (options.ownerPid !== undefined && (!Number.isInteger(options.ownerPid) || options.ownerPid <= 1)) {
|
|
60
|
+
throw new Error("--owner-pid must be an integer greater than 1")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
options.root = path.resolve(options.root)
|
|
64
|
+
options.screensDir = path.join(options.root, "screens")
|
|
65
|
+
options.stateDir = path.join(options.root, "state")
|
|
66
|
+
options.pidFile = path.join(options.stateDir, "server.pid")
|
|
67
|
+
options.infoFile = path.join(options.stateDir, "display-info.json")
|
|
68
|
+
options.logFile = path.join(options.stateDir, "server.log")
|
|
69
|
+
return options
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function ensureDirs(options) {
|
|
73
|
+
fs.mkdirSync(options.screensDir, { recursive: true })
|
|
74
|
+
fs.mkdirSync(options.stateDir, { recursive: true })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function jsonOut(value) {
|
|
78
|
+
process.stdout.write(`${JSON.stringify(value)}\n`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readJson(filePath) {
|
|
82
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function processAlive(pid) {
|
|
86
|
+
if (!pid || !Number.isInteger(pid)) return false
|
|
87
|
+
try {
|
|
88
|
+
process.kill(pid, 0)
|
|
89
|
+
return true
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return error?.code === "EPERM"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function processArgs(pid) {
|
|
96
|
+
try {
|
|
97
|
+
return execFileSync("ps", ["-p", String(pid), "-o", "args="], {
|
|
98
|
+
encoding: "utf8",
|
|
99
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
100
|
+
}).trim()
|
|
101
|
+
} catch {
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function ownsServerProcess(options, pid) {
|
|
107
|
+
const args = processArgs(pid)
|
|
108
|
+
// Process-command inspection is best-effort; when unavailable, fall back to
|
|
109
|
+
// PID-file behavior so stop still works on platforms without a compatible ps.
|
|
110
|
+
if (args === null) return true
|
|
111
|
+
return args.includes(scriptPath) && args.includes("serve") && args.includes(options.root)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function resolveOwnerPid() {
|
|
115
|
+
const parentPid = process.ppid
|
|
116
|
+
if (!parentPid || parentPid <= 1) return null
|
|
117
|
+
try {
|
|
118
|
+
const grandparent = Number(execFileSync("ps", ["-o", "ppid=", "-p", String(parentPid)], {
|
|
119
|
+
encoding: "utf8",
|
|
120
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
121
|
+
}).trim())
|
|
122
|
+
if (Number.isInteger(grandparent) && grandparent > 1) return grandparent
|
|
123
|
+
} catch {
|
|
124
|
+
// Fall back to the direct parent when grandparent lookup is unavailable.
|
|
125
|
+
}
|
|
126
|
+
return parentPid
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function readPid(options) {
|
|
130
|
+
if (!fs.existsSync(options.pidFile)) return null
|
|
131
|
+
const pid = Number(fs.readFileSync(options.pidFile, "utf8").trim())
|
|
132
|
+
return Number.isInteger(pid) ? pid : null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getRunningInfo(options) {
|
|
136
|
+
const pid = readPid(options)
|
|
137
|
+
if (!processAlive(pid)) return null
|
|
138
|
+
if (!ownsServerProcess(options, pid)) return null
|
|
139
|
+
if (!fs.existsSync(options.infoFile)) return null
|
|
140
|
+
return readJson(options.infoFile)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function newestScreen(options) {
|
|
144
|
+
if (!fs.existsSync(options.screensDir)) return null
|
|
145
|
+
const files = fs.readdirSync(options.screensDir)
|
|
146
|
+
.filter((file) => file.endsWith(".html"))
|
|
147
|
+
.map((file) => {
|
|
148
|
+
const filePath = path.join(options.screensDir, file)
|
|
149
|
+
return { filePath, mtimeMs: fs.statSync(filePath).mtimeMs }
|
|
150
|
+
})
|
|
151
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs)
|
|
152
|
+
return files[0]?.filePath ?? null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isFullDocument(html) {
|
|
156
|
+
const trimmed = html.trimStart().toLowerCase()
|
|
157
|
+
return trimmed.startsWith("<!doctype") || trimmed.startsWith("<html")
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function screenVersion(options) {
|
|
161
|
+
const screen = newestScreen(options)
|
|
162
|
+
if (!screen) return { screen: null, mtimeMs: 0 }
|
|
163
|
+
return {
|
|
164
|
+
screen: path.basename(screen),
|
|
165
|
+
mtimeMs: fs.statSync(screen).mtimeMs,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function refreshScript(options) {
|
|
170
|
+
const initialVersion = JSON.stringify(screenVersion(options))
|
|
171
|
+
return `<script>
|
|
172
|
+
(function(){
|
|
173
|
+
var currentVersion = ${initialVersion};
|
|
174
|
+
function key(version) {
|
|
175
|
+
return String(version && version.screen) + ":" + String(version && version.mtimeMs);
|
|
176
|
+
}
|
|
177
|
+
async function checkForVisualProbeUpdate() {
|
|
178
|
+
try {
|
|
179
|
+
var response = await fetch("/version", { cache: "no-store" });
|
|
180
|
+
if (!response.ok) return;
|
|
181
|
+
var nextVersion = await response.json();
|
|
182
|
+
if (key(nextVersion) !== key(currentVersion)) {
|
|
183
|
+
window.location.reload();
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
// Keep the current sketch visible if the transient version check fails.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
setInterval(checkForVisualProbeUpdate, 1000);
|
|
190
|
+
})();
|
|
191
|
+
</script>`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function wrapFragment(options, content) {
|
|
195
|
+
return `<!doctype html>
|
|
196
|
+
<html>
|
|
197
|
+
<head>
|
|
198
|
+
<meta charset="utf-8">
|
|
199
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
200
|
+
<title>CE Brainstorm Visual Probe</title>
|
|
201
|
+
<style>
|
|
202
|
+
body { margin: 0; font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; background: #f7f7f8; color: #1f2328; }
|
|
203
|
+
header { padding: 10px 18px; border-bottom: 1px solid #d8dee4; background: #fff; color: #57606a; font-size: 13px; }
|
|
204
|
+
main { padding: 24px; }
|
|
205
|
+
</style>
|
|
206
|
+
</head>
|
|
207
|
+
<body>
|
|
208
|
+
<header>CE Brainstorm Visual Probe - directional sketch, reply in chat</header>
|
|
209
|
+
<main>${content}</main>
|
|
210
|
+
${refreshScript(options)}
|
|
211
|
+
</body>
|
|
212
|
+
</html>`
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function injectRefresh(options, html) {
|
|
216
|
+
if (html.includes("</body>")) {
|
|
217
|
+
return html.replace("</body>", `${refreshScript(options)}\n</body>`)
|
|
218
|
+
}
|
|
219
|
+
return `${html}\n${refreshScript(options)}`
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function renderPage(options) {
|
|
223
|
+
const screen = newestScreen(options)
|
|
224
|
+
if (!screen) {
|
|
225
|
+
return wrapFragment(options, "<h1>Waiting for a visual probe...</h1><p>The agent will update this page when a sketch is ready.</p>")
|
|
226
|
+
}
|
|
227
|
+
const html = fs.readFileSync(screen, "utf8")
|
|
228
|
+
return isFullDocument(html) ? injectRefresh(options, html) : wrapFragment(options, html)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function safeFileResponse(options, req, res) {
|
|
232
|
+
const name = decodeURIComponent(req.url.slice("/files/".length))
|
|
233
|
+
const filePath = path.join(options.screensDir, path.basename(name))
|
|
234
|
+
if (!fs.existsSync(filePath)) {
|
|
235
|
+
res.writeHead(404)
|
|
236
|
+
res.end("Not found")
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
res.writeHead(200, { "Content-Type": contentType(filePath) })
|
|
240
|
+
res.end(fs.readFileSync(filePath))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function contentType(filePath) {
|
|
244
|
+
const ext = path.extname(filePath).toLowerCase()
|
|
245
|
+
if (ext === ".svg") return "image/svg+xml"
|
|
246
|
+
if (ext === ".png") return "image/png"
|
|
247
|
+
if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg"
|
|
248
|
+
if (ext === ".gif") return "image/gif"
|
|
249
|
+
if (ext === ".html") return "text/html; charset=utf-8"
|
|
250
|
+
if (ext === ".css") return "text/css; charset=utf-8"
|
|
251
|
+
if (ext === ".js") return "text/javascript; charset=utf-8"
|
|
252
|
+
return "application/octet-stream"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function start(options) {
|
|
256
|
+
ensureDirs(options)
|
|
257
|
+
options.ownerPid = options.ownerPid ?? resolveOwnerPid()
|
|
258
|
+
const running = getRunningInfo(options)
|
|
259
|
+
if (running) {
|
|
260
|
+
jsonOut({ ...running, status: "running" })
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fs.rmSync(options.pidFile, { force: true })
|
|
265
|
+
fs.rmSync(options.infoFile, { force: true })
|
|
266
|
+
|
|
267
|
+
if (options.foreground) {
|
|
268
|
+
await serve(options)
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const logFd = fs.openSync(options.logFile, "a")
|
|
273
|
+
const child = spawn(process.execPath, [
|
|
274
|
+
scriptPath,
|
|
275
|
+
"serve",
|
|
276
|
+
"--root",
|
|
277
|
+
options.root,
|
|
278
|
+
"--host",
|
|
279
|
+
options.host,
|
|
280
|
+
"--port",
|
|
281
|
+
String(options.port),
|
|
282
|
+
...(options.ownerPid ? ["--owner-pid", String(options.ownerPid)] : []),
|
|
283
|
+
], {
|
|
284
|
+
detached: true,
|
|
285
|
+
stdio: ["ignore", logFd, logFd],
|
|
286
|
+
})
|
|
287
|
+
child.unref()
|
|
288
|
+
fs.closeSync(logFd)
|
|
289
|
+
|
|
290
|
+
const started = await waitForInfo(options, child.pid)
|
|
291
|
+
if (!started) {
|
|
292
|
+
throw new Error(`Server failed to start. See ${options.logFile}`)
|
|
293
|
+
}
|
|
294
|
+
jsonOut({ ...started, status: "started" })
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function waitForInfo(options, pid) {
|
|
298
|
+
for (let i = 0; i < 100; i++) {
|
|
299
|
+
if (fs.existsSync(options.infoFile)) return readJson(options.infoFile)
|
|
300
|
+
if (pid && !processAlive(pid)) return null
|
|
301
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
302
|
+
}
|
|
303
|
+
return null
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function serve(options) {
|
|
307
|
+
ensureDirs(options)
|
|
308
|
+
|
|
309
|
+
let lastActivity = Date.now()
|
|
310
|
+
const touch = () => {
|
|
311
|
+
lastActivity = Date.now()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const server = http.createServer((req, res) => {
|
|
315
|
+
if (req.method === "GET" && req.url === "/") {
|
|
316
|
+
touch()
|
|
317
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
318
|
+
res.end(renderPage(options))
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
if (req.method === "GET" && req.url === "/version") {
|
|
322
|
+
res.writeHead(200, {
|
|
323
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
324
|
+
"Cache-Control": "no-store",
|
|
325
|
+
})
|
|
326
|
+
res.end(`${JSON.stringify(screenVersion(options))}\n`)
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
if (req.method === "GET" && req.url.startsWith("/files/")) {
|
|
330
|
+
touch()
|
|
331
|
+
safeFileResponse(options, req, res)
|
|
332
|
+
return
|
|
333
|
+
}
|
|
334
|
+
res.writeHead(404)
|
|
335
|
+
res.end("Not found")
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
server.listen(options.port, options.host, () => {
|
|
339
|
+
const address = server.address()
|
|
340
|
+
const port = typeof address === "object" && address ? address.port : options.port
|
|
341
|
+
const info = {
|
|
342
|
+
status: "running",
|
|
343
|
+
root: options.root,
|
|
344
|
+
host: options.host,
|
|
345
|
+
port,
|
|
346
|
+
url: `http://${DEFAULT_URL_HOST}:${port}`,
|
|
347
|
+
screen_dir: options.screensDir,
|
|
348
|
+
state_dir: options.stateDir,
|
|
349
|
+
pid: process.pid,
|
|
350
|
+
owner_pid: options.ownerPid ?? null,
|
|
351
|
+
}
|
|
352
|
+
fs.writeFileSync(options.pidFile, `${process.pid}\n`)
|
|
353
|
+
fs.writeFileSync(options.infoFile, `${JSON.stringify(info, null, 2)}\n`)
|
|
354
|
+
console.log(JSON.stringify(info))
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
const idleTimer = setInterval(() => {
|
|
358
|
+
if (options.ownerPid && !processAlive(options.ownerPid)) {
|
|
359
|
+
server.close(() => process.exit(0))
|
|
360
|
+
} else if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) {
|
|
361
|
+
server.close(() => process.exit(0))
|
|
362
|
+
}
|
|
363
|
+
}, LIFECYCLE_CHECK_MS)
|
|
364
|
+
idleTimer.unref()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function stop(options) {
|
|
368
|
+
const pid = readPid(options)
|
|
369
|
+
if (!processAlive(pid)) {
|
|
370
|
+
fs.rmSync(options.pidFile, { force: true })
|
|
371
|
+
jsonOut({ status: "stopped", root: options.root })
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
if (!ownsServerProcess(options, pid)) {
|
|
375
|
+
fs.rmSync(options.pidFile, { force: true })
|
|
376
|
+
jsonOut({ status: "stopped", root: options.root })
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
process.kill(pid)
|
|
381
|
+
for (let i = 0; i < 20; i++) {
|
|
382
|
+
if (!processAlive(pid)) break
|
|
383
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
384
|
+
}
|
|
385
|
+
if (processAlive(pid)) {
|
|
386
|
+
try {
|
|
387
|
+
process.kill(pid, "SIGKILL")
|
|
388
|
+
} catch {
|
|
389
|
+
// Process may have exited between the liveness check and kill.
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
fs.rmSync(options.pidFile, { force: true })
|
|
394
|
+
jsonOut({ status: "stopped", root: options.root })
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function status(options) {
|
|
398
|
+
const info = getRunningInfo(options)
|
|
399
|
+
if (!info) {
|
|
400
|
+
jsonOut({ status: "stopped", root: options.root })
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
jsonOut({ ...info, status: "running" })
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function main() {
|
|
407
|
+
try {
|
|
408
|
+
const options = parseArgs(process.argv)
|
|
409
|
+
if (options.command === "start") await start(options)
|
|
410
|
+
else if (options.command === "serve") await serve(options)
|
|
411
|
+
else if (options.command === "stop") await stop(options)
|
|
412
|
+
else if (options.command === "status") status(options)
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error(error.message)
|
|
415
|
+
process.exit(1)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
await main()
|