@claude-flow/cli 3.42.2 → 3.42.3
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/.claude/agents/analysis/analyze-code-quality.md +178 -178
- package/.claude/agents/analysis/code-analyzer.md +209 -209
- package/.claude/agents/analysis/code-review/analyze-code-quality.md +178 -178
- package/.claude/agents/architecture/arch-system-design.md +156 -156
- package/.claude/agents/architecture/system-design/arch-system-design.md +154 -154
- package/.claude/agents/browser/browser-agent.yaml +182 -182
- package/.claude/agents/consensus/byzantine-coordinator.md +62 -62
- package/.claude/agents/consensus/crdt-synchronizer.md +996 -996
- package/.claude/agents/consensus/gossip-coordinator.md +62 -62
- package/.claude/agents/consensus/performance-benchmarker.md +850 -850
- package/.claude/agents/consensus/quorum-manager.md +822 -822
- package/.claude/agents/consensus/raft-manager.md +62 -62
- package/.claude/agents/consensus/security-manager.md +621 -621
- package/.claude/agents/core/planner.md +374 -374
- package/.claude/agents/custom/test-long-runner.md +44 -44
- package/.claude/agents/data/data-ml-model.md +444 -444
- package/.claude/agents/data/ml/data-ml-model.md +192 -192
- package/.claude/agents/development/backend/dev-backend-api.md +141 -141
- package/.claude/agents/development/dev-backend-api.md +344 -344
- package/.claude/agents/devops/ci-cd/ops-cicd-github.md +163 -163
- package/.claude/agents/devops/ops-cicd-github.md +164 -164
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +173 -173
- package/.claude/agents/documentation/docs-api-openapi.md +354 -354
- package/.claude/agents/flow-nexus/app-store.md +87 -87
- package/.claude/agents/flow-nexus/authentication.md +68 -68
- package/.claude/agents/flow-nexus/challenges.md +80 -80
- package/.claude/agents/flow-nexus/neural-network.md +87 -87
- package/.claude/agents/flow-nexus/payments.md +82 -82
- package/.claude/agents/flow-nexus/sandbox.md +75 -75
- package/.claude/agents/flow-nexus/swarm.md +75 -75
- package/.claude/agents/flow-nexus/user-tools.md +95 -95
- package/.claude/agents/flow-nexus/workflow.md +83 -83
- package/.claude/agents/github/code-review-swarm.md +377 -377
- package/.claude/agents/github/github-modes.md +172 -172
- package/.claude/agents/github/issue-tracker.md +575 -575
- package/.claude/agents/github/multi-repo-swarm.md +552 -552
- package/.claude/agents/github/pr-manager.md +437 -437
- package/.claude/agents/github/project-board-sync.md +508 -508
- package/.claude/agents/github/release-manager.md +604 -604
- package/.claude/agents/github/release-swarm.md +582 -582
- package/.claude/agents/github/repo-architect.md +397 -397
- package/.claude/agents/github/swarm-issue.md +572 -572
- package/.claude/agents/github/swarm-pr.md +427 -427
- package/.claude/agents/github/sync-coordinator.md +451 -451
- package/.claude/agents/github/workflow-automation.md +902 -902
- package/.claude/agents/goal/agent.md +815 -815
- package/.claude/agents/optimization/benchmark-suite.md +664 -664
- package/.claude/agents/optimization/load-balancer.md +430 -430
- package/.claude/agents/optimization/performance-monitor.md +671 -671
- package/.claude/agents/optimization/resource-allocator.md +673 -673
- package/.claude/agents/optimization/topology-optimizer.md +807 -807
- package/.claude/agents/payments/agentic-payments.md +126 -126
- package/.claude/agents/sona/sona-learning-optimizer.md +74 -74
- package/.claude/agents/sparc/architecture.md +698 -698
- package/.claude/agents/sparc/pseudocode.md +519 -519
- package/.claude/agents/sparc/refinement.md +801 -801
- package/.claude/agents/sparc/specification.md +477 -477
- package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +224 -224
- package/.claude/agents/specialized/spec-mobile-react-native.md +226 -226
- package/.claude/agents/sublinear/consensus-coordinator.md +337 -337
- package/.claude/agents/sublinear/matrix-optimizer.md +184 -184
- package/.claude/agents/sublinear/pagerank-analyzer.md +298 -298
- package/.claude/agents/sublinear/performance-optimizer.md +367 -367
- package/.claude/agents/sublinear/trading-predictor.md +245 -245
- package/.claude/agents/swarm/adaptive-coordinator.md +1126 -1126
- package/.claude/agents/swarm/hierarchical-coordinator.md +709 -709
- package/.claude/agents/swarm/mesh-coordinator.md +962 -962
- package/.claude/agents/templates/automation-smart-agent.md +204 -204
- package/.claude/agents/templates/base-template-generator.md +289 -289
- package/.claude/agents/templates/coordinator-swarm-init.md +89 -89
- package/.claude/agents/templates/github-pr-manager.md +176 -176
- package/.claude/agents/templates/implementer-sparc-coder.md +258 -258
- package/.claude/agents/templates/memory-coordinator.md +186 -186
- package/.claude/agents/templates/orchestrator-task.md +138 -138
- package/.claude/agents/templates/performance-analyzer.md +198 -198
- package/.claude/agents/templates/sparc-coordinator.md +513 -513
- package/.claude/agents/testing/production-validator.md +394 -394
- package/.claude/agents/testing/tdd-london-swarm.md +243 -243
- package/.claude/agents/v3/aidefence-guardian.md +282 -282
- package/.claude/agents/v3/claims-authorizer.md +208 -208
- package/.claude/agents/v3/collective-intelligence-coordinator.md +993 -993
- package/.claude/agents/v3/ddd-domain-expert.md +220 -220
- package/.claude/agents/v3/injection-analyst.md +236 -236
- package/.claude/agents/v3/performance-engineer.md +1233 -1233
- package/.claude/agents/v3/pii-detector.md +151 -151
- package/.claude/agents/v3/reasoningbank-learner.md +213 -213
- package/.claude/agents/v3/security-architect-aidefence.md +410 -410
- package/.claude/agents/v3/security-architect.md +867 -867
- package/.claude/agents/v3/swarm-memory-manager.md +157 -157
- package/.claude/agents/v3/v3-integration-architect.md +205 -205
- package/.claude/commands/agents/README.md +50 -50
- package/.claude/commands/agents/agent-capabilities.md +140 -140
- package/.claude/commands/agents/agent-coordination.md +28 -28
- package/.claude/commands/agents/agent-spawning.md +28 -28
- package/.claude/commands/agents/agent-types.md +216 -216
- package/.claude/commands/agents/health.md +139 -139
- package/.claude/commands/agents/list.md +100 -100
- package/.claude/commands/agents/logs.md +130 -130
- package/.claude/commands/agents/metrics.md +122 -122
- package/.claude/commands/agents/pool.md +127 -127
- package/.claude/commands/agents/spawn.md +140 -140
- package/.claude/commands/agents/status.md +115 -115
- package/.claude/commands/agents/stop.md +102 -102
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
- package/.claude/commands/analysis/README.md +9 -9
- package/.claude/commands/analysis/bottleneck-detect.md +162 -162
- package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
- package/.claude/commands/analysis/performance-report.md +25 -25
- package/.claude/commands/analysis/token-efficiency.md +44 -44
- package/.claude/commands/analysis/token-usage.md +25 -25
- package/.claude/commands/automation/README.md +9 -9
- package/.claude/commands/automation/auto-agent.md +122 -122
- package/.claude/commands/automation/self-healing.md +105 -105
- package/.claude/commands/automation/session-memory.md +89 -89
- package/.claude/commands/automation/smart-agents.md +72 -72
- package/.claude/commands/automation/smart-spawn.md +25 -25
- package/.claude/commands/automation/workflow-select.md +25 -25
- package/.claude/commands/claude-flow-help.md +103 -103
- package/.claude/commands/claude-flow-memory.md +107 -107
- package/.claude/commands/claude-flow-swarm.md +205 -205
- package/.claude/commands/coordination/README.md +9 -9
- package/.claude/commands/coordination/agent-spawn.md +25 -25
- package/.claude/commands/coordination/init.md +44 -44
- package/.claude/commands/coordination/orchestrate.md +43 -43
- package/.claude/commands/coordination/spawn.md +45 -45
- package/.claude/commands/coordination/swarm-init.md +85 -85
- package/.claude/commands/coordination/task-orchestrate.md +25 -25
- package/.claude/commands/github/README.md +11 -11
- package/.claude/commands/github/code-review-swarm.md +513 -513
- package/.claude/commands/github/code-review.md +25 -25
- package/.claude/commands/github/github-modes.md +146 -146
- package/.claude/commands/github/github-swarm.md +121 -121
- package/.claude/commands/github/issue-tracker.md +291 -291
- package/.claude/commands/github/issue-triage.md +25 -25
- package/.claude/commands/github/multi-repo-swarm.md +518 -518
- package/.claude/commands/github/pr-enhance.md +26 -26
- package/.claude/commands/github/pr-manager.md +169 -169
- package/.claude/commands/github/project-board-sync.md +470 -470
- package/.claude/commands/github/release-manager.md +339 -339
- package/.claude/commands/github/release-swarm.md +543 -543
- package/.claude/commands/github/repo-analyze.md +25 -25
- package/.claude/commands/github/repo-architect.md +366 -366
- package/.claude/commands/github/swarm-issue.md +484 -484
- package/.claude/commands/github/swarm-pr.md +287 -287
- package/.claude/commands/github/sync-coordinator.md +302 -302
- package/.claude/commands/github/workflow-automation.md +441 -441
- package/.claude/commands/hive-mind/README.md +17 -17
- package/.claude/commands/hive-mind/hive-mind-consensus.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-init.md +18 -18
- package/.claude/commands/hive-mind/hive-mind-memory.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-metrics.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-resume.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-sessions.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-spawn.md +21 -21
- package/.claude/commands/hive-mind/hive-mind-status.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-stop.md +8 -8
- package/.claude/commands/hive-mind/hive-mind-wizard.md +8 -8
- package/.claude/commands/hive-mind/hive-mind.md +27 -27
- package/.claude/commands/hooks/README.md +11 -11
- package/.claude/commands/hooks/overview.md +57 -57
- package/.claude/commands/hooks/post-edit.md +117 -117
- package/.claude/commands/hooks/post-task.md +112 -112
- package/.claude/commands/hooks/pre-edit.md +113 -113
- package/.claude/commands/hooks/pre-task.md +111 -111
- package/.claude/commands/hooks/session-end.md +118 -118
- package/.claude/commands/hooks/setup.md +102 -102
- package/.claude/commands/memory/README.md +9 -9
- package/.claude/commands/memory/memory-persist.md +25 -25
- package/.claude/commands/memory/memory-search.md +25 -25
- package/.claude/commands/memory/memory-usage.md +25 -25
- package/.claude/commands/memory/neural.md +47 -47
- package/.claude/commands/monitoring/README.md +9 -9
- package/.claude/commands/monitoring/agent-metrics.md +25 -25
- package/.claude/commands/monitoring/agents.md +44 -44
- package/.claude/commands/monitoring/real-time-view.md +25 -25
- package/.claude/commands/monitoring/status.md +46 -46
- package/.claude/commands/monitoring/swarm-monitor.md +25 -25
- package/.claude/commands/optimization/README.md +9 -9
- package/.claude/commands/optimization/auto-topology.md +61 -61
- package/.claude/commands/optimization/cache-manage.md +25 -25
- package/.claude/commands/optimization/parallel-execute.md +25 -25
- package/.claude/commands/optimization/parallel-execution.md +49 -49
- package/.claude/commands/optimization/topology-optimize.md +25 -25
- package/.claude/commands/pair/README.md +260 -260
- package/.claude/commands/pair/commands.md +545 -545
- package/.claude/commands/pair/config.md +509 -509
- package/.claude/commands/pair/examples.md +511 -511
- package/.claude/commands/pair/modes.md +347 -347
- package/.claude/commands/pair/session.md +406 -406
- package/.claude/commands/pair/start.md +208 -208
- package/.claude/commands/sparc/analyzer.md +51 -51
- package/.claude/commands/sparc/architect.md +53 -53
- package/.claude/commands/sparc/ask.md +97 -97
- package/.claude/commands/sparc/batch-executor.md +54 -54
- package/.claude/commands/sparc/code.md +89 -89
- package/.claude/commands/sparc/coder.md +54 -54
- package/.claude/commands/sparc/debug.md +83 -83
- package/.claude/commands/sparc/debugger.md +54 -54
- package/.claude/commands/sparc/designer.md +53 -53
- package/.claude/commands/sparc/devops.md +109 -109
- package/.claude/commands/sparc/docs-writer.md +80 -80
- package/.claude/commands/sparc/documenter.md +54 -54
- package/.claude/commands/sparc/innovator.md +54 -54
- package/.claude/commands/sparc/integration.md +83 -83
- package/.claude/commands/sparc/mcp.md +117 -117
- package/.claude/commands/sparc/memory-manager.md +54 -54
- package/.claude/commands/sparc/optimizer.md +54 -54
- package/.claude/commands/sparc/orchestrator.md +131 -131
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
- package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
- package/.claude/commands/sparc/researcher.md +54 -54
- package/.claude/commands/sparc/reviewer.md +54 -54
- package/.claude/commands/sparc/security-review.md +80 -80
- package/.claude/commands/sparc/sparc-modes.md +174 -174
- package/.claude/commands/sparc/sparc.md +111 -111
- package/.claude/commands/sparc/spec-pseudocode.md +80 -80
- package/.claude/commands/sparc/supabase-admin.md +348 -348
- package/.claude/commands/sparc/swarm-coordinator.md +54 -54
- package/.claude/commands/sparc/tdd.md +54 -54
- package/.claude/commands/sparc/tester.md +54 -54
- package/.claude/commands/sparc/tutorial.md +79 -79
- package/.claude/commands/sparc/workflow-manager.md +54 -54
- package/.claude/commands/sparc.md +166 -166
- package/.claude/commands/stream-chain/pipeline.md +120 -120
- package/.claude/commands/stream-chain/run.md +69 -69
- package/.claude/commands/swarm/README.md +15 -15
- package/.claude/commands/swarm/analysis.md +95 -95
- package/.claude/commands/swarm/development.md +96 -96
- package/.claude/commands/swarm/examples.md +168 -168
- package/.claude/commands/swarm/maintenance.md +102 -102
- package/.claude/commands/swarm/optimization.md +117 -117
- package/.claude/commands/swarm/research.md +136 -136
- package/.claude/commands/swarm/swarm-analysis.md +8 -8
- package/.claude/commands/swarm/swarm-background.md +8 -8
- package/.claude/commands/swarm/swarm-init.md +19 -19
- package/.claude/commands/swarm/swarm-modes.md +8 -8
- package/.claude/commands/swarm/swarm-monitor.md +8 -8
- package/.claude/commands/swarm/swarm-spawn.md +19 -19
- package/.claude/commands/swarm/swarm-status.md +8 -8
- package/.claude/commands/swarm/swarm-strategies.md +8 -8
- package/.claude/commands/swarm/swarm.md +87 -87
- package/.claude/commands/swarm/testing.md +131 -131
- package/.claude/commands/training/README.md +9 -9
- package/.claude/commands/training/model-update.md +25 -25
- package/.claude/commands/training/neural-patterns.md +107 -107
- package/.claude/commands/training/neural-train.md +75 -75
- package/.claude/commands/training/pattern-learn.md +25 -25
- package/.claude/commands/training/specialization.md +62 -62
- package/.claude/commands/truth/start.md +142 -142
- package/.claude/commands/verify/check.md +49 -49
- package/.claude/commands/verify/start.md +127 -127
- package/.claude/commands/workflows/README.md +9 -9
- package/.claude/commands/workflows/development.md +77 -77
- package/.claude/commands/workflows/research.md +62 -62
- package/.claude/commands/workflows/workflow-create.md +25 -25
- package/.claude/commands/workflows/workflow-execute.md +25 -25
- package/.claude/commands/workflows/workflow-export.md +25 -25
- package/.claude/eval/human-relevance-frozen-v1.json +17 -17
- package/.claude/evolve-proof/generation-0.json +211 -211
- package/.claude/evolve-proof/real-generation-0.json +406 -406
- package/.claude/evolve-proof/real-generation-1.json +406 -406
- package/.claude/helpers/.helpers-version +1 -1
- package/.claude/helpers/README.md +96 -96
- package/.claude/helpers/adr-compliance.sh +186 -186
- package/.claude/helpers/auto-commit.sh +178 -178
- package/.claude/helpers/auto-memory-hook.mjs +430 -430
- package/.claude/helpers/checkpoint-manager.sh +251 -251
- package/.claude/helpers/daemon-manager.sh +252 -252
- package/.claude/helpers/ddd-tracker.sh +144 -144
- package/.claude/helpers/github-safe.js +156 -156
- package/.claude/helpers/github-setup.sh +45 -45
- package/.claude/helpers/guidance-hook.sh +13 -13
- package/.claude/helpers/guidance-hooks.sh +102 -102
- package/.claude/helpers/health-monitor.sh +108 -108
- package/.claude/helpers/helpers.manifest.json +6 -6
- package/.claude/helpers/hook-handler.cjs +606 -606
- package/.claude/helpers/intelligence.cjs +1169 -1169
- package/.claude/helpers/learning-hooks.sh +329 -329
- package/.claude/helpers/learning-optimizer.sh +127 -127
- package/.claude/helpers/learning-service.mjs +1144 -1144
- package/.claude/helpers/memory.js +83 -83
- package/.claude/helpers/metrics-db.mjs +503 -503
- package/.claude/helpers/pattern-consolidator.sh +86 -86
- package/.claude/helpers/perf-worker.sh +160 -160
- package/.claude/helpers/post-commit +16 -16
- package/.claude/helpers/pre-commit +26 -26
- package/.claude/helpers/quick-start.sh +19 -19
- package/.claude/helpers/router.js +105 -105
- package/.claude/helpers/security-scanner.sh +127 -127
- package/.claude/helpers/session.js +157 -157
- package/.claude/helpers/setup-mcp.sh +18 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +189 -189
- package/.claude/helpers/statusline-hook.sh +21 -21
- package/.claude/helpers/statusline.cjs +1290 -1290
- package/.claude/helpers/statusline.js +340 -340
- package/.claude/helpers/swarm-comms.sh +353 -353
- package/.claude/helpers/swarm-hooks.sh +761 -761
- package/.claude/helpers/swarm-monitor.sh +210 -210
- package/.claude/helpers/sync-v3-metrics.sh +245 -245
- package/.claude/helpers/update-v3-progress.sh +165 -165
- package/.claude/helpers/v3-quick-status.sh +57 -57
- package/.claude/helpers/v3.sh +110 -110
- package/.claude/helpers/validate-v3-config.sh +215 -215
- package/.claude/helpers/worker-manager.sh +170 -170
- package/.claude/proven-config.json +41 -41
- package/.claude/proven-config.manifest.json +37 -37
- package/.claude/proven-config.signed.json +41 -41
- package/.claude/settings.json +182 -182
- package/.claude/skills/agentdb-advanced/SKILL.md +550 -550
- package/.claude/skills/agentdb-learning/SKILL.md +545 -545
- package/.claude/skills/agentdb-memory-patterns/SKILL.md +339 -339
- package/.claude/skills/agentdb-optimization/SKILL.md +509 -509
- package/.claude/skills/agentdb-vector-search/SKILL.md +339 -339
- package/.claude/skills/browser/SKILL.md +204 -204
- package/.claude/skills/dual-mode/README.md +71 -71
- package/.claude/skills/dual-mode/dual-collect.md +103 -103
- package/.claude/skills/dual-mode/dual-coordinate.md +85 -85
- package/.claude/skills/dual-mode/dual-spawn.md +81 -81
- package/.claude/skills/flow-nexus-neural/SKILL.md +727 -727
- package/.claude/skills/flow-nexus-platform/SKILL.md +1154 -1154
- package/.claude/skills/flow-nexus-swarm/SKILL.md +604 -604
- package/.claude/skills/github-code-review/SKILL.md +1125 -1125
- package/.claude/skills/github-multi-repo/SKILL.md +862 -862
- package/.claude/skills/github-project-management/SKILL.md +1262 -1262
- package/.claude/skills/github-release-management/SKILL.md +1064 -1064
- package/.claude/skills/github-workflow-automation/SKILL.md +1047 -1047
- package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
- package/.claude/skills/pair-programming/SKILL.md +1202 -1202
- package/.claude/skills/reasoningbank-agentdb/SKILL.md +446 -446
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +201 -201
- package/.claude/skills/skill-builder/SKILL.md +910 -910
- package/.claude/skills/sparc-methodology/SKILL.md +1106 -1106
- package/.claude/skills/stream-chain/SKILL.md +560 -560
- package/.claude/skills/swarm-advanced/SKILL.md +970 -970
- package/.claude/skills/swarm-orchestration/SKILL.md +179 -179
- package/.claude/skills/v3-cli-modernization/SKILL.md +871 -871
- package/.claude/skills/v3-core-implementation/SKILL.md +796 -796
- package/.claude/skills/v3-ddd-architecture/SKILL.md +441 -441
- package/.claude/skills/v3-integration-deep/SKILL.md +240 -240
- package/.claude/skills/v3-mcp-optimization/SKILL.md +776 -776
- package/.claude/skills/v3-memory-unification/SKILL.md +173 -173
- package/.claude/skills/v3-performance-optimization/SKILL.md +389 -389
- package/.claude/skills/v3-security-overhaul/SKILL.md +81 -81
- package/.claude/skills/v3-swarm-coordination/SKILL.md +339 -339
- package/.claude/skills/verification-quality/SKILL.md +691 -691
- package/README.md +422 -422
- package/bin/cli.js +338 -338
- package/bin/mcp-server.js +224 -224
- package/bin/preinstall.cjs +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/benchmarks/gaia-critic.js +24 -24
- package/dist/src/business-pods/bbs-budget-tracker.js +53 -53
- package/dist/src/commands/completions.js +409 -409
- package/dist/src/commands/daemon.js +44 -44
- package/dist/src/commands/doctor.js +4 -4
- package/dist/src/commands/embeddings.js +26 -26
- package/dist/src/commands/hive-mind.js +97 -97
- package/dist/src/commands/hooks.js +9 -9
- package/dist/src/commands/init.js +75 -75
- package/dist/src/commands/ruvector/backup.js +23 -23
- package/dist/src/commands/ruvector/benchmark.js +31 -31
- package/dist/src/commands/ruvector/import.js +14 -14
- package/dist/src/commands/ruvector/init.js +115 -115
- package/dist/src/commands/ruvector/migrate.js +99 -99
- package/dist/src/commands/ruvector/optimize.js +51 -51
- package/dist/src/commands/ruvector/setup.js +624 -624
- package/dist/src/commands/ruvector/status.js +38 -38
- package/dist/src/config/proven-config.js +2 -2
- package/dist/src/init/claudemd-generator.js +273 -273
- package/dist/src/init/executor.js +453 -453
- package/dist/src/init/helper-signing.js +2 -2
- package/dist/src/init/helpers-generator.js +917 -751
- package/dist/src/init/statusline-generator.js +24 -24
- package/dist/src/mcp-tools/agentdb-tools.js +15 -15
- package/dist/src/mcp-tools/browser-intent-tools.js +19 -19
- package/dist/src/mcp-tools/seraphina-tools.js +4 -4
- package/dist/src/memory/graph-edge-writer.js +22 -22
- package/dist/src/memory/memory-bridge.js +114 -114
- package/dist/src/memory/memory-initializer.js +416 -416
- package/dist/src/memory/rabitq-index.js +5 -5
- package/dist/src/proxy/verify.js +2 -2
- package/dist/src/runtime/headless.js +28 -28
- package/dist/src/ruvector/diskann-backend.d.ts +78 -0
- package/dist/src/ruvector/diskann-backend.js +310 -0
- package/dist/src/services/distill-tuning.js +7 -7
- package/dist/src/services/headless-worker-executor.js +84 -84
- package/dist/src/services/memory-distillation.js +18 -18
- package/dist/src/transfer/deploy-seraphine.js +23 -23
- package/node_modules/@claude-flow/codex/.agents/skills/github-automation/SKILL.md +32 -32
- package/node_modules/@claude-flow/codex/.agents/skills/memory-management/SKILL.md +45 -45
- package/node_modules/@claude-flow/codex/.agents/skills/performance-analysis/SKILL.md +32 -32
- package/node_modules/@claude-flow/codex/.agents/skills/security-audit/SKILL.md +46 -46
- package/node_modules/@claude-flow/codex/.agents/skills/sparc-methodology/SKILL.md +46 -46
- package/node_modules/@claude-flow/codex/.agents/skills/swarm-orchestration/SKILL.md +53 -53
- package/node_modules/@claude-flow/codex/README.md +1044 -1044
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +13 -13
- package/node_modules/@claude-flow/codex/dist/generators/agents-md.js +664 -664
- package/node_modules/@claude-flow/codex/dist/generators/config-toml.js +455 -455
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.js +45 -45
- package/node_modules/@claude-flow/codex/dist/initializer.js +167 -167
- package/node_modules/@claude-flow/codex/dist/templates/index.js +15 -15
- package/node_modules/@claude-flow/mcp/README.md +429 -429
- package/node_modules/@claude-flow/plugin-agent-federation/README.md +49 -49
- package/node_modules/@claude-flow/security/README.md +292 -292
- package/node_modules/@claude-flow/security/dist/credential-generator.js +9 -9
- package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
- package/node_modules/@claude-flow/security/dist/oauth/callback-server.js +9 -9
- package/package.json +181 -181
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +32 -32
- package/plugins/ruflo-metaharness/README.md +72 -72
- package/plugins/ruflo-metaharness/agents/metaharness-architect.md +58 -58
- package/plugins/ruflo-metaharness/commands/ruflo-metaharness.md +50 -50
- package/plugins/ruflo-metaharness/scripts/_darwin.mjs +210 -210
- package/plugins/ruflo-metaharness/scripts/_harness.mjs +334 -334
- package/plugins/ruflo-metaharness/scripts/_invoke.mjs +230 -230
- package/plugins/ruflo-metaharness/scripts/_redblue.mjs +143 -143
- package/plugins/ruflo-metaharness/scripts/_similarity.mjs +161 -161
- package/plugins/ruflo-metaharness/scripts/_spike-similarity.mjs +223 -223
- package/plugins/ruflo-metaharness/scripts/audit-list.mjs +158 -158
- package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +272 -272
- package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +146 -146
- package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +186 -186
- package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +177 -177
- package/plugins/ruflo-metaharness/scripts/bench.mjs +95 -95
- package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +363 -363
- package/plugins/ruflo-metaharness/scripts/evolve.mjs +404 -404
- package/plugins/ruflo-metaharness/scripts/genome.mjs +105 -105
- package/plugins/ruflo-metaharness/scripts/gepa.mjs +153 -153
- package/plugins/ruflo-metaharness/scripts/learn.mjs +127 -127
- package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +110 -110
- package/plugins/ruflo-metaharness/scripts/mint.mjs +126 -126
- package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +228 -228
- package/plugins/ruflo-metaharness/scripts/redblue.mjs +286 -286
- package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +250 -250
- package/plugins/ruflo-metaharness/scripts/score.mjs +92 -92
- package/plugins/ruflo-metaharness/scripts/security-bench.mjs +174 -174
- package/plugins/ruflo-metaharness/scripts/similarity.mjs +158 -158
- package/plugins/ruflo-metaharness/scripts/smoke.sh +2422 -2422
- package/plugins/ruflo-metaharness/scripts/test-graceful-degradation.mjs +165 -165
- package/plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs +498 -498
- package/plugins/ruflo-metaharness/scripts/test-parallel-pipeline.mjs +204 -204
- package/plugins/ruflo-metaharness/scripts/test-pipeline-roundtrip.mjs +586 -586
- package/plugins/ruflo-metaharness/scripts/test-similarity.mjs +372 -372
- package/plugins/ruflo-metaharness/scripts/test-with-openrouter.mjs +229 -229
- package/plugins/ruflo-metaharness/scripts/threat-model.mjs +62 -62
- package/plugins/ruflo-metaharness/skills/harness-bench/SKILL.md +64 -64
- package/plugins/ruflo-metaharness/skills/harness-drift-from-history/SKILL.md +65 -65
- package/plugins/ruflo-metaharness/skills/harness-evolve/SKILL.md +131 -131
- package/plugins/ruflo-metaharness/skills/harness-genome/SKILL.md +57 -57
- package/plugins/ruflo-metaharness/skills/harness-gepa/SKILL.md +65 -65
- package/plugins/ruflo-metaharness/skills/harness-learn/SKILL.md +65 -65
- package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +49 -49
- package/plugins/ruflo-metaharness/skills/harness-mint/SKILL.md +72 -72
- package/plugins/ruflo-metaharness/skills/harness-oia-audit/SKILL.md +79 -79
- package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +66 -66
- package/plugins/ruflo-metaharness/skills/harness-security-bench/SKILL.md +101 -101
- package/plugins/ruflo-metaharness/skills/harness-similarity/SKILL.md +67 -67
- package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +41 -41
- package/scripts/postinstall.cjs +153 -153
|
@@ -65,371 +65,371 @@ const TOOL_FAILURE_EXPR = '(function(hi){' +
|
|
|
65
65
|
* Generate pre-commit hook script
|
|
66
66
|
*/
|
|
67
67
|
export function generatePreCommitHook() {
|
|
68
|
-
return `#!/bin/bash
|
|
69
|
-
# Ruflo Pre-Commit Hook
|
|
70
|
-
# Validates code quality before commit
|
|
71
|
-
|
|
72
|
-
set -e
|
|
73
|
-
|
|
74
|
-
echo "🔍 Running Ruflo pre-commit checks..."
|
|
75
|
-
|
|
76
|
-
# Get staged files
|
|
77
|
-
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
78
|
-
|
|
79
|
-
# Run validation for each staged file
|
|
80
|
-
for FILE in $STAGED_FILES; do
|
|
81
|
-
if [[ "$FILE" =~ \\.(ts|js|tsx|jsx)$ ]]; then
|
|
82
|
-
echo " Validating: $FILE"
|
|
83
|
-
npx @claude-flow/cli hooks pre-edit --file "$FILE" --validate-syntax 2>/dev/null || true
|
|
84
|
-
fi
|
|
85
|
-
done
|
|
86
|
-
|
|
87
|
-
# Run tests if available
|
|
88
|
-
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
89
|
-
echo "🧪 Running tests..."
|
|
90
|
-
npm test --if-present 2>/dev/null || echo " Tests skipped or failed"
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
echo "✅ Pre-commit checks complete"
|
|
68
|
+
return `#!/bin/bash
|
|
69
|
+
# Ruflo Pre-Commit Hook
|
|
70
|
+
# Validates code quality before commit
|
|
71
|
+
|
|
72
|
+
set -e
|
|
73
|
+
|
|
74
|
+
echo "🔍 Running Ruflo pre-commit checks..."
|
|
75
|
+
|
|
76
|
+
# Get staged files
|
|
77
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
78
|
+
|
|
79
|
+
# Run validation for each staged file
|
|
80
|
+
for FILE in $STAGED_FILES; do
|
|
81
|
+
if [[ "$FILE" =~ \\.(ts|js|tsx|jsx)$ ]]; then
|
|
82
|
+
echo " Validating: $FILE"
|
|
83
|
+
npx @claude-flow/cli hooks pre-edit --file "$FILE" --validate-syntax 2>/dev/null || true
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
|
|
87
|
+
# Run tests if available
|
|
88
|
+
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
89
|
+
echo "🧪 Running tests..."
|
|
90
|
+
npm test --if-present 2>/dev/null || echo " Tests skipped or failed"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "✅ Pre-commit checks complete"
|
|
94
94
|
`;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
97
|
* Generate post-commit hook script
|
|
98
98
|
*/
|
|
99
99
|
export function generatePostCommitHook() {
|
|
100
|
-
return `#!/bin/bash
|
|
101
|
-
# Ruflo Post-Commit Hook
|
|
102
|
-
# Records commit metrics and trains patterns
|
|
103
|
-
|
|
104
|
-
COMMIT_HASH=$(git rev-parse HEAD)
|
|
105
|
-
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
106
|
-
|
|
107
|
-
echo "📊 Recording commit metrics..."
|
|
108
|
-
|
|
109
|
-
# Notify ruflo of commit
|
|
110
|
-
npx ruflo@latest hooks notify \\
|
|
111
|
-
--message "Commit: $COMMIT_MSG" \\
|
|
112
|
-
--level info \\
|
|
113
|
-
--metadata '{"hash": "'$COMMIT_HASH'"}' 2>/dev/null || true
|
|
114
|
-
|
|
115
|
-
echo "✅ Commit recorded"
|
|
100
|
+
return `#!/bin/bash
|
|
101
|
+
# Ruflo Post-Commit Hook
|
|
102
|
+
# Records commit metrics and trains patterns
|
|
103
|
+
|
|
104
|
+
COMMIT_HASH=$(git rev-parse HEAD)
|
|
105
|
+
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
106
|
+
|
|
107
|
+
echo "📊 Recording commit metrics..."
|
|
108
|
+
|
|
109
|
+
# Notify ruflo of commit
|
|
110
|
+
npx ruflo@latest hooks notify \\
|
|
111
|
+
--message "Commit: $COMMIT_MSG" \\
|
|
112
|
+
--level info \\
|
|
113
|
+
--metadata '{"hash": "'$COMMIT_HASH'"}' 2>/dev/null || true
|
|
114
|
+
|
|
115
|
+
echo "✅ Commit recorded"
|
|
116
116
|
`;
|
|
117
117
|
}
|
|
118
118
|
/**
|
|
119
119
|
* Generate session manager script
|
|
120
120
|
*/
|
|
121
121
|
export function generateSessionManager() {
|
|
122
|
-
return `#!/usr/bin/env node
|
|
123
|
-
/**
|
|
124
|
-
* Ruflo Session Manager
|
|
125
|
-
* Handles session lifecycle: start, restore, end
|
|
126
|
-
*/
|
|
127
|
-
|
|
128
|
-
const fs = require('fs');
|
|
129
|
-
const path = require('path');
|
|
130
|
-
|
|
131
|
-
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
132
|
-
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
133
|
-
|
|
134
|
-
const commands = {
|
|
135
|
-
start: () => {
|
|
136
|
-
const sessionId = \`session-\${Date.now()}\`;
|
|
137
|
-
const session = {
|
|
138
|
-
id: sessionId,
|
|
139
|
-
startedAt: new Date().toISOString(),
|
|
140
|
-
cwd: process.cwd(),
|
|
141
|
-
context: {},
|
|
142
|
-
metrics: {
|
|
143
|
-
edits: 0,
|
|
144
|
-
commands: 0,
|
|
145
|
-
tasks: 0,
|
|
146
|
-
errors: 0,
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
151
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
152
|
-
|
|
153
|
-
console.log(\`Session started: \${sessionId}\`);
|
|
154
|
-
return session;
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
restore: () => {
|
|
158
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
159
|
-
console.log('No session to restore');
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
164
|
-
session.restoredAt = new Date().toISOString();
|
|
165
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
166
|
-
|
|
167
|
-
console.log(\`Session restored: \${session.id}\`);
|
|
168
|
-
return session;
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
end: () => {
|
|
172
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
173
|
-
console.log('No active session');
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
178
|
-
session.endedAt = new Date().toISOString();
|
|
179
|
-
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
180
|
-
|
|
181
|
-
// Archive session
|
|
182
|
-
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
183
|
-
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
184
|
-
fs.unlinkSync(SESSION_FILE);
|
|
185
|
-
|
|
186
|
-
console.log(\`Session ended: \${session.id}\`);
|
|
187
|
-
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
188
|
-
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
189
|
-
|
|
190
|
-
return session;
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
status: () => {
|
|
194
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
195
|
-
console.log('No active session');
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
200
|
-
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
201
|
-
|
|
202
|
-
console.log(\`Session: \${session.id}\`);
|
|
203
|
-
console.log(\`Started: \${session.startedAt}\`);
|
|
204
|
-
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
205
|
-
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
206
|
-
|
|
207
|
-
return session;
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
update: (key, value) => {
|
|
211
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
212
|
-
console.log('No active session');
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
217
|
-
session.context[key] = value;
|
|
218
|
-
session.updatedAt = new Date().toISOString();
|
|
219
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
220
|
-
|
|
221
|
-
return session;
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
metric: (name) => {
|
|
225
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
230
|
-
if (session.metrics[name] !== undefined) {
|
|
231
|
-
session.metrics[name]++;
|
|
232
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return session;
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// CLI
|
|
240
|
-
const [,, command, ...args] = process.argv;
|
|
241
|
-
|
|
242
|
-
if (command && commands[command]) {
|
|
243
|
-
commands[command](...args);
|
|
244
|
-
} else {
|
|
245
|
-
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
module.exports = commands;
|
|
122
|
+
return `#!/usr/bin/env node
|
|
123
|
+
/**
|
|
124
|
+
* Ruflo Session Manager
|
|
125
|
+
* Handles session lifecycle: start, restore, end
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
const fs = require('fs');
|
|
129
|
+
const path = require('path');
|
|
130
|
+
|
|
131
|
+
const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
132
|
+
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
133
|
+
|
|
134
|
+
const commands = {
|
|
135
|
+
start: () => {
|
|
136
|
+
const sessionId = \`session-\${Date.now()}\`;
|
|
137
|
+
const session = {
|
|
138
|
+
id: sessionId,
|
|
139
|
+
startedAt: new Date().toISOString(),
|
|
140
|
+
cwd: process.cwd(),
|
|
141
|
+
context: {},
|
|
142
|
+
metrics: {
|
|
143
|
+
edits: 0,
|
|
144
|
+
commands: 0,
|
|
145
|
+
tasks: 0,
|
|
146
|
+
errors: 0,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
151
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
152
|
+
|
|
153
|
+
console.log(\`Session started: \${sessionId}\`);
|
|
154
|
+
return session;
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
restore: () => {
|
|
158
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
159
|
+
console.log('No session to restore');
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
164
|
+
session.restoredAt = new Date().toISOString();
|
|
165
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
166
|
+
|
|
167
|
+
console.log(\`Session restored: \${session.id}\`);
|
|
168
|
+
return session;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
end: () => {
|
|
172
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
173
|
+
console.log('No active session');
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
178
|
+
session.endedAt = new Date().toISOString();
|
|
179
|
+
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
180
|
+
|
|
181
|
+
// Archive session
|
|
182
|
+
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
183
|
+
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
184
|
+
fs.unlinkSync(SESSION_FILE);
|
|
185
|
+
|
|
186
|
+
console.log(\`Session ended: \${session.id}\`);
|
|
187
|
+
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
188
|
+
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
189
|
+
|
|
190
|
+
return session;
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
status: () => {
|
|
194
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
195
|
+
console.log('No active session');
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
200
|
+
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
201
|
+
|
|
202
|
+
console.log(\`Session: \${session.id}\`);
|
|
203
|
+
console.log(\`Started: \${session.startedAt}\`);
|
|
204
|
+
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
205
|
+
console.log(\`Metrics: \${JSON.stringify(session.metrics)}\`);
|
|
206
|
+
|
|
207
|
+
return session;
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
update: (key, value) => {
|
|
211
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
212
|
+
console.log('No active session');
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
217
|
+
session.context[key] = value;
|
|
218
|
+
session.updatedAt = new Date().toISOString();
|
|
219
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
220
|
+
|
|
221
|
+
return session;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
metric: (name) => {
|
|
225
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
230
|
+
if (session.metrics[name] !== undefined) {
|
|
231
|
+
session.metrics[name]++;
|
|
232
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return session;
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// CLI
|
|
240
|
+
const [,, command, ...args] = process.argv;
|
|
241
|
+
|
|
242
|
+
if (command && commands[command]) {
|
|
243
|
+
commands[command](...args);
|
|
244
|
+
} else {
|
|
245
|
+
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = commands;
|
|
249
249
|
`;
|
|
250
250
|
}
|
|
251
251
|
/**
|
|
252
252
|
* Generate agent router script
|
|
253
253
|
*/
|
|
254
254
|
export function generateAgentRouter() {
|
|
255
|
-
return `#!/usr/bin/env node
|
|
256
|
-
/**
|
|
257
|
-
* Ruflo Agent Router
|
|
258
|
-
*
|
|
259
|
-
* Static keyword router that suggests an agent for a task description.
|
|
260
|
-
* NOTE: This is *not* a learned model. It is a heuristic table; "confidence"
|
|
261
|
-
* is reported as a heuristic prior, not a calibrated probability.
|
|
262
|
-
*
|
|
263
|
-
* #2257 fix: patterns are now word-boundary-anchored so short tokens like
|
|
264
|
-
* \`cd\`, \`ci\`, \`ui\`, \`add\`, \`structure\` no longer match inside unrelated
|
|
265
|
-
* words (\`decision\`, \`infrastructure\`, \`address\`, \`addendum\`). Default
|
|
266
|
-
* matched-confidence dropped from 0.8 to 0.6, and fall-through from 0.5 to
|
|
267
|
-
* 0.3, to reflect that this is a static heuristic, not a learned classifier.
|
|
268
|
-
*/
|
|
269
|
-
|
|
270
|
-
const AGENT_CAPABILITIES = {
|
|
271
|
-
coder: ['code-generation', 'refactoring', 'debugging', 'implementation'],
|
|
272
|
-
tester: ['unit-testing', 'integration-testing', 'coverage', 'test-generation'],
|
|
273
|
-
reviewer: ['code-review', 'security-audit', 'quality-check', 'best-practices'],
|
|
274
|
-
researcher: ['web-search', 'documentation', 'analysis', 'summarization'],
|
|
275
|
-
architect: ['system-design', 'architecture', 'patterns', 'scalability'],
|
|
276
|
-
'backend-dev': ['api', 'database', 'server', 'authentication'],
|
|
277
|
-
'frontend-dev': ['ui', 'react', 'css', 'components'],
|
|
278
|
-
devops: ['ci-cd', 'docker', 'deployment', 'infrastructure'],
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
// Each entry has a token list. Single tokens get \\b…\\b boundaries so 'cd'
|
|
282
|
-
// won't match inside 'decide'. Phrases (whitespace or '/') match literally —
|
|
283
|
-
// the whitespace acts as a natural boundary.
|
|
284
|
-
const TASK_PATTERNS = [
|
|
285
|
-
{ tokens: ['implement', 'create', 'build', 'add', 'write code', 'refactor', 'debug'], agent: 'coder' },
|
|
286
|
-
{ tokens: ['test', 'tests', 'spec', 'coverage', 'unit test', 'integration test'], agent: 'tester' },
|
|
287
|
-
{ tokens: ['review', 'audit', 'check', 'validate', 'security'], agent: 'reviewer' },
|
|
288
|
-
{ tokens: ['research', 'find', 'search', 'documentation', 'explore'], agent: 'researcher' },
|
|
289
|
-
{ tokens: ['design', 'architect', 'architecture', 'structure', 'plan'], agent: 'architect' },
|
|
290
|
-
{ tokens: ['api', 'endpoint', 'server', 'backend', 'database'], agent: 'backend-dev' },
|
|
291
|
-
{ tokens: ['ui', 'frontend', 'component', 'react', 'css', 'style'], agent: 'frontend-dev' },
|
|
292
|
-
{ tokens: ['deploy', 'docker', 'ci', 'cd', 'ci/cd', 'pipeline', 'infrastructure', 'devops'], agent: 'devops' },
|
|
293
|
-
];
|
|
294
|
-
|
|
295
|
-
function escapeRegex(s) {
|
|
296
|
-
return s.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function buildPattern(tokens) {
|
|
300
|
-
const alternatives = tokens.map((tok) => {
|
|
301
|
-
const escaped = escapeRegex(tok.toLowerCase());
|
|
302
|
-
if (/\\s|\\//.test(tok)) return escaped;
|
|
303
|
-
return \`\\\\b\${escaped}\\\\b\`;
|
|
304
|
-
});
|
|
305
|
-
return new RegExp(\`(?:\${alternatives.join('|')})\`, 'i');
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const COMPILED_PATTERNS = TASK_PATTERNS.map((entry) => ({
|
|
309
|
-
agent: entry.agent,
|
|
310
|
-
tokens: entry.tokens,
|
|
311
|
-
regex: buildPattern(entry.tokens),
|
|
312
|
-
}));
|
|
313
|
-
|
|
314
|
-
function routeTask(task) {
|
|
315
|
-
const taskLower = String(task == null ? '' : task).toLowerCase();
|
|
316
|
-
for (const entry of COMPILED_PATTERNS) {
|
|
317
|
-
if (entry.regex.test(taskLower)) {
|
|
318
|
-
return {
|
|
319
|
-
agent: entry.agent,
|
|
320
|
-
confidence: 0.6,
|
|
321
|
-
reason: \`Matched keyword(s) from: \${entry.tokens.join('|')}\`,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return {
|
|
326
|
-
agent: 'coder',
|
|
327
|
-
confidence: 0.3,
|
|
328
|
-
reason: 'Default routing - no specific keyword matched',
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// CLI
|
|
333
|
-
const task = process.argv.slice(2).join(' ');
|
|
334
|
-
|
|
335
|
-
if (task) {
|
|
336
|
-
const result = routeTask(task);
|
|
337
|
-
console.log(JSON.stringify(result, null, 2));
|
|
338
|
-
} else {
|
|
339
|
-
console.log('Usage: router.js <task description>');
|
|
340
|
-
console.log('\\nAvailable agents:', Object.keys(AGENT_CAPABILITIES).join(', '));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
module.exports = { routeTask, AGENT_CAPABILITIES, TASK_PATTERNS, buildPattern };
|
|
255
|
+
return `#!/usr/bin/env node
|
|
256
|
+
/**
|
|
257
|
+
* Ruflo Agent Router
|
|
258
|
+
*
|
|
259
|
+
* Static keyword router that suggests an agent for a task description.
|
|
260
|
+
* NOTE: This is *not* a learned model. It is a heuristic table; "confidence"
|
|
261
|
+
* is reported as a heuristic prior, not a calibrated probability.
|
|
262
|
+
*
|
|
263
|
+
* #2257 fix: patterns are now word-boundary-anchored so short tokens like
|
|
264
|
+
* \`cd\`, \`ci\`, \`ui\`, \`add\`, \`structure\` no longer match inside unrelated
|
|
265
|
+
* words (\`decision\`, \`infrastructure\`, \`address\`, \`addendum\`). Default
|
|
266
|
+
* matched-confidence dropped from 0.8 to 0.6, and fall-through from 0.5 to
|
|
267
|
+
* 0.3, to reflect that this is a static heuristic, not a learned classifier.
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
const AGENT_CAPABILITIES = {
|
|
271
|
+
coder: ['code-generation', 'refactoring', 'debugging', 'implementation'],
|
|
272
|
+
tester: ['unit-testing', 'integration-testing', 'coverage', 'test-generation'],
|
|
273
|
+
reviewer: ['code-review', 'security-audit', 'quality-check', 'best-practices'],
|
|
274
|
+
researcher: ['web-search', 'documentation', 'analysis', 'summarization'],
|
|
275
|
+
architect: ['system-design', 'architecture', 'patterns', 'scalability'],
|
|
276
|
+
'backend-dev': ['api', 'database', 'server', 'authentication'],
|
|
277
|
+
'frontend-dev': ['ui', 'react', 'css', 'components'],
|
|
278
|
+
devops: ['ci-cd', 'docker', 'deployment', 'infrastructure'],
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Each entry has a token list. Single tokens get \\b…\\b boundaries so 'cd'
|
|
282
|
+
// won't match inside 'decide'. Phrases (whitespace or '/') match literally —
|
|
283
|
+
// the whitespace acts as a natural boundary.
|
|
284
|
+
const TASK_PATTERNS = [
|
|
285
|
+
{ tokens: ['implement', 'create', 'build', 'add', 'write code', 'refactor', 'debug'], agent: 'coder' },
|
|
286
|
+
{ tokens: ['test', 'tests', 'spec', 'coverage', 'unit test', 'integration test'], agent: 'tester' },
|
|
287
|
+
{ tokens: ['review', 'audit', 'check', 'validate', 'security'], agent: 'reviewer' },
|
|
288
|
+
{ tokens: ['research', 'find', 'search', 'documentation', 'explore'], agent: 'researcher' },
|
|
289
|
+
{ tokens: ['design', 'architect', 'architecture', 'structure', 'plan'], agent: 'architect' },
|
|
290
|
+
{ tokens: ['api', 'endpoint', 'server', 'backend', 'database'], agent: 'backend-dev' },
|
|
291
|
+
{ tokens: ['ui', 'frontend', 'component', 'react', 'css', 'style'], agent: 'frontend-dev' },
|
|
292
|
+
{ tokens: ['deploy', 'docker', 'ci', 'cd', 'ci/cd', 'pipeline', 'infrastructure', 'devops'], agent: 'devops' },
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
function escapeRegex(s) {
|
|
296
|
+
return s.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function buildPattern(tokens) {
|
|
300
|
+
const alternatives = tokens.map((tok) => {
|
|
301
|
+
const escaped = escapeRegex(tok.toLowerCase());
|
|
302
|
+
if (/\\s|\\//.test(tok)) return escaped;
|
|
303
|
+
return \`\\\\b\${escaped}\\\\b\`;
|
|
304
|
+
});
|
|
305
|
+
return new RegExp(\`(?:\${alternatives.join('|')})\`, 'i');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const COMPILED_PATTERNS = TASK_PATTERNS.map((entry) => ({
|
|
309
|
+
agent: entry.agent,
|
|
310
|
+
tokens: entry.tokens,
|
|
311
|
+
regex: buildPattern(entry.tokens),
|
|
312
|
+
}));
|
|
313
|
+
|
|
314
|
+
function routeTask(task) {
|
|
315
|
+
const taskLower = String(task == null ? '' : task).toLowerCase();
|
|
316
|
+
for (const entry of COMPILED_PATTERNS) {
|
|
317
|
+
if (entry.regex.test(taskLower)) {
|
|
318
|
+
return {
|
|
319
|
+
agent: entry.agent,
|
|
320
|
+
confidence: 0.6,
|
|
321
|
+
reason: \`Matched keyword(s) from: \${entry.tokens.join('|')}\`,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
agent: 'coder',
|
|
327
|
+
confidence: 0.3,
|
|
328
|
+
reason: 'Default routing - no specific keyword matched',
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// CLI
|
|
333
|
+
const task = process.argv.slice(2).join(' ');
|
|
334
|
+
|
|
335
|
+
if (task) {
|
|
336
|
+
const result = routeTask(task);
|
|
337
|
+
console.log(JSON.stringify(result, null, 2));
|
|
338
|
+
} else {
|
|
339
|
+
console.log('Usage: router.js <task description>');
|
|
340
|
+
console.log('\\nAvailable agents:', Object.keys(AGENT_CAPABILITIES).join(', '));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
module.exports = { routeTask, AGENT_CAPABILITIES, TASK_PATTERNS, buildPattern };
|
|
344
344
|
`;
|
|
345
345
|
}
|
|
346
346
|
/**
|
|
347
347
|
* Generate memory helper script
|
|
348
348
|
*/
|
|
349
349
|
export function generateMemoryHelper() {
|
|
350
|
-
return `#!/usr/bin/env node
|
|
351
|
-
/**
|
|
352
|
-
* Ruflo Memory Helper
|
|
353
|
-
* Simple key-value memory for cross-session context
|
|
354
|
-
*/
|
|
355
|
-
|
|
356
|
-
const fs = require('fs');
|
|
357
|
-
const path = require('path');
|
|
358
|
-
|
|
359
|
-
const MEMORY_DIR = path.join(process.cwd(), '.claude-flow', 'data');
|
|
360
|
-
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
|
361
|
-
|
|
362
|
-
function loadMemory() {
|
|
363
|
-
try {
|
|
364
|
-
if (fs.existsSync(MEMORY_FILE)) {
|
|
365
|
-
return JSON.parse(fs.readFileSync(MEMORY_FILE, 'utf-8'));
|
|
366
|
-
}
|
|
367
|
-
} catch (e) {
|
|
368
|
-
// Ignore
|
|
369
|
-
}
|
|
370
|
-
return {};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function saveMemory(memory) {
|
|
374
|
-
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
375
|
-
fs.writeFileSync(MEMORY_FILE, JSON.stringify(memory, null, 2));
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const commands = {
|
|
379
|
-
get: (key) => {
|
|
380
|
-
const memory = loadMemory();
|
|
381
|
-
const value = key ? memory[key] : memory;
|
|
382
|
-
console.log(JSON.stringify(value, null, 2));
|
|
383
|
-
return value;
|
|
384
|
-
},
|
|
385
|
-
|
|
386
|
-
set: (key, value) => {
|
|
387
|
-
if (!key) {
|
|
388
|
-
console.error('Key required');
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
const memory = loadMemory();
|
|
392
|
-
memory[key] = value;
|
|
393
|
-
memory._updated = new Date().toISOString();
|
|
394
|
-
saveMemory(memory);
|
|
395
|
-
console.log(\`Set: \${key}\`);
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
delete: (key) => {
|
|
399
|
-
if (!key) {
|
|
400
|
-
console.error('Key required');
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
const memory = loadMemory();
|
|
404
|
-
delete memory[key];
|
|
405
|
-
saveMemory(memory);
|
|
406
|
-
console.log(\`Deleted: \${key}\`);
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
clear: () => {
|
|
410
|
-
saveMemory({});
|
|
411
|
-
console.log('Memory cleared');
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
keys: () => {
|
|
415
|
-
const memory = loadMemory();
|
|
416
|
-
const keys = Object.keys(memory).filter(k => !k.startsWith('_'));
|
|
417
|
-
console.log(keys.join('\\n'));
|
|
418
|
-
return keys;
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
// CLI
|
|
423
|
-
const [,, command, key, ...valueParts] = process.argv;
|
|
424
|
-
const value = valueParts.join(' ');
|
|
425
|
-
|
|
426
|
-
if (command && commands[command]) {
|
|
427
|
-
commands[command](key, value);
|
|
428
|
-
} else {
|
|
429
|
-
console.log('Usage: memory.js <get|set|delete|clear|keys> [key] [value]');
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
module.exports = commands;
|
|
350
|
+
return `#!/usr/bin/env node
|
|
351
|
+
/**
|
|
352
|
+
* Ruflo Memory Helper
|
|
353
|
+
* Simple key-value memory for cross-session context
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
const fs = require('fs');
|
|
357
|
+
const path = require('path');
|
|
358
|
+
|
|
359
|
+
const MEMORY_DIR = path.join(process.cwd(), '.claude-flow', 'data');
|
|
360
|
+
const MEMORY_FILE = path.join(MEMORY_DIR, 'memory.json');
|
|
361
|
+
|
|
362
|
+
function loadMemory() {
|
|
363
|
+
try {
|
|
364
|
+
if (fs.existsSync(MEMORY_FILE)) {
|
|
365
|
+
return JSON.parse(fs.readFileSync(MEMORY_FILE, 'utf-8'));
|
|
366
|
+
}
|
|
367
|
+
} catch (e) {
|
|
368
|
+
// Ignore
|
|
369
|
+
}
|
|
370
|
+
return {};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function saveMemory(memory) {
|
|
374
|
+
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
375
|
+
fs.writeFileSync(MEMORY_FILE, JSON.stringify(memory, null, 2));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const commands = {
|
|
379
|
+
get: (key) => {
|
|
380
|
+
const memory = loadMemory();
|
|
381
|
+
const value = key ? memory[key] : memory;
|
|
382
|
+
console.log(JSON.stringify(value, null, 2));
|
|
383
|
+
return value;
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
set: (key, value) => {
|
|
387
|
+
if (!key) {
|
|
388
|
+
console.error('Key required');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const memory = loadMemory();
|
|
392
|
+
memory[key] = value;
|
|
393
|
+
memory._updated = new Date().toISOString();
|
|
394
|
+
saveMemory(memory);
|
|
395
|
+
console.log(\`Set: \${key}\`);
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
delete: (key) => {
|
|
399
|
+
if (!key) {
|
|
400
|
+
console.error('Key required');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const memory = loadMemory();
|
|
404
|
+
delete memory[key];
|
|
405
|
+
saveMemory(memory);
|
|
406
|
+
console.log(\`Deleted: \${key}\`);
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
clear: () => {
|
|
410
|
+
saveMemory({});
|
|
411
|
+
console.log('Memory cleared');
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
keys: () => {
|
|
415
|
+
const memory = loadMemory();
|
|
416
|
+
const keys = Object.keys(memory).filter(k => !k.startsWith('_'));
|
|
417
|
+
console.log(keys.join('\\n'));
|
|
418
|
+
return keys;
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// CLI
|
|
423
|
+
const [,, command, key, ...valueParts] = process.argv;
|
|
424
|
+
const value = valueParts.join(' ');
|
|
425
|
+
|
|
426
|
+
if (command && commands[command]) {
|
|
427
|
+
commands[command](key, value);
|
|
428
|
+
} else {
|
|
429
|
+
console.log('Usage: memory.js <get|set|delete|clear|keys> [key] [value]');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
module.exports = commands;
|
|
433
433
|
`;
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
@@ -992,375 +992,375 @@ export function generateIntelligenceStub() {
|
|
|
992
992
|
* @claude-flow/memory is not installed. Gets overwritten when source copy succeeds.
|
|
993
993
|
*/
|
|
994
994
|
export function generateAutoMemoryHook() {
|
|
995
|
-
return `#!/usr/bin/env node
|
|
996
|
-
/**
|
|
997
|
-
* Auto Memory Bridge Hook (ADR-048/049) — Minimal Fallback
|
|
998
|
-
* Full version is copied from package source when available.
|
|
999
|
-
*
|
|
1000
|
-
* Usage:
|
|
1001
|
-
* node auto-memory-hook.mjs import # SessionStart
|
|
1002
|
-
* node auto-memory-hook.mjs sync # SessionEnd / Stop
|
|
1003
|
-
* node auto-memory-hook.mjs status # Show bridge status
|
|
1004
|
-
*/
|
|
1005
|
-
|
|
1006
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
1007
|
-
import { join, dirname } from 'path';
|
|
1008
|
-
import { fileURLToPath } from 'url';
|
|
1009
|
-
|
|
1010
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
1011
|
-
const __dirname = dirname(__filename);
|
|
1012
|
-
const PROJECT_ROOT = join(__dirname, '../..');
|
|
1013
|
-
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
|
|
1014
|
-
const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
|
|
1015
|
-
|
|
1016
|
-
const DIM = '\\x1b[2m';
|
|
1017
|
-
const YELLOW = '\\x1b[0;33m';
|
|
1018
|
-
const RESET = '\\x1b[0m';
|
|
1019
|
-
const dim = (msg) => console.log(\` \${DIM}\${msg}\${RESET}\`);
|
|
1020
|
-
|
|
1021
|
-
// #2545: fail LOUD instead of a silent dim skip when @claude-flow/memory is
|
|
1022
|
-
// unresolvable — self-learning imports are a no-op and the user must be told.
|
|
1023
|
-
function warnMemoryUnavailable() {
|
|
1024
|
-
const l1 = \`[AutoMemory] @claude-flow/memory not resolvable from \${PROJECT_ROOT} — self-learning imports are DISABLED.\`;
|
|
1025
|
-
const l2 = ' Fix: npm i -D @claude-flow/memory (or re-run: npx ruflo@latest init, then npx ruflo@latest doctor --fix)';
|
|
1026
|
-
console.log(\`\${YELLOW}\${l1}\${RESET}\`);
|
|
1027
|
-
console.log(\`\${YELLOW}\${l2}\${RESET}\`);
|
|
1028
|
-
process.stderr.write(\`\${l1}\\n\${l2}\\n\`);
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
// Ensure data dir
|
|
1032
|
-
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
|
|
1033
|
-
|
|
1034
|
-
async function loadMemoryPackage() {
|
|
1035
|
-
// Strategy 0 (#2545): sidecar recorded by \`init\` / \`doctor --fix\`. On the npx
|
|
1036
|
-
// path @claude-flow/memory lands in the npx cache (unreachable by walk-up), so
|
|
1037
|
-
// init records its absolute path here — the only strategy that works there.
|
|
1038
|
-
try {
|
|
1039
|
-
const sidecar = join(PROJECT_ROOT, '.claude-flow', 'memory-package.json');
|
|
1040
|
-
if (existsSync(sidecar)) {
|
|
1041
|
-
const rec = JSON.parse(readFileSync(sidecar, 'utf-8'));
|
|
1042
|
-
if (rec && rec.distPath && existsSync(rec.distPath)) {
|
|
1043
|
-
return await import(\`file://\${rec.distPath}\`);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
} catch { /* fall through */ }
|
|
1047
|
-
|
|
1048
|
-
// Strategy 1: Use createRequire for CJS-style resolution (handles nested node_modules
|
|
1049
|
-
// when installed as a transitive dependency via npx ruflo / npx claude-flow)
|
|
1050
|
-
try {
|
|
1051
|
-
const { createRequire } = await import('module');
|
|
1052
|
-
const require = createRequire(join(PROJECT_ROOT, 'package.json'));
|
|
1053
|
-
return require('@claude-flow/memory');
|
|
1054
|
-
} catch { /* fall through */ }
|
|
1055
|
-
|
|
1056
|
-
// Strategy 2: ESM import (works when @claude-flow/memory is a direct dependency)
|
|
1057
|
-
try { return await import('@claude-flow/memory'); } catch { /* fall through */ }
|
|
1058
|
-
|
|
1059
|
-
// Strategy 3: Walk up from PROJECT_ROOT looking for the package in any node_modules
|
|
1060
|
-
let searchDir = PROJECT_ROOT;
|
|
1061
|
-
const { parse } = await import('path');
|
|
1062
|
-
while (searchDir !== parse(searchDir).root) {
|
|
1063
|
-
const candidate = join(searchDir, 'node_modules', '@claude-flow', 'memory', 'dist', 'index.js');
|
|
1064
|
-
if (existsSync(candidate)) {
|
|
1065
|
-
try { return await import(\`file://\${candidate}\`); } catch { /* fall through */ }
|
|
1066
|
-
}
|
|
1067
|
-
searchDir = dirname(searchDir);
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
return null;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
async function doImport() {
|
|
1074
|
-
const memPkg = await loadMemoryPackage();
|
|
1075
|
-
|
|
1076
|
-
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
1077
|
-
warnMemoryUnavailable();
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// Full implementation deferred to copied version
|
|
1082
|
-
dim('Auto memory import available — run init --upgrade for full support');
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
async function doSync() {
|
|
1086
|
-
if (!existsSync(STORE_PATH)) {
|
|
1087
|
-
dim('No entries to sync');
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
const memPkg = await loadMemoryPackage();
|
|
1092
|
-
|
|
1093
|
-
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
1094
|
-
warnMemoryUnavailable();
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
dim('Auto memory sync available — run init --upgrade for full support');
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
function doStatus() {
|
|
1102
|
-
console.log('\\n=== Auto Memory Bridge Status ===\\n');
|
|
1103
|
-
console.log(' Package: Fallback mode (run init --upgrade for full)');
|
|
1104
|
-
console.log(\` Store: \${existsSync(STORE_PATH) ? 'Initialized' : 'Not initialized'}\`);
|
|
1105
|
-
console.log('');
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
// Suppress unhandled rejection warnings from dynamic import() failures
|
|
1109
|
-
process.on('unhandledRejection', () => {});
|
|
1110
|
-
|
|
1111
|
-
const command = process.argv[2] || 'status';
|
|
1112
|
-
|
|
1113
|
-
try {
|
|
1114
|
-
switch (command) {
|
|
1115
|
-
case 'import': await doImport(); break;
|
|
1116
|
-
case 'sync': await doSync(); break;
|
|
1117
|
-
case 'status': doStatus(); break;
|
|
1118
|
-
default:
|
|
1119
|
-
console.log('Usage: auto-memory-hook.mjs <import|sync|status>');
|
|
1120
|
-
process.exit(1);
|
|
1121
|
-
}
|
|
1122
|
-
} catch (err) {
|
|
1123
|
-
// Hooks must never crash Claude Code - fail silently
|
|
1124
|
-
dim(\`Error (non-critical): \${err.message}\`);
|
|
1125
|
-
}
|
|
1126
|
-
// Ensure clean exit for Claude Code hooks (exit 0 = success)
|
|
1127
|
-
process.exit(0);
|
|
995
|
+
return `#!/usr/bin/env node
|
|
996
|
+
/**
|
|
997
|
+
* Auto Memory Bridge Hook (ADR-048/049) — Minimal Fallback
|
|
998
|
+
* Full version is copied from package source when available.
|
|
999
|
+
*
|
|
1000
|
+
* Usage:
|
|
1001
|
+
* node auto-memory-hook.mjs import # SessionStart
|
|
1002
|
+
* node auto-memory-hook.mjs sync # SessionEnd / Stop
|
|
1003
|
+
* node auto-memory-hook.mjs status # Show bridge status
|
|
1004
|
+
*/
|
|
1005
|
+
|
|
1006
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
1007
|
+
import { join, dirname } from 'path';
|
|
1008
|
+
import { fileURLToPath } from 'url';
|
|
1009
|
+
|
|
1010
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1011
|
+
const __dirname = dirname(__filename);
|
|
1012
|
+
const PROJECT_ROOT = join(__dirname, '../..');
|
|
1013
|
+
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
|
|
1014
|
+
const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
|
|
1015
|
+
|
|
1016
|
+
const DIM = '\\x1b[2m';
|
|
1017
|
+
const YELLOW = '\\x1b[0;33m';
|
|
1018
|
+
const RESET = '\\x1b[0m';
|
|
1019
|
+
const dim = (msg) => console.log(\` \${DIM}\${msg}\${RESET}\`);
|
|
1020
|
+
|
|
1021
|
+
// #2545: fail LOUD instead of a silent dim skip when @claude-flow/memory is
|
|
1022
|
+
// unresolvable — self-learning imports are a no-op and the user must be told.
|
|
1023
|
+
function warnMemoryUnavailable() {
|
|
1024
|
+
const l1 = \`[AutoMemory] @claude-flow/memory not resolvable from \${PROJECT_ROOT} — self-learning imports are DISABLED.\`;
|
|
1025
|
+
const l2 = ' Fix: npm i -D @claude-flow/memory (or re-run: npx ruflo@latest init, then npx ruflo@latest doctor --fix)';
|
|
1026
|
+
console.log(\`\${YELLOW}\${l1}\${RESET}\`);
|
|
1027
|
+
console.log(\`\${YELLOW}\${l2}\${RESET}\`);
|
|
1028
|
+
process.stderr.write(\`\${l1}\\n\${l2}\\n\`);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Ensure data dir
|
|
1032
|
+
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
|
|
1033
|
+
|
|
1034
|
+
async function loadMemoryPackage() {
|
|
1035
|
+
// Strategy 0 (#2545): sidecar recorded by \`init\` / \`doctor --fix\`. On the npx
|
|
1036
|
+
// path @claude-flow/memory lands in the npx cache (unreachable by walk-up), so
|
|
1037
|
+
// init records its absolute path here — the only strategy that works there.
|
|
1038
|
+
try {
|
|
1039
|
+
const sidecar = join(PROJECT_ROOT, '.claude-flow', 'memory-package.json');
|
|
1040
|
+
if (existsSync(sidecar)) {
|
|
1041
|
+
const rec = JSON.parse(readFileSync(sidecar, 'utf-8'));
|
|
1042
|
+
if (rec && rec.distPath && existsSync(rec.distPath)) {
|
|
1043
|
+
return await import(\`file://\${rec.distPath}\`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
} catch { /* fall through */ }
|
|
1047
|
+
|
|
1048
|
+
// Strategy 1: Use createRequire for CJS-style resolution (handles nested node_modules
|
|
1049
|
+
// when installed as a transitive dependency via npx ruflo / npx claude-flow)
|
|
1050
|
+
try {
|
|
1051
|
+
const { createRequire } = await import('module');
|
|
1052
|
+
const require = createRequire(join(PROJECT_ROOT, 'package.json'));
|
|
1053
|
+
return require('@claude-flow/memory');
|
|
1054
|
+
} catch { /* fall through */ }
|
|
1055
|
+
|
|
1056
|
+
// Strategy 2: ESM import (works when @claude-flow/memory is a direct dependency)
|
|
1057
|
+
try { return await import('@claude-flow/memory'); } catch { /* fall through */ }
|
|
1058
|
+
|
|
1059
|
+
// Strategy 3: Walk up from PROJECT_ROOT looking for the package in any node_modules
|
|
1060
|
+
let searchDir = PROJECT_ROOT;
|
|
1061
|
+
const { parse } = await import('path');
|
|
1062
|
+
while (searchDir !== parse(searchDir).root) {
|
|
1063
|
+
const candidate = join(searchDir, 'node_modules', '@claude-flow', 'memory', 'dist', 'index.js');
|
|
1064
|
+
if (existsSync(candidate)) {
|
|
1065
|
+
try { return await import(\`file://\${candidate}\`); } catch { /* fall through */ }
|
|
1066
|
+
}
|
|
1067
|
+
searchDir = dirname(searchDir);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
async function doImport() {
|
|
1074
|
+
const memPkg = await loadMemoryPackage();
|
|
1075
|
+
|
|
1076
|
+
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
1077
|
+
warnMemoryUnavailable();
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Full implementation deferred to copied version
|
|
1082
|
+
dim('Auto memory import available — run init --upgrade for full support');
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
async function doSync() {
|
|
1086
|
+
if (!existsSync(STORE_PATH)) {
|
|
1087
|
+
dim('No entries to sync');
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const memPkg = await loadMemoryPackage();
|
|
1092
|
+
|
|
1093
|
+
if (!memPkg || !memPkg.AutoMemoryBridge) {
|
|
1094
|
+
warnMemoryUnavailable();
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
dim('Auto memory sync available — run init --upgrade for full support');
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
function doStatus() {
|
|
1102
|
+
console.log('\\n=== Auto Memory Bridge Status ===\\n');
|
|
1103
|
+
console.log(' Package: Fallback mode (run init --upgrade for full)');
|
|
1104
|
+
console.log(\` Store: \${existsSync(STORE_PATH) ? 'Initialized' : 'Not initialized'}\`);
|
|
1105
|
+
console.log('');
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Suppress unhandled rejection warnings from dynamic import() failures
|
|
1109
|
+
process.on('unhandledRejection', () => {});
|
|
1110
|
+
|
|
1111
|
+
const command = process.argv[2] || 'status';
|
|
1112
|
+
|
|
1113
|
+
try {
|
|
1114
|
+
switch (command) {
|
|
1115
|
+
case 'import': await doImport(); break;
|
|
1116
|
+
case 'sync': await doSync(); break;
|
|
1117
|
+
case 'status': doStatus(); break;
|
|
1118
|
+
default:
|
|
1119
|
+
console.log('Usage: auto-memory-hook.mjs <import|sync|status>');
|
|
1120
|
+
process.exit(1);
|
|
1121
|
+
}
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
// Hooks must never crash Claude Code - fail silently
|
|
1124
|
+
dim(\`Error (non-critical): \${err.message}\`);
|
|
1125
|
+
}
|
|
1126
|
+
// Ensure clean exit for Claude Code hooks (exit 0 = success)
|
|
1127
|
+
process.exit(0);
|
|
1128
1128
|
`;
|
|
1129
1129
|
}
|
|
1130
1130
|
/**
|
|
1131
1131
|
* Generate Windows PowerShell daemon manager
|
|
1132
1132
|
*/
|
|
1133
1133
|
export function generateWindowsDaemonManager() {
|
|
1134
|
-
return `# RuFlo V3 Daemon Manager for Windows
|
|
1135
|
-
# PowerShell script for managing background processes
|
|
1136
|
-
|
|
1137
|
-
param(
|
|
1138
|
-
[Parameter(Position=0)]
|
|
1139
|
-
[ValidateSet('start', 'stop', 'status', 'restart')]
|
|
1140
|
-
[string]$Action = 'status'
|
|
1141
|
-
)
|
|
1142
|
-
|
|
1143
|
-
$ErrorActionPreference = 'SilentlyContinue'
|
|
1144
|
-
$ClaudeFlowDir = Join-Path $PWD '.claude-flow'
|
|
1145
|
-
$PidDir = Join-Path $ClaudeFlowDir 'pids'
|
|
1146
|
-
|
|
1147
|
-
# Ensure directories exist
|
|
1148
|
-
if (-not (Test-Path $PidDir)) {
|
|
1149
|
-
New-Item -ItemType Directory -Path $PidDir -Force | Out-Null
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
function Get-DaemonStatus {
|
|
1153
|
-
param([string]$Name, [string]$PidFile)
|
|
1154
|
-
|
|
1155
|
-
if (Test-Path $PidFile) {
|
|
1156
|
-
$pid = Get-Content $PidFile
|
|
1157
|
-
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
|
1158
|
-
if ($process) {
|
|
1159
|
-
return @{ Running = $true; Pid = $pid }
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
return @{ Running = $false; Pid = $null }
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
function Start-SwarmMonitor {
|
|
1166
|
-
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1167
|
-
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
1168
|
-
|
|
1169
|
-
if ($status.Running) {
|
|
1170
|
-
Write-Host "Swarm monitor already running (PID: $($status.Pid))" -ForegroundColor Yellow
|
|
1171
|
-
return
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
Write-Host "Starting swarm monitor..." -ForegroundColor Cyan
|
|
1175
|
-
$process = Start-Process -FilePath 'node' -ArgumentList @(
|
|
1176
|
-
'-e',
|
|
1177
|
-
'setInterval(() => { require("fs").writeFileSync(".claude-flow/metrics/swarm-activity.json", JSON.stringify({swarm:{active:true,agent_count:0},timestamp:Date.now()})) }, 5000)'
|
|
1178
|
-
) -PassThru -WindowStyle Hidden
|
|
1179
|
-
|
|
1180
|
-
$process.Id | Out-File $pidFile
|
|
1181
|
-
Write-Host "Swarm monitor started (PID: $($process.Id))" -ForegroundColor Green
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function Stop-SwarmMonitor {
|
|
1185
|
-
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1186
|
-
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
1187
|
-
|
|
1188
|
-
if (-not $status.Running) {
|
|
1189
|
-
Write-Host "Swarm monitor not running" -ForegroundColor Yellow
|
|
1190
|
-
return
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
Stop-Process -Id $status.Pid -Force
|
|
1194
|
-
Remove-Item $pidFile -Force
|
|
1195
|
-
Write-Host "Swarm monitor stopped" -ForegroundColor Green
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
function Show-Status {
|
|
1199
|
-
Write-Host ""
|
|
1200
|
-
Write-Host "RuFlo V3 Daemon Status" -ForegroundColor Cyan
|
|
1201
|
-
Write-Host "=============================" -ForegroundColor Cyan
|
|
1202
|
-
|
|
1203
|
-
$swarmPid = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1204
|
-
$swarmStatus = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $swarmPid
|
|
1205
|
-
|
|
1206
|
-
if ($swarmStatus.Running) {
|
|
1207
|
-
Write-Host " Swarm Monitor: RUNNING (PID: $($swarmStatus.Pid))" -ForegroundColor Green
|
|
1208
|
-
} else {
|
|
1209
|
-
Write-Host " Swarm Monitor: STOPPED" -ForegroundColor Red
|
|
1210
|
-
}
|
|
1211
|
-
Write-Host ""
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
switch ($Action) {
|
|
1215
|
-
'start' {
|
|
1216
|
-
Start-SwarmMonitor
|
|
1217
|
-
Show-Status
|
|
1218
|
-
}
|
|
1219
|
-
'stop' {
|
|
1220
|
-
Stop-SwarmMonitor
|
|
1221
|
-
Show-Status
|
|
1222
|
-
}
|
|
1223
|
-
'restart' {
|
|
1224
|
-
Stop-SwarmMonitor
|
|
1225
|
-
Start-Sleep -Seconds 1
|
|
1226
|
-
Start-SwarmMonitor
|
|
1227
|
-
Show-Status
|
|
1228
|
-
}
|
|
1229
|
-
'status' {
|
|
1230
|
-
Show-Status
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1134
|
+
return `# RuFlo V3 Daemon Manager for Windows
|
|
1135
|
+
# PowerShell script for managing background processes
|
|
1136
|
+
|
|
1137
|
+
param(
|
|
1138
|
+
[Parameter(Position=0)]
|
|
1139
|
+
[ValidateSet('start', 'stop', 'status', 'restart')]
|
|
1140
|
+
[string]$Action = 'status'
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
1144
|
+
$ClaudeFlowDir = Join-Path $PWD '.claude-flow'
|
|
1145
|
+
$PidDir = Join-Path $ClaudeFlowDir 'pids'
|
|
1146
|
+
|
|
1147
|
+
# Ensure directories exist
|
|
1148
|
+
if (-not (Test-Path $PidDir)) {
|
|
1149
|
+
New-Item -ItemType Directory -Path $PidDir -Force | Out-Null
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
function Get-DaemonStatus {
|
|
1153
|
+
param([string]$Name, [string]$PidFile)
|
|
1154
|
+
|
|
1155
|
+
if (Test-Path $PidFile) {
|
|
1156
|
+
$pid = Get-Content $PidFile
|
|
1157
|
+
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
|
1158
|
+
if ($process) {
|
|
1159
|
+
return @{ Running = $true; Pid = $pid }
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return @{ Running = $false; Pid = $null }
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function Start-SwarmMonitor {
|
|
1166
|
+
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1167
|
+
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
1168
|
+
|
|
1169
|
+
if ($status.Running) {
|
|
1170
|
+
Write-Host "Swarm monitor already running (PID: $($status.Pid))" -ForegroundColor Yellow
|
|
1171
|
+
return
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
Write-Host "Starting swarm monitor..." -ForegroundColor Cyan
|
|
1175
|
+
$process = Start-Process -FilePath 'node' -ArgumentList @(
|
|
1176
|
+
'-e',
|
|
1177
|
+
'setInterval(() => { require("fs").writeFileSync(".claude-flow/metrics/swarm-activity.json", JSON.stringify({swarm:{active:true,agent_count:0},timestamp:Date.now()})) }, 5000)'
|
|
1178
|
+
) -PassThru -WindowStyle Hidden
|
|
1179
|
+
|
|
1180
|
+
$process.Id | Out-File $pidFile
|
|
1181
|
+
Write-Host "Swarm monitor started (PID: $($process.Id))" -ForegroundColor Green
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function Stop-SwarmMonitor {
|
|
1185
|
+
$pidFile = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1186
|
+
$status = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $pidFile
|
|
1187
|
+
|
|
1188
|
+
if (-not $status.Running) {
|
|
1189
|
+
Write-Host "Swarm monitor not running" -ForegroundColor Yellow
|
|
1190
|
+
return
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
Stop-Process -Id $status.Pid -Force
|
|
1194
|
+
Remove-Item $pidFile -Force
|
|
1195
|
+
Write-Host "Swarm monitor stopped" -ForegroundColor Green
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
function Show-Status {
|
|
1199
|
+
Write-Host ""
|
|
1200
|
+
Write-Host "RuFlo V3 Daemon Status" -ForegroundColor Cyan
|
|
1201
|
+
Write-Host "=============================" -ForegroundColor Cyan
|
|
1202
|
+
|
|
1203
|
+
$swarmPid = Join-Path $PidDir 'swarm-monitor.pid'
|
|
1204
|
+
$swarmStatus = Get-DaemonStatus -Name 'swarm-monitor' -PidFile $swarmPid
|
|
1205
|
+
|
|
1206
|
+
if ($swarmStatus.Running) {
|
|
1207
|
+
Write-Host " Swarm Monitor: RUNNING (PID: $($swarmStatus.Pid))" -ForegroundColor Green
|
|
1208
|
+
} else {
|
|
1209
|
+
Write-Host " Swarm Monitor: STOPPED" -ForegroundColor Red
|
|
1210
|
+
}
|
|
1211
|
+
Write-Host ""
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
switch ($Action) {
|
|
1215
|
+
'start' {
|
|
1216
|
+
Start-SwarmMonitor
|
|
1217
|
+
Show-Status
|
|
1218
|
+
}
|
|
1219
|
+
'stop' {
|
|
1220
|
+
Stop-SwarmMonitor
|
|
1221
|
+
Show-Status
|
|
1222
|
+
}
|
|
1223
|
+
'restart' {
|
|
1224
|
+
Stop-SwarmMonitor
|
|
1225
|
+
Start-Sleep -Seconds 1
|
|
1226
|
+
Start-SwarmMonitor
|
|
1227
|
+
Show-Status
|
|
1228
|
+
}
|
|
1229
|
+
'status' {
|
|
1230
|
+
Show-Status
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
1233
|
`;
|
|
1234
1234
|
}
|
|
1235
1235
|
/**
|
|
1236
1236
|
* Generate Windows batch file wrapper
|
|
1237
1237
|
*/
|
|
1238
1238
|
export function generateWindowsBatchWrapper() {
|
|
1239
|
-
return `@echo off
|
|
1240
|
-
REM RuFlo V3 - Windows Batch Wrapper
|
|
1241
|
-
REM Routes to PowerShell daemon manager
|
|
1242
|
-
|
|
1243
|
-
PowerShell -ExecutionPolicy Bypass -File "%~dp0daemon-manager.ps1" %*
|
|
1239
|
+
return `@echo off
|
|
1240
|
+
REM RuFlo V3 - Windows Batch Wrapper
|
|
1241
|
+
REM Routes to PowerShell daemon manager
|
|
1242
|
+
|
|
1243
|
+
PowerShell -ExecutionPolicy Bypass -File "%~dp0daemon-manager.ps1" %*
|
|
1244
1244
|
`;
|
|
1245
1245
|
}
|
|
1246
1246
|
/**
|
|
1247
1247
|
* Generate cross-platform session manager
|
|
1248
1248
|
*/
|
|
1249
1249
|
export function generateCrossPlatformSessionManager() {
|
|
1250
|
-
return `#!/usr/bin/env node
|
|
1251
|
-
/**
|
|
1252
|
-
* Ruflo Cross-Platform Session Manager
|
|
1253
|
-
* Works on Windows, macOS, and Linux
|
|
1254
|
-
*/
|
|
1255
|
-
|
|
1256
|
-
const fs = require('fs');
|
|
1257
|
-
const path = require('path');
|
|
1258
|
-
const os = require('os');
|
|
1259
|
-
|
|
1260
|
-
// Platform-specific paths
|
|
1261
|
-
const platform = os.platform();
|
|
1262
|
-
const homeDir = os.homedir();
|
|
1263
|
-
|
|
1264
|
-
// Get data directory based on platform
|
|
1265
|
-
function getDataDir() {
|
|
1266
|
-
const localDir = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
1267
|
-
if (fs.existsSync(path.dirname(localDir))) {
|
|
1268
|
-
return localDir;
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
switch (platform) {
|
|
1272
|
-
case 'win32':
|
|
1273
|
-
return path.join(process.env.APPDATA || homeDir, 'claude-flow', 'sessions');
|
|
1274
|
-
case 'darwin':
|
|
1275
|
-
return path.join(homeDir, 'Library', 'Application Support', 'claude-flow', 'sessions');
|
|
1276
|
-
default:
|
|
1277
|
-
return path.join(homeDir, '.claude-flow', 'sessions');
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
const SESSION_DIR = getDataDir();
|
|
1282
|
-
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
1283
|
-
|
|
1284
|
-
// Ensure directory exists
|
|
1285
|
-
function ensureDir(dir) {
|
|
1286
|
-
if (!fs.existsSync(dir)) {
|
|
1287
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
const commands = {
|
|
1292
|
-
start: () => {
|
|
1293
|
-
ensureDir(SESSION_DIR);
|
|
1294
|
-
const sessionId = \`session-\${Date.now()}\`;
|
|
1295
|
-
const session = {
|
|
1296
|
-
id: sessionId,
|
|
1297
|
-
startedAt: new Date().toISOString(),
|
|
1298
|
-
platform: platform,
|
|
1299
|
-
cwd: process.cwd(),
|
|
1300
|
-
context: {},
|
|
1301
|
-
metrics: { edits: 0, commands: 0, tasks: 0, errors: 0 }
|
|
1302
|
-
};
|
|
1303
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1304
|
-
console.log(\`Session started: \${sessionId}\`);
|
|
1305
|
-
return session;
|
|
1306
|
-
},
|
|
1307
|
-
|
|
1308
|
-
restore: () => {
|
|
1309
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1310
|
-
console.log('No session to restore');
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1314
|
-
session.restoredAt = new Date().toISOString();
|
|
1315
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1316
|
-
console.log(\`Session restored: \${session.id}\`);
|
|
1317
|
-
return session;
|
|
1318
|
-
},
|
|
1319
|
-
|
|
1320
|
-
end: () => {
|
|
1321
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1322
|
-
console.log('No active session');
|
|
1323
|
-
return null;
|
|
1324
|
-
}
|
|
1325
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1326
|
-
session.endedAt = new Date().toISOString();
|
|
1327
|
-
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1328
|
-
|
|
1329
|
-
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
1330
|
-
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
1331
|
-
fs.unlinkSync(SESSION_FILE);
|
|
1332
|
-
|
|
1333
|
-
console.log(\`Session ended: \${session.id}\`);
|
|
1334
|
-
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
1335
|
-
return session;
|
|
1336
|
-
},
|
|
1337
|
-
|
|
1338
|
-
status: () => {
|
|
1339
|
-
if (!fs.existsSync(SESSION_FILE)) {
|
|
1340
|
-
console.log('No active session');
|
|
1341
|
-
return null;
|
|
1342
|
-
}
|
|
1343
|
-
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1344
|
-
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1345
|
-
console.log(\`Session: \${session.id}\`);
|
|
1346
|
-
console.log(\`Platform: \${session.platform}\`);
|
|
1347
|
-
console.log(\`Started: \${session.startedAt}\`);
|
|
1348
|
-
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
1349
|
-
return session;
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
|
|
1353
|
-
// CLI
|
|
1354
|
-
const [,, command, ...args] = process.argv;
|
|
1355
|
-
if (command && commands[command]) {
|
|
1356
|
-
commands[command](...args);
|
|
1357
|
-
} else {
|
|
1358
|
-
console.log('Usage: session.js <start|restore|end|status>');
|
|
1359
|
-
console.log(\`Platform: \${platform}\`);
|
|
1360
|
-
console.log(\`Data dir: \${SESSION_DIR}\`);
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
module.exports = commands;
|
|
1250
|
+
return `#!/usr/bin/env node
|
|
1251
|
+
/**
|
|
1252
|
+
* Ruflo Cross-Platform Session Manager
|
|
1253
|
+
* Works on Windows, macOS, and Linux
|
|
1254
|
+
*/
|
|
1255
|
+
|
|
1256
|
+
const fs = require('fs');
|
|
1257
|
+
const path = require('path');
|
|
1258
|
+
const os = require('os');
|
|
1259
|
+
|
|
1260
|
+
// Platform-specific paths
|
|
1261
|
+
const platform = os.platform();
|
|
1262
|
+
const homeDir = os.homedir();
|
|
1263
|
+
|
|
1264
|
+
// Get data directory based on platform
|
|
1265
|
+
function getDataDir() {
|
|
1266
|
+
const localDir = path.join(process.cwd(), '.claude-flow', 'sessions');
|
|
1267
|
+
if (fs.existsSync(path.dirname(localDir))) {
|
|
1268
|
+
return localDir;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
switch (platform) {
|
|
1272
|
+
case 'win32':
|
|
1273
|
+
return path.join(process.env.APPDATA || homeDir, 'claude-flow', 'sessions');
|
|
1274
|
+
case 'darwin':
|
|
1275
|
+
return path.join(homeDir, 'Library', 'Application Support', 'claude-flow', 'sessions');
|
|
1276
|
+
default:
|
|
1277
|
+
return path.join(homeDir, '.claude-flow', 'sessions');
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const SESSION_DIR = getDataDir();
|
|
1282
|
+
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
|
|
1283
|
+
|
|
1284
|
+
// Ensure directory exists
|
|
1285
|
+
function ensureDir(dir) {
|
|
1286
|
+
if (!fs.existsSync(dir)) {
|
|
1287
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const commands = {
|
|
1292
|
+
start: () => {
|
|
1293
|
+
ensureDir(SESSION_DIR);
|
|
1294
|
+
const sessionId = \`session-\${Date.now()}\`;
|
|
1295
|
+
const session = {
|
|
1296
|
+
id: sessionId,
|
|
1297
|
+
startedAt: new Date().toISOString(),
|
|
1298
|
+
platform: platform,
|
|
1299
|
+
cwd: process.cwd(),
|
|
1300
|
+
context: {},
|
|
1301
|
+
metrics: { edits: 0, commands: 0, tasks: 0, errors: 0 }
|
|
1302
|
+
};
|
|
1303
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1304
|
+
console.log(\`Session started: \${sessionId}\`);
|
|
1305
|
+
return session;
|
|
1306
|
+
},
|
|
1307
|
+
|
|
1308
|
+
restore: () => {
|
|
1309
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1310
|
+
console.log('No session to restore');
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1314
|
+
session.restoredAt = new Date().toISOString();
|
|
1315
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
1316
|
+
console.log(\`Session restored: \${session.id}\`);
|
|
1317
|
+
return session;
|
|
1318
|
+
},
|
|
1319
|
+
|
|
1320
|
+
end: () => {
|
|
1321
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1322
|
+
console.log('No active session');
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1326
|
+
session.endedAt = new Date().toISOString();
|
|
1327
|
+
session.duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1328
|
+
|
|
1329
|
+
const archivePath = path.join(SESSION_DIR, \`\${session.id}.json\`);
|
|
1330
|
+
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
|
|
1331
|
+
fs.unlinkSync(SESSION_FILE);
|
|
1332
|
+
|
|
1333
|
+
console.log(\`Session ended: \${session.id}\`);
|
|
1334
|
+
console.log(\`Duration: \${Math.round(session.duration / 1000 / 60)} minutes\`);
|
|
1335
|
+
return session;
|
|
1336
|
+
},
|
|
1337
|
+
|
|
1338
|
+
status: () => {
|
|
1339
|
+
if (!fs.existsSync(SESSION_FILE)) {
|
|
1340
|
+
console.log('No active session');
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
1344
|
+
const duration = Date.now() - new Date(session.startedAt).getTime();
|
|
1345
|
+
console.log(\`Session: \${session.id}\`);
|
|
1346
|
+
console.log(\`Platform: \${session.platform}\`);
|
|
1347
|
+
console.log(\`Started: \${session.startedAt}\`);
|
|
1348
|
+
console.log(\`Duration: \${Math.round(duration / 1000 / 60)} minutes\`);
|
|
1349
|
+
return session;
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
// CLI
|
|
1354
|
+
const [,, command, ...args] = process.argv;
|
|
1355
|
+
if (command && commands[command]) {
|
|
1356
|
+
commands[command](...args);
|
|
1357
|
+
} else {
|
|
1358
|
+
console.log('Usage: session.js <start|restore|end|status>');
|
|
1359
|
+
console.log(\`Platform: \${platform}\`);
|
|
1360
|
+
console.log(\`Data dir: \${SESSION_DIR}\`);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
module.exports = commands;
|
|
1364
1364
|
`;
|
|
1365
1365
|
}
|
|
1366
1366
|
/**
|
|
@@ -1403,65 +1403,231 @@ export function generateHelpers(options) {
|
|
|
1403
1403
|
* - Both: .claude/helpers/ruflo-hook.cjs available as a canonical cross-platform shim
|
|
1404
1404
|
*/
|
|
1405
1405
|
export function generateRufloHookCjs() {
|
|
1406
|
-
return `#!/usr/bin/env node
|
|
1407
|
-
/**
|
|
1408
|
-
* ruflo-hook.cjs — cross-platform Node.js port of ruflo-hook.sh (#2132)
|
|
1409
|
-
*
|
|
1410
|
-
* Deployed to .claude/helpers/ during ruflo init. On Windows, the
|
|
1411
|
-
* generated .claude/settings.json hooks point here instead of the
|
|
1412
|
-
* plugin's bash-only ruflo-hook.sh.
|
|
1413
|
-
*
|
|
1414
|
-
* Always exits 0 — hook subcommands are best-effort telemetry and must
|
|
1415
|
-
* never block a Claude Code turn.
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1406
|
+
return `#!/usr/bin/env node
|
|
1407
|
+
/**
|
|
1408
|
+
* ruflo-hook.cjs — cross-platform Node.js port of ruflo-hook.sh (#2132)
|
|
1409
|
+
*
|
|
1410
|
+
* Deployed to .claude/helpers/ during ruflo init. On Windows, the
|
|
1411
|
+
* generated .claude/settings.json hooks point here instead of the
|
|
1412
|
+
* plugin's bash-only ruflo-hook.sh.
|
|
1413
|
+
*
|
|
1414
|
+
* Always exits 0 — hook subcommands are best-effort telemetry and must
|
|
1415
|
+
* never block a Claude Code turn.
|
|
1416
|
+
*
|
|
1417
|
+
* Windows argv integrity: hook-derived values (a Bash tool's \`command\`, a file path) must
|
|
1418
|
+
* reach the CLI as literal argv and never as shell syntax. Two layers, in
|
|
1419
|
+
* this order:
|
|
1420
|
+
*
|
|
1421
|
+
* 1. resolveInvocation() maps the command to a real executable — and on
|
|
1422
|
+
* Windows maps npm's .cmd shim to the package's own .js entrypoint —
|
|
1423
|
+
* so the spawn runs \`node <entry>\` with shell:false and NO cmd.exe in
|
|
1424
|
+
* the chain at all. Nothing to escape, nothing to re-tokenize, and no
|
|
1425
|
+
* %VAR% expansion. (The escaped fallback was measured on a real
|
|
1426
|
+
* windows-latest runner and does pass %VAR% through literally; layer 1
|
|
1427
|
+
* is still preferred because it removes the parser rather than
|
|
1428
|
+
* out-guessing it.)
|
|
1429
|
+
* 2. escapeCmdArg() guards the residual Windows path where step 1 cannot
|
|
1430
|
+
* identify an entrypoint. Escaping is strictly the weaker layer: it is
|
|
1431
|
+
* only reachable when resolution fails, and it is the one part of this
|
|
1432
|
+
* file that a non-Windows CI run cannot prove.
|
|
1433
|
+
*/
|
|
1434
|
+
|
|
1435
|
+
'use strict';
|
|
1436
|
+
|
|
1437
|
+
const { spawnSync } = require('child_process');
|
|
1438
|
+
const fs = require('fs');
|
|
1439
|
+
const path = require('path');
|
|
1440
|
+
|
|
1441
|
+
function done() { process.exit(0); }
|
|
1442
|
+
|
|
1443
|
+
/** Case-insensitive env lookup — Windows env keys are not case-stable. */
|
|
1444
|
+
function envValue(env, name) {
|
|
1445
|
+
const key = Object.keys(env).find((c) => c.toLowerCase() === name.toLowerCase());
|
|
1446
|
+
return key ? env[key] : undefined;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Locate a command on PATH using fs only.
|
|
1451
|
+
*
|
|
1452
|
+
* Deliberately NOT \`execSync('where ...')\` / \`command -v\`: that spawns a
|
|
1453
|
+
* shell on every hook invocation, which is both the thing this file is
|
|
1454
|
+
* trying to get away from and a per-turn cost. Taking \`env\` and \`platform\`
|
|
1455
|
+
* as arguments is what lets the Windows branch be exercised from a
|
|
1456
|
+
* Linux/macOS CI run — see the Windows argv tests.
|
|
1457
|
+
*/
|
|
1458
|
+
function resolveCommandPath(command, env = process.env, platform = process.platform) {
|
|
1459
|
+
const hasSeparator = command.includes('/') || command.includes('\\\\');
|
|
1460
|
+
const dirs = hasSeparator
|
|
1461
|
+
? ['']
|
|
1462
|
+
: (envValue(env, 'PATH') || '').split(platform === 'win32' ? ';' : path.delimiter);
|
|
1463
|
+
const hasExtension = path.extname(command) !== '';
|
|
1464
|
+
const extensions = platform === 'win32' && !(hasSeparator && hasExtension)
|
|
1465
|
+
? (envValue(env, 'PATHEXT') || '.COM;.EXE;.BAT;.CMD').split(';')
|
|
1466
|
+
: [''];
|
|
1467
|
+
for (const dir of dirs) {
|
|
1468
|
+
for (const ext of extensions) {
|
|
1469
|
+
const base = path.resolve(dir || '.', command);
|
|
1470
|
+
const candidates = ext
|
|
1471
|
+
? [base + ext.toLowerCase(), base + ext.toUpperCase()]
|
|
1472
|
+
: [base];
|
|
1473
|
+
for (const file of candidates) {
|
|
1474
|
+
try {
|
|
1475
|
+
fs.accessSync(file, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
|
|
1476
|
+
if (fs.statSync(file).isFile()) return file;
|
|
1477
|
+
} catch { /* keep searching */ }
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Map an npm-generated Windows shim (ruflo.cmd / npx.cmd / …) to the .js
|
|
1486
|
+
* entrypoint it would have run, so it can be executed as \`node <entry>\`
|
|
1487
|
+
* with no shell.
|
|
1488
|
+
*
|
|
1489
|
+
* Handles both npm layouts: a global prefix (\`<prefix>/ruflo.cmd\` beside
|
|
1490
|
+
* \`<prefix>/node_modules/ruflo\`) and a local one (\`node_modules/.bin/ruflo.cmd\`
|
|
1491
|
+
* beside \`node_modules/ruflo\`). \`npx\` lives in the \`npm\` package, hence the
|
|
1492
|
+
* command→package mapping rather than assuming they match.
|
|
1493
|
+
*
|
|
1494
|
+
* The entrypoint comes from the package's own \`bin\` field, never a guessed
|
|
1495
|
+
* filename, and is required to resolve inside the package directory — a
|
|
1496
|
+
* manifest pointing outside it is refused rather than followed.
|
|
1497
|
+
*/
|
|
1498
|
+
function resolveNpmShim(shimPath) {
|
|
1499
|
+
const command = path.basename(shimPath, path.extname(shimPath)).toLowerCase();
|
|
1500
|
+
const packageName = command === 'npx' ? 'npm' : command;
|
|
1501
|
+
if (!['ruflo', 'claude-flow', 'npm'].includes(packageName)) return null;
|
|
1502
|
+
try {
|
|
1503
|
+
const shimDir = path.dirname(shimPath);
|
|
1504
|
+
const packageDir = path.basename(shimDir).toLowerCase() === '.bin'
|
|
1505
|
+
? path.resolve(shimDir, '..', packageName)
|
|
1506
|
+
: path.resolve(shimDir, 'node_modules', packageName);
|
|
1507
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
|
|
1508
|
+
const declared = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.[command];
|
|
1509
|
+
if (typeof declared !== 'string') return null;
|
|
1510
|
+
const canonicalPackageDir = fs.realpathSync(packageDir);
|
|
1511
|
+
const canonicalEntry = fs.realpathSync(path.resolve(packageDir, declared));
|
|
1512
|
+
const relativeEntry = path.relative(canonicalPackageDir, canonicalEntry);
|
|
1513
|
+
if (relativeEntry.startsWith('..' + path.sep) || path.isAbsolute(relativeEntry)) return null;
|
|
1514
|
+
if (!fs.statSync(canonicalEntry).isFile()) return null;
|
|
1515
|
+
return { command: process.execPath, args: [canonicalEntry] };
|
|
1516
|
+
} catch { return null; }
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Decide how to run \`bin\` without a shell. Returns {command, args}, or null
|
|
1521
|
+
* when no shell-free invocation could be identified (Windows shim that is
|
|
1522
|
+
* not an npm package entry) — the caller then falls back to the escaped
|
|
1523
|
+
* cmd.exe path rather than dropping the hook.
|
|
1524
|
+
*/
|
|
1525
|
+
function resolveInvocation(bin, binArgs, options = {}) {
|
|
1526
|
+
const env = options.env || process.env;
|
|
1527
|
+
const platform = options.platform || process.platform;
|
|
1528
|
+
const commandPath = resolveCommandPath(bin, env, platform);
|
|
1529
|
+
if (!commandPath) return null;
|
|
1530
|
+
if (platform === 'win32' && /\\.(?:cmd|bat|ps1)$/i.test(commandPath)) {
|
|
1531
|
+
const npmBin = resolveNpmShim(commandPath);
|
|
1532
|
+
return npmBin ? { command: npmBin.command, args: [...npmBin.args, ...binArgs] } : null;
|
|
1533
|
+
}
|
|
1534
|
+
return { command: commandPath, args: binArgs };
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
/**
|
|
1538
|
+
* Escape one argv element so it survives BOTH parsers a Windows shell:true
|
|
1539
|
+
* spawn puts it through before the target CLI ever sees it:
|
|
1540
|
+
* 1. cmd.exe's own line tokenizer, which still scans for & | < > ^ % ! " ( )
|
|
1541
|
+
* even inside a per-argument quoted segment — quoting alone does not
|
|
1542
|
+
* shield cmd.exe metacharacters, and this runs a SECOND time when the
|
|
1543
|
+
* resolved binary is itself a .cmd shim (npm's \`ruflo\`/\`claude-flow\`/
|
|
1544
|
+
* \`npx\` global installs on Windows), because launching a .cmd file is
|
|
1545
|
+
* cmd.exe re-invoking itself on the command line.
|
|
1546
|
+
* 2. The eventual CommandLineToArgvW argv parse in the target process,
|
|
1547
|
+
* which needs backslash-before-quote sequences doubled and the value
|
|
1548
|
+
* quoted so it lands as ONE argument.
|
|
1549
|
+
* Without this, a hook-derived value (e.g. a Bash tool's \`command\`, or a
|
|
1550
|
+
* file path) containing a shell metacharacter can be reinterpreted as a
|
|
1551
|
+
* separate command / redirection instead of reaching the CLI as literal
|
|
1552
|
+
* data — this is the class of bug in CVE-2024-27980 (Node's own .bat/.cmd
|
|
1553
|
+
* argument-injection advisory). Algorithm: https://qntm.org/cmd, the same
|
|
1554
|
+
* reference the \`cross-spawn\` package's Windows escaping is built from.
|
|
1555
|
+
*
|
|
1556
|
+
* Byte-identical to plugins/ruflo-core/scripts/ruflo-hook.cjs so the four
|
|
1557
|
+
* copies can be diffed against each other. This is the fallback, not the
|
|
1558
|
+
* primary defence: resolveInvocation() above is preferred because it removes
|
|
1559
|
+
* cmd.exe from the chain entirely rather than out-guessing its tokenizer.
|
|
1560
|
+
*/
|
|
1561
|
+
function escapeCmdArg(arg) {
|
|
1562
|
+
let s = String(arg);
|
|
1563
|
+
s = s.replace(/(\\\\*)"/g, '$1$1\\\\"').replace(/(\\\\*)$/, '$1$1');
|
|
1564
|
+
s = \`"\${s}"\`;
|
|
1565
|
+
return s.replace(/[()%!^"<>&|;,]/g, '^$&');
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
function invokeHook(bin, binArgs, hookArgs, stdinData, options = {}) {
|
|
1569
|
+
const env = options.env || process.env;
|
|
1570
|
+
const platform = options.platform || process.platform;
|
|
1571
|
+
const spawnOpts = {
|
|
1572
|
+
input: stdinData || '',
|
|
1573
|
+
encoding: 'utf8',
|
|
1574
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
1575
|
+
timeout: 30_000,
|
|
1576
|
+
env,
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
// Layer 1: no shell. CreateProcess/execve receives the argv array
|
|
1580
|
+
// verbatim, so nothing in it can be reinterpreted as syntax.
|
|
1581
|
+
const invocation = resolveInvocation(bin, binArgs, { env, platform });
|
|
1582
|
+
if (invocation) {
|
|
1583
|
+
const result = spawnSync(invocation.command, [...invocation.args, ...hookArgs], {
|
|
1584
|
+
...spawnOpts,
|
|
1585
|
+
shell: false,
|
|
1586
|
+
});
|
|
1587
|
+
return result.status === 0;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// Layer 2: Windows shim we could not map to an entrypoint. cmd.exe is
|
|
1591
|
+
// unavoidable here (CreateProcess cannot launch a .cmd, and Node has
|
|
1592
|
+
// refused to since CVE-2024-27980), so every element is escaped. Losing
|
|
1593
|
+
// the hook entirely would be the wrong trade — telemetry is best-effort,
|
|
1594
|
+
// but silently doing nothing hides breakage.
|
|
1595
|
+
const useShell = platform === 'win32';
|
|
1596
|
+
const args = [...binArgs, ...hookArgs];
|
|
1597
|
+
const result = spawnSync(
|
|
1598
|
+
useShell ? escapeCmdArg(bin) : bin,
|
|
1599
|
+
useShell ? args.map(escapeCmdArg) : args,
|
|
1600
|
+
{ ...spawnOpts, shell: useShell },
|
|
1601
|
+
);
|
|
1602
|
+
return result.status === 0;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
function main() {
|
|
1606
|
+
const args = process.argv.slice(2);
|
|
1607
|
+
if (args.length === 0) done();
|
|
1608
|
+
|
|
1609
|
+
const [subcommand, ...rest] = args;
|
|
1610
|
+
|
|
1611
|
+
let stdinData = '';
|
|
1612
|
+
try { stdinData = fs.readFileSync(0, 'utf8'); } catch { stdinData = ''; }
|
|
1613
|
+
|
|
1614
|
+
const hookArgs = ['hooks', subcommand, ...rest];
|
|
1615
|
+
|
|
1616
|
+
// Presence is checked separately from invocation strategy: a command that
|
|
1617
|
+
// exists but cannot be resolved to an entrypoint still runs, via layer 2.
|
|
1618
|
+
if (resolveCommandPath('ruflo')) { invokeHook('ruflo', [], hookArgs, stdinData); done(); }
|
|
1619
|
+
if (resolveCommandPath('claude-flow')) { invokeHook('claude-flow', [], hookArgs, stdinData); done(); }
|
|
1620
|
+
invokeHook('npx', ['--prefer-offline', '--yes', 'ruflo@latest'], hookArgs, stdinData);
|
|
1621
|
+
done();
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Test seam: the Windows argv suite require()s this file to drive resolveInvocation()
|
|
1625
|
+
// and invokeHook() with a simulated { platform: 'win32', env } — which is how
|
|
1626
|
+
// the Windows branch is proved from a Linux/macOS CI run. hooks.json always
|
|
1627
|
+
// invokes this file directly, so main() runs unconditionally otherwise.
|
|
1628
|
+
if (!globalThis.__RUFLO_HOOK_IMPORT_ONLY__) main();
|
|
1629
|
+
|
|
1630
|
+
module.exports = { invokeHook, resolveCommandPath, resolveInvocation, resolveNpmShim, escapeCmdArg };
|
|
1465
1631
|
`;
|
|
1466
1632
|
}
|
|
1467
1633
|
//# sourceMappingURL=helpers-generator.js.map
|