agileflow 4.0.0-alpha.2 → 4.0.0-alpha.21
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/CHANGELOG.md +51 -0
- package/content/plugins/accessibility/plugin.yaml +14 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/SKILL.md +392 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/references/aria-patterns.md +528 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/references/testing-checklist.md +457 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/references/wcag-guide.md +683 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/workflows/audit-page.md +310 -0
- package/content/plugins/accessibility/skills/agileflow-accessibility/workflows/implement-accessible-component.md +479 -0
- package/content/plugins/ads/agents/ads-audit-budget.md +185 -0
- package/content/plugins/ads/agents/ads-audit-compliance.md +171 -0
- package/content/plugins/ads/agents/ads-audit-creative.md +168 -0
- package/content/plugins/ads/agents/ads-audit-google.md +227 -0
- package/content/plugins/ads/agents/ads-audit-meta.md +184 -0
- package/content/plugins/ads/agents/ads-audit-tracking.md +205 -0
- package/content/plugins/ads/agents/ads-consensus.md +410 -0
- package/content/plugins/ads/agents/ads-generate.md +152 -0
- package/content/plugins/ads/agents/ads-performance-tracker.md +212 -0
- package/content/plugins/ads/plugin.yaml +23 -4
- package/content/plugins/ads/skills/agileflow-ads/SKILL.md +218 -0
- package/content/plugins/ads/skills/agileflow-ads/references/ad-copy-formula-guide.md +131 -0
- package/content/plugins/ads/skills/agileflow-ads/references/audience-targeting-guide.md +137 -0
- package/content/plugins/ads/skills/agileflow-ads/references/bid-strategy-guide.md +115 -0
- package/content/plugins/ads/skills/agileflow-ads/references/platform-benchmarks.md +100 -0
- package/content/plugins/ads/skills/agileflow-ads/workflows/audit.md +118 -0
- package/content/plugins/ads/skills/agileflow-ads/workflows/generate.md +84 -0
- package/content/plugins/audit/agents/a11y-analyzer-aria.md +173 -0
- package/content/plugins/audit/agents/a11y-analyzer-forms.md +173 -0
- package/content/plugins/audit/agents/a11y-analyzer-keyboard.md +183 -0
- package/content/plugins/audit/agents/a11y-analyzer-semantic.md +169 -0
- package/content/plugins/audit/agents/a11y-analyzer-visual.md +172 -0
- package/content/plugins/audit/agents/a11y-consensus.md +249 -0
- package/content/plugins/audit/agents/accessibility.md +558 -0
- package/content/plugins/audit/agents/api-quality-analyzer-conventions.md +156 -0
- package/content/plugins/audit/agents/api-quality-analyzer-docs.md +184 -0
- package/content/plugins/audit/agents/api-quality-analyzer-errors.md +191 -0
- package/content/plugins/audit/agents/api-quality-analyzer-pagination.md +179 -0
- package/content/plugins/audit/agents/api-quality-analyzer-versioning.md +150 -0
- package/content/plugins/audit/agents/api-quality-consensus.md +217 -0
- package/content/plugins/audit/agents/api-validator.md +191 -0
- package/content/plugins/audit/agents/arch-analyzer-circular.md +156 -0
- package/content/plugins/audit/agents/arch-analyzer-complexity.md +193 -0
- package/content/plugins/audit/agents/arch-analyzer-coupling.md +152 -0
- package/content/plugins/audit/agents/arch-analyzer-layering.md +160 -0
- package/content/plugins/audit/agents/arch-analyzer-patterns.md +210 -0
- package/content/plugins/audit/agents/arch-consensus.md +228 -0
- package/content/plugins/audit/agents/browser-qa.md +342 -0
- package/content/plugins/audit/agents/code-reviewer.md +298 -0
- package/content/plugins/audit/agents/completeness-analyzer-api.md +199 -0
- package/content/plugins/audit/agents/completeness-analyzer-conditional.md +211 -0
- package/content/plugins/audit/agents/completeness-analyzer-handlers.md +166 -0
- package/content/plugins/audit/agents/completeness-analyzer-imports.md +165 -0
- package/content/plugins/audit/agents/completeness-analyzer-routes.md +190 -0
- package/content/plugins/audit/agents/completeness-analyzer-state.md +196 -0
- package/content/plugins/audit/agents/completeness-analyzer-stubs.md +206 -0
- package/content/plugins/audit/agents/completeness-consensus.md +295 -0
- package/content/plugins/audit/agents/error-analyzer.md +213 -0
- package/content/plugins/audit/agents/flow-analyzer-authorization.md +182 -0
- package/content/plugins/audit/agents/flow-analyzer-discovery.md +174 -0
- package/content/plugins/audit/agents/flow-analyzer-errors.md +186 -0
- package/content/plugins/audit/agents/flow-analyzer-feedback.md +185 -0
- package/content/plugins/audit/agents/flow-analyzer-navigation.md +177 -0
- package/content/plugins/audit/agents/flow-analyzer-persistence.md +193 -0
- package/content/plugins/audit/agents/flow-analyzer-wiring.md +169 -0
- package/content/plugins/audit/agents/flow-consensus.md +237 -0
- package/content/plugins/audit/agents/legal-analyzer-a11y.md +114 -0
- package/content/plugins/audit/agents/legal-analyzer-ai.md +121 -0
- package/content/plugins/audit/agents/legal-analyzer-consumer.md +114 -0
- package/content/plugins/audit/agents/legal-analyzer-content.md +117 -0
- package/content/plugins/audit/agents/legal-analyzer-international.md +119 -0
- package/content/plugins/audit/agents/legal-analyzer-licensing.md +119 -0
- package/content/plugins/audit/agents/legal-analyzer-privacy.md +112 -0
- package/content/plugins/audit/agents/legal-analyzer-security.md +116 -0
- package/content/plugins/audit/agents/legal-analyzer-terms.md +115 -0
- package/content/plugins/audit/agents/legal-consensus.md +250 -0
- package/content/plugins/audit/agents/logic-analyzer-edge.md +179 -0
- package/content/plugins/audit/agents/logic-analyzer-flow.md +264 -0
- package/content/plugins/audit/agents/logic-analyzer-invariant.md +215 -0
- package/content/plugins/audit/agents/logic-analyzer-race.md +280 -0
- package/content/plugins/audit/agents/logic-analyzer-type.md +227 -0
- package/content/plugins/audit/agents/logic-consensus.md +259 -0
- package/content/plugins/audit/agents/perf-analyzer-assets.md +182 -0
- package/content/plugins/audit/agents/perf-analyzer-bundle.md +173 -0
- package/content/plugins/audit/agents/perf-analyzer-caching.md +170 -0
- package/content/plugins/audit/agents/perf-analyzer-compute.md +173 -0
- package/content/plugins/audit/agents/perf-analyzer-memory.md +193 -0
- package/content/plugins/audit/agents/perf-analyzer-network.md +165 -0
- package/content/plugins/audit/agents/perf-analyzer-queries.md +162 -0
- package/content/plugins/audit/agents/perf-analyzer-rendering.md +168 -0
- package/content/plugins/audit/agents/perf-consensus.md +287 -0
- package/content/plugins/audit/agents/qa.md +820 -0
- package/content/plugins/audit/agents/quality-analyzer-comments.md +159 -0
- package/content/plugins/audit/agents/quality-analyzer-duplication.md +184 -0
- package/content/plugins/audit/agents/quality-analyzer-naming.md +160 -0
- package/content/plugins/audit/agents/quality-consensus.md +241 -0
- package/content/plugins/audit/agents/schema-validator.md +473 -0
- package/content/plugins/audit/agents/security-analyzer-api.md +210 -0
- package/content/plugins/audit/agents/security-analyzer-auth.md +169 -0
- package/content/plugins/audit/agents/security-analyzer-authz.md +180 -0
- package/content/plugins/audit/agents/security-analyzer-deps.md +153 -0
- package/content/plugins/audit/agents/security-analyzer-infra.md +184 -0
- package/content/plugins/audit/agents/security-analyzer-injection.md +155 -0
- package/content/plugins/audit/agents/security-analyzer-input.md +201 -0
- package/content/plugins/audit/agents/security-analyzer-secrets.md +183 -0
- package/content/plugins/audit/agents/security-consensus.md +283 -0
- package/content/plugins/audit/agents/test-analyzer-assertions.md +188 -0
- package/content/plugins/audit/agents/test-analyzer-coverage.md +189 -0
- package/content/plugins/audit/agents/test-analyzer-fragility.md +193 -0
- package/content/plugins/audit/agents/test-analyzer-integration.md +161 -0
- package/content/plugins/audit/agents/test-analyzer-maintenance.md +180 -0
- package/content/plugins/audit/agents/test-analyzer-mocking.md +188 -0
- package/content/plugins/audit/agents/test-analyzer-patterns.md +196 -0
- package/content/plugins/audit/agents/test-analyzer-structure.md +184 -0
- package/content/plugins/audit/agents/test-consensus.md +301 -0
- package/content/plugins/audit/agents/testing.md +561 -0
- package/content/plugins/audit/agents/ui-validator.md +344 -0
- package/content/plugins/audit/plugin.yaml +186 -5
- package/content/plugins/audit/skills/agileflow-audit/SKILL.md +113 -0
- package/content/plugins/audit/skills/agileflow-audit/references/audit-depth-guide.md +151 -0
- package/content/plugins/audit/skills/agileflow-audit/references/dependency-risk-guide.md +139 -0
- package/content/plugins/audit/skills/agileflow-audit/references/owasp-top10.md +120 -0
- package/content/plugins/audit/skills/agileflow-audit/references/performance-budget-guide.md +143 -0
- package/content/plugins/audit/skills/agileflow-audit/references/wcag-criteria.md +117 -0
- package/content/plugins/audit/skills/agileflow-audit/workflows/run-audit.md +52 -0
- package/content/plugins/audit/skills/agileflow-audit/workflows/tdd.md +66 -0
- package/content/plugins/core/agents/adr-writer.md +521 -0
- package/content/plugins/core/agents/epic-planner.md +520 -0
- package/content/plugins/core/agents/mentor.md +709 -0
- package/content/plugins/core/agents/orchestrator.md +776 -0
- package/content/plugins/core/agents/team-coordinator.md +334 -0
- package/content/plugins/core/agents/team-lead.md +181 -0
- package/content/plugins/core/agents/workspace-orchestrator.md +146 -0
- package/content/plugins/core/hooks/context-loader.js +31 -4
- package/content/plugins/core/hooks/damage-control-bash.js +10 -2
- package/content/plugins/core/hooks/damage-control-edit.js +4 -1
- package/content/plugins/core/hooks/damage-control-patterns.yaml +1 -1
- package/content/plugins/core/hooks/damage-control-write.js +4 -1
- package/content/plugins/core/hooks/{pre-compact-state.js → post-compact-state.js} +25 -8
- package/content/plugins/core/hooks/preferences-injector.js +352 -0
- package/content/plugins/core/plugin.yaml +24 -28
- package/content/plugins/core/skills/agileflow-adr/SKILL.md +34 -8
- package/content/plugins/core/skills/agileflow-adr/references/madr-format-guide.md +86 -0
- package/content/plugins/core/skills/agileflow-adr/workflows/write-adr.md +57 -0
- package/content/plugins/core/skills/agileflow-babysit-mentor/SKILL.md +94 -27
- package/content/plugins/core/skills/agileflow-babysit-mentor/references/mentor-decision-guide.md +81 -0
- package/content/plugins/core/skills/agileflow-babysit-mentor/workflows/mentor-session.md +79 -0
- package/content/plugins/core/skills/agileflow-epic-planner/SKILL.md +37 -7
- package/content/plugins/core/skills/agileflow-epic-planner/references/epic-sizing-guide.md +81 -0
- package/content/plugins/core/skills/agileflow-epic-planner/workflows/plan-epic.md +55 -0
- package/content/plugins/core/skills/agileflow-status-updater/SKILL.md +36 -20
- package/content/plugins/core/skills/agileflow-status-updater/references/status-transitions.md +89 -0
- package/content/plugins/core/skills/agileflow-status-updater/workflows/update-status.md +56 -0
- package/content/plugins/core/skills/agileflow-story-writer/SKILL.md +39 -114
- package/content/plugins/core/skills/agileflow-story-writer/references/estimation-reference.md +36 -0
- package/content/plugins/core/skills/agileflow-story-writer/references/story-template.md +92 -0
- package/content/plugins/core/skills/agileflow-story-writer/workflows/write-story.md +138 -0
- package/content/plugins/council/agents/council-advocate.md +223 -0
- package/content/plugins/council/agents/council-analyst.md +278 -0
- package/content/plugins/council/agents/council-compounder.md +204 -0
- package/content/plugins/council/agents/council-contrarian.md +217 -0
- package/content/plugins/council/agents/council-moonshot.md +217 -0
- package/content/plugins/council/agents/council-optimist.md +185 -0
- package/content/plugins/council/agents/council-revenue.md +200 -0
- package/content/plugins/council/agents/council-technical.md +218 -0
- package/content/plugins/council/agents/multi-expert.md +334 -0
- package/content/plugins/council/plugin.yaml +23 -4
- package/content/plugins/council/skills/agileflow-council/SKILL.md +102 -0
- package/content/plugins/council/skills/agileflow-council/references/decision-log-template.md +109 -0
- package/content/plugins/council/skills/agileflow-council/references/perspective-guide.md +104 -0
- package/content/plugins/council/skills/agileflow-council/references/when-to-convene-guide.md +112 -0
- package/content/plugins/council/skills/agileflow-council/workflows/convene.md +73 -0
- package/content/plugins/council/skills/agileflow-council/workflows/multi-expert.md +75 -0
- package/content/plugins/database/plugin.yaml +14 -0
- package/content/plugins/database/skills/agileflow-database/SKILL.md +284 -0
- package/content/plugins/database/skills/agileflow-database/references/indexing-guide.md +313 -0
- package/content/plugins/database/skills/agileflow-database/references/migration-guide.md +328 -0
- package/content/plugins/database/skills/agileflow-database/references/schema-design-guide.md +467 -0
- package/content/plugins/database/skills/agileflow-database/workflows/design-schema.md +213 -0
- package/content/plugins/database/skills/agileflow-database/workflows/optimize-query.md +253 -0
- package/content/plugins/debugging/plugin.yaml +14 -0
- package/content/plugins/debugging/skills/agileflow-debug/SKILL.md +236 -0
- package/content/plugins/debugging/skills/agileflow-debug/references/common-patterns.md +350 -0
- package/content/plugins/debugging/skills/agileflow-debug/references/debugging-strategies.md +328 -0
- package/content/plugins/debugging/skills/agileflow-debug/workflows/debug-issue.md +187 -0
- package/content/plugins/debugging/skills/agileflow-debug/workflows/reproduce-bug.md +194 -0
- package/content/plugins/delivery/agents/ci.md +547 -0
- package/content/plugins/delivery/agents/devops.md +789 -0
- package/content/plugins/delivery/plugin.yaml +19 -0
- package/content/plugins/delivery/skills/agileflow-delivery/SKILL.md +111 -0
- package/content/plugins/delivery/skills/agileflow-delivery/references/changelog-format-guide.md +133 -0
- package/content/plugins/delivery/skills/agileflow-delivery/references/ci-pipeline-guide.md +158 -0
- package/content/plugins/delivery/skills/agileflow-delivery/references/pr-checklist-guide.md +133 -0
- package/content/plugins/delivery/skills/agileflow-delivery/references/release-checklist.md +142 -0
- package/content/plugins/delivery/skills/agileflow-delivery/workflows/changelog.md +72 -0
- package/content/plugins/delivery/skills/agileflow-delivery/workflows/deploy.md +74 -0
- package/content/plugins/delivery/skills/agileflow-delivery/workflows/pr.md +75 -0
- package/content/plugins/docs/agents/documentation.md +544 -0
- package/content/plugins/docs/agents/readme-updater.md +640 -0
- package/content/plugins/docs/plugin.yaml +19 -0
- package/content/plugins/docs/skills/agileflow-docs/SKILL.md +106 -0
- package/content/plugins/docs/skills/agileflow-docs/references/api-doc-template.md +167 -0
- package/content/plugins/docs/skills/agileflow-docs/references/doc-types-guide.md +141 -0
- package/content/plugins/docs/skills/agileflow-docs/references/readme-template.md +156 -0
- package/content/plugins/docs/skills/agileflow-docs/workflows/readme-sync.md +57 -0
- package/content/plugins/docs/skills/agileflow-docs/workflows/sync.md +64 -0
- package/content/plugins/engineering/agents/api.md +718 -0
- package/content/plugins/engineering/agents/codebase-query.md +285 -0
- package/content/plugins/engineering/agents/compliance.md +559 -0
- package/content/plugins/engineering/agents/database.md +644 -0
- package/content/plugins/engineering/agents/integrations.md +644 -0
- package/content/plugins/engineering/agents/mobile.md +552 -0
- package/content/plugins/engineering/agents/monitoring.md +585 -0
- package/content/plugins/engineering/agents/performance.md +529 -0
- package/content/plugins/engineering/agents/refactor.md +592 -0
- package/content/plugins/engineering/agents/security.md +524 -0
- package/content/plugins/engineering/agents/ui.md +1336 -0
- package/content/plugins/engineering/plugin.yaml +37 -0
- package/content/plugins/engineering/skills/agileflow-engineering/SKILL.md +127 -0
- package/content/plugins/engineering/skills/agileflow-engineering/references/code-review-guide.md +126 -0
- package/content/plugins/engineering/skills/agileflow-engineering/references/domain-routing-guide.md +89 -0
- package/content/plugins/engineering/skills/agileflow-engineering/references/refactoring-guide.md +136 -0
- package/content/plugins/engineering/skills/agileflow-engineering/workflows/diagnose.md +63 -0
- package/content/plugins/engineering/skills/agileflow-engineering/workflows/impact.md +60 -0
- package/content/plugins/ideation/agents/brainstorm-analyzer-features.md +179 -0
- package/content/plugins/ideation/agents/brainstorm-analyzer-growth.md +169 -0
- package/content/plugins/ideation/agents/brainstorm-analyzer-integration.md +181 -0
- package/content/plugins/ideation/agents/brainstorm-analyzer-market.md +150 -0
- package/content/plugins/ideation/agents/brainstorm-analyzer-ux.md +180 -0
- package/content/plugins/ideation/agents/brainstorm-consensus.md +245 -0
- package/content/plugins/ideation/agents/design.md +568 -0
- package/content/plugins/ideation/agents/product.md +582 -0
- package/content/plugins/ideation/plugin.yaml +31 -0
- package/content/plugins/ideation/skills/agileflow-ideation/SKILL.md +109 -0
- package/content/plugins/ideation/skills/agileflow-ideation/references/brainstorm-techniques.md +138 -0
- package/content/plugins/ideation/skills/agileflow-ideation/references/competitive-analysis-template.md +148 -0
- package/content/plugins/ideation/skills/agileflow-ideation/references/feature-prioritization-guide.md +147 -0
- package/content/plugins/ideation/skills/agileflow-ideation/references/user-story-patterns.md +152 -0
- package/content/plugins/ideation/skills/agileflow-ideation/workflows/features.md +65 -0
- package/content/plugins/ideation/skills/agileflow-ideation/workflows/ideate.md +54 -0
- package/content/plugins/migration/agents/datamigration.md +757 -0
- package/content/plugins/migration/plugin.yaml +17 -0
- package/content/plugins/migration/skills/agileflow-migration/SKILL.md +106 -0
- package/content/plugins/migration/skills/agileflow-migration/references/data-validation-checklist.md +154 -0
- package/content/plugins/migration/skills/agileflow-migration/references/migration-patterns.md +209 -0
- package/content/plugins/migration/skills/agileflow-migration/references/rollback-playbook.md +171 -0
- package/content/plugins/migration/skills/agileflow-migration/references/version-compatibility-matrix.md +155 -0
- package/content/plugins/migration/skills/agileflow-migration/workflows/plan.md +73 -0
- package/content/plugins/migration/skills/agileflow-migration/workflows/validate.md +71 -0
- package/content/plugins/performance/plugin.yaml +14 -0
- package/content/plugins/performance/skills/agileflow-performance/SKILL.md +224 -0
- package/content/plugins/performance/skills/agileflow-performance/references/optimization-patterns.md +554 -0
- package/content/plugins/performance/skills/agileflow-performance/references/profiling-guide.md +383 -0
- package/content/plugins/performance/skills/agileflow-performance/references/web-vitals-guide.md +360 -0
- package/content/plugins/performance/skills/agileflow-performance/workflows/improve-web-vitals.md +344 -0
- package/content/plugins/performance/skills/agileflow-performance/workflows/profile-and-fix.md +254 -0
- package/content/plugins/planning/agents/analytics.md +670 -0
- package/content/plugins/planning/agents/rlm-subcore.md +215 -0
- package/content/plugins/planning/plugin.yaml +19 -0
- package/content/plugins/planning/skills/agileflow-planning/SKILL.md +111 -0
- package/content/plugins/planning/skills/agileflow-planning/references/estimation-guide.md +114 -0
- package/content/plugins/planning/skills/agileflow-planning/references/rpi-workflow.md +119 -0
- package/content/plugins/planning/skills/agileflow-planning/references/sprint-planning-guide.md +145 -0
- package/content/plugins/planning/skills/agileflow-planning/workflows/impact.md +63 -0
- package/content/plugins/planning/skills/agileflow-planning/workflows/rpi.md +104 -0
- package/content/plugins/psychology/plugin.yaml +14 -0
- package/content/plugins/psychology/skills/agileflow-retention/SKILL.md +252 -0
- package/content/plugins/psychology/skills/agileflow-retention/references/competitor-analysis.md +240 -0
- package/content/plugins/psychology/skills/agileflow-retention/references/psychology-models.md +349 -0
- package/content/plugins/psychology/skills/agileflow-retention/references/retention-patterns.md +279 -0
- package/content/plugins/psychology/skills/agileflow-retention/workflows/design-retention-feature.md +287 -0
- package/content/plugins/psychology/skills/agileflow-retention/workflows/retention-audit.md +259 -0
- package/content/plugins/refactoring/plugin.yaml +14 -0
- package/content/plugins/refactoring/skills/agileflow-refactor/SKILL.md +235 -0
- package/content/plugins/refactoring/skills/agileflow-refactor/references/refactoring-patterns.md +405 -0
- package/content/plugins/refactoring/skills/agileflow-refactor/references/safety-checks.md +177 -0
- package/content/plugins/refactoring/skills/agileflow-refactor/workflows/extract-module.md +226 -0
- package/content/plugins/refactoring/skills/agileflow-refactor/workflows/safe-refactor.md +169 -0
- package/content/plugins/research/agents/research.md +503 -0
- package/content/plugins/research/plugin.yaml +17 -0
- package/content/plugins/research/skills/agileflow-research/SKILL.md +110 -0
- package/content/plugins/research/skills/agileflow-research/references/knowledge-decay-guide.md +121 -0
- package/content/plugins/research/skills/agileflow-research/references/research-prompt-guide.md +141 -0
- package/content/plugins/research/skills/agileflow-research/references/synthesis-template.md +154 -0
- package/content/plugins/research/skills/agileflow-research/workflows/analyze.md +60 -0
- package/content/plugins/research/skills/agileflow-research/workflows/ask.md +64 -0
- package/content/plugins/research/skills/agileflow-research/workflows/import.md +66 -0
- package/content/plugins/research/skills/agileflow-research/workflows/synthesize.md +66 -0
- package/content/plugins/reviews/plugin.yaml +14 -0
- package/content/plugins/reviews/skills/agileflow-pr-reviewer/SKILL.md +241 -0
- package/content/plugins/reviews/skills/agileflow-pr-reviewer/references/review-checklist.md +200 -0
- package/content/plugins/reviews/skills/agileflow-pr-reviewer/references/security-patterns.md +328 -0
- package/content/plugins/reviews/skills/agileflow-pr-reviewer/workflows/review-pr.md +153 -0
- package/content/plugins/reviews/skills/agileflow-pr-reviewer/workflows/security-review.md +177 -0
- package/content/plugins/seo/agents/seo-analyzer-content.md +169 -0
- package/content/plugins/seo/agents/seo-analyzer-images.md +198 -0
- package/content/plugins/seo/agents/seo-analyzer-performance.md +217 -0
- package/content/plugins/seo/agents/seo-analyzer-schema.md +184 -0
- package/content/plugins/seo/agents/seo-analyzer-sitemap.md +177 -0
- package/content/plugins/seo/agents/seo-analyzer-technical.md +151 -0
- package/content/plugins/seo/agents/seo-consensus.md +304 -0
- package/content/plugins/seo/plugin.yaml +19 -4
- package/content/plugins/seo/skills/agileflow-seo/SKILL.md +188 -0
- package/content/plugins/seo/skills/agileflow-seo/references/cwv-thresholds.md +110 -0
- package/content/plugins/seo/skills/agileflow-seo/references/eeat-framework.md +144 -0
- package/content/plugins/seo/skills/agileflow-seo/references/keyword-research-guide.md +125 -0
- package/content/plugins/seo/skills/agileflow-seo/references/schema-types.md +139 -0
- package/content/plugins/seo/skills/agileflow-seo/references/technical-seo-checklist.md +139 -0
- package/content/plugins/seo/skills/agileflow-seo/workflows/audit.md +98 -0
- package/content/plugins/seo/skills/agileflow-seo/workflows/page.md +118 -0
- package/content/plugins/testing/plugin.yaml +16 -0
- package/content/plugins/testing/skills/agileflow-test-writer/SKILL.md +260 -0
- package/content/plugins/testing/skills/agileflow-test-writer/references/coverage-targets.md +239 -0
- package/content/plugins/testing/skills/agileflow-test-writer/references/test-patterns.md +420 -0
- package/content/plugins/testing/skills/agileflow-test-writer/workflows/add-coverage.md +154 -0
- package/content/plugins/testing/skills/agileflow-test-writer/workflows/write-tests-from-ac.md +225 -0
- package/package.json +2 -2
- package/src/cli/commands/doctor.js +818 -30
- package/src/cli/commands/hook.js +17 -14
- package/src/cli/commands/launch.js +1454 -0
- package/src/cli/commands/learn.js +149 -0
- package/src/cli/commands/plugins.js +113 -0
- package/src/cli/commands/setup.js +455 -110
- package/src/cli/commands/skills.js +324 -0
- package/src/cli/commands/status.js +8 -10
- package/src/cli/commands/update.js +76 -15
- package/src/cli/index.js +90 -26
- package/src/cli/wizard/babysit-mode-picker.js +192 -0
- package/src/cli/wizard/behaviors-picker.js +208 -54
- package/src/cli/wizard/ide-picker.js +40 -28
- package/src/cli/wizard/install-scope-picker.js +57 -0
- package/src/cli/wizard/launch-alias-picker.js +50 -0
- package/src/cli/wizard/launch-cli-picker.js +129 -0
- package/src/cli/wizard/launch-tmux-picker.js +133 -0
- package/src/cli/wizard/learnings-picker.js +40 -0
- package/src/cli/wizard/plugin-picker.js +47 -16
- package/src/lib/brand.js +116 -0
- package/src/lib/errors.js +120 -0
- package/src/lib/path-check.js +39 -0
- package/src/runtime/config/defaults.js +22 -17
- package/src/runtime/config/loader.js +77 -8
- package/src/runtime/config/schema.json +43 -16
- package/src/runtime/config/writer.js +3 -1
- package/src/runtime/ide/babysit-skill.js +202 -0
- package/src/runtime/ide/capabilities.js +84 -29
- package/src/runtime/ide/claude-code-content.js +177 -0
- package/src/runtime/ide/claude-code-settings.js +67 -29
- package/src/runtime/ide/claude-code-skills.js +47 -32
- package/src/runtime/ide/codex-config.js +295 -0
- package/src/runtime/installer/install.js +252 -24
- package/src/runtime/launch/alias-installer.js +191 -0
- package/src/runtime/launch/cli-resume.js +244 -0
- package/src/runtime/launch/closed-windows.js +338 -0
- package/src/runtime/launch/defaults.js +66 -0
- package/src/runtime/launch/detect-clis.js +69 -0
- package/src/runtime/launch/doctor.js +464 -0
- package/src/runtime/launch/exec-wrapper.js +114 -0
- package/src/runtime/launch/parallel-session.js +247 -0
- package/src/runtime/launch/prefs.js +211 -0
- package/src/runtime/launch/project-prefs.js +234 -0
- package/src/runtime/launch/resolve-cli.js +56 -0
- package/src/runtime/launch/restore.js +152 -0
- package/src/runtime/launch/schema.json +75 -0
- package/src/runtime/launch/session-lifecycle.js +313 -0
- package/src/runtime/launch/session-registry.js +401 -0
- package/src/runtime/launch/spawn.js +103 -0
- package/src/runtime/launch/tabs.js +350 -0
- package/src/runtime/launch/tmux.js +764 -0
- package/src/runtime/launch/worktree.js +260 -0
- package/src/runtime/plugins/registry.js +16 -11
- package/src/runtime/plugins/validator.js +57 -43
- package/src/runtime/skills/learnings.js +308 -0
- package/content/plugins/core/hooks/babysit-mentor-injector.js +0 -55
- package/src/cli/wizard/personalization.js +0 -64
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tmux session orchestration for `agileflow launch`.
|
|
3
|
+
*
|
|
4
|
+
* Slice 2b: wrap the user's AI CLI in a per-cwd tmux session so
|
|
5
|
+
* detach/reattach work like the v3 `af` script. Multiple `agileflow
|
|
6
|
+
* launch` invocations from the same directory either reattach to an
|
|
7
|
+
* existing detached session, or — if one is already attached — spawn
|
|
8
|
+
* a numbered sibling (`<cli>-<dir>`, `<cli>-<dir>-2`, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Everything that shells out to tmux goes through the `runner` parameter
|
|
11
|
+
* (default: child_process), so unit tests can drive every code path
|
|
12
|
+
* without a real tmux daemon. The orchestrator also threads the
|
|
13
|
+
* preferred status-bar position from launch-prefs.json onto the session
|
|
14
|
+
* after it's created.
|
|
15
|
+
*
|
|
16
|
+
* What this module deliberately does NOT do (deferred):
|
|
17
|
+
* - keybind preset files (.tmux.conf snippets for default/minimal/none)
|
|
18
|
+
* - freeze recovery, kill, list subcommands
|
|
19
|
+
* - multi-pane / worktree session creation (Alt+N etc.)
|
|
20
|
+
*/
|
|
21
|
+
const path = require("path");
|
|
22
|
+
const child_process = require("child_process");
|
|
23
|
+
|
|
24
|
+
const { commandExists: realCommandExists } = require("../../lib/path-check.js");
|
|
25
|
+
const { signalToExitCode } = require("./spawn.js");
|
|
26
|
+
const { resolveAgileflowBin } = require("./alias-installer.js");
|
|
27
|
+
const tabs = require("./tabs.js");
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} TmuxLaunchResult
|
|
31
|
+
* @property {number} exitCode - exit code of the attach (or session creation on failure)
|
|
32
|
+
* @property {NodeJS.Signals | null} [signal] - signal that terminated the attach, when applicable
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {Object} TmuxRunSyncResult
|
|
37
|
+
* @property {number} status - subprocess exit status (1 when spawn itself failed)
|
|
38
|
+
* @property {string} stdout
|
|
39
|
+
* @property {string} stderr
|
|
40
|
+
* @property {Error | null} error - non-null when spawn ITSELF failed (e.g., ENOENT)
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} TmuxRunner
|
|
45
|
+
* @property {(args: string[]) => TmuxRunSyncResult} runSync - non-interactive tmux commands
|
|
46
|
+
* @property {(args: string[]) => Promise<{ exitCode: number, signal: NodeJS.Signals | null }>} runAttach - interactive `tmux attach` (foreground, stdio inherited)
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Return true when the current process is already inside a tmux client.
|
|
51
|
+
* tmux exports `$TMUX` containing the socket path + session id; we treat
|
|
52
|
+
* any non-empty value as "inside". Callers should NOT nest sessions.
|
|
53
|
+
*
|
|
54
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
function isInsideTmux(env = process.env) {
|
|
58
|
+
return typeof env.TMUX === "string" && env.TMUX.length > 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether `tmux` resolves on PATH. Injectable for tests.
|
|
63
|
+
*
|
|
64
|
+
* @param {(name: string) => boolean} [exists]
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
function tmuxAvailable(exists = realCommandExists) {
|
|
68
|
+
return exists("tmux");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Derive a tmux session name from the AI CLI id and the current working
|
|
73
|
+
* directory. tmux session names can't contain `:` or `.`; we also strip
|
|
74
|
+
* other shell-noisy characters so names stay tab-completable.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} cli - e.g. "claude" / "codex"
|
|
77
|
+
* @param {string} cwd - absolute path; the basename is what's used
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
function baseSessionName(cli, cwd) {
|
|
81
|
+
const dir = path.basename(cwd).replace(/[^A-Za-z0-9_-]+/g, "_") || "root";
|
|
82
|
+
return `${cli}-${dir}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Walk `<base>`, `<base>-2`, `<base>-3`, ... until one is unused (per
|
|
87
|
+
* `exists()`) — used to spawn a parallel session when the canonical name
|
|
88
|
+
* is already attached. Caller decides whether to attach to an existing
|
|
89
|
+
* detached session or always create new.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} base
|
|
92
|
+
* @param {(name: string) => boolean} exists
|
|
93
|
+
* @param {number} [max] - safety bound; defaults to 32
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function nextFreeSessionName(base, exists, max = 32) {
|
|
97
|
+
if (!exists(base)) return base;
|
|
98
|
+
for (let i = 2; i <= max; i++) {
|
|
99
|
+
const candidate = `${base}-${i}`;
|
|
100
|
+
if (!exists(candidate)) return candidate;
|
|
101
|
+
}
|
|
102
|
+
// Fall back to base — caller will see a tmux "duplicate session" error
|
|
103
|
+
// rather than us silently picking a 33rd session. Surfacing the failure
|
|
104
|
+
// is better than masking it.
|
|
105
|
+
return base;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @returns {TmuxRunner}
|
|
110
|
+
*/
|
|
111
|
+
function defaultRunner() {
|
|
112
|
+
return {
|
|
113
|
+
runSync(args) {
|
|
114
|
+
const result = child_process.spawnSync("tmux", args, {
|
|
115
|
+
encoding: "utf8",
|
|
116
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
// spawnSync returns `status: null` when the spawn itself failed
|
|
120
|
+
// (ENOENT etc.). In that case `error` is set; callers must check it
|
|
121
|
+
// before treating status=1 as a real tmux exit code.
|
|
122
|
+
status: typeof result.status === "number" ? result.status : 1,
|
|
123
|
+
stdout: result.stdout || "",
|
|
124
|
+
stderr: result.stderr || "",
|
|
125
|
+
error: result.error || null,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
runAttach(args) {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
let settled = false;
|
|
131
|
+
const finish = (fn, value) => {
|
|
132
|
+
if (settled) return;
|
|
133
|
+
settled = true;
|
|
134
|
+
fn(value);
|
|
135
|
+
};
|
|
136
|
+
let child;
|
|
137
|
+
try {
|
|
138
|
+
child = child_process.spawn("tmux", args, { stdio: "inherit" });
|
|
139
|
+
} catch (err) {
|
|
140
|
+
finish(reject, err);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
child.on("error", (err) => finish(reject, err));
|
|
144
|
+
child.on("close", (code, signal) => {
|
|
145
|
+
// Mirror spawn.js's signal-to-exit-code table so shell scripts
|
|
146
|
+
// can distinguish SIGINT (130) from SIGTERM (143) etc., instead
|
|
147
|
+
// of all signal exits collapsing to 128.
|
|
148
|
+
const exitCode =
|
|
149
|
+
typeof code === "number" ? code : signalToExitCode(signal);
|
|
150
|
+
finish(resolve, { exitCode, signal: signal || null });
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {string} name
|
|
159
|
+
* @param {TmuxRunner} runner
|
|
160
|
+
* @returns {boolean}
|
|
161
|
+
*/
|
|
162
|
+
function sessionExists(name, runner) {
|
|
163
|
+
const result = runner.runSync(["has-session", "-t", `=${name}`]);
|
|
164
|
+
return result.status === 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Detect the running tmux's version via `tmux -V`. Returns null when
|
|
169
|
+
* tmux isn't on PATH or its output is unparseable. Pure (no caching);
|
|
170
|
+
* `launchInTmux` calls it once per invocation and threads the result.
|
|
171
|
+
*
|
|
172
|
+
* @param {TmuxRunner} runner
|
|
173
|
+
* @returns {{ major: number, minor: number } | null}
|
|
174
|
+
*/
|
|
175
|
+
function detectTmuxVersion(runner) {
|
|
176
|
+
const result = runner.runSync(["-V"]);
|
|
177
|
+
if (result.status !== 0) return null;
|
|
178
|
+
return tabs.parseTmuxVersion(result.stdout || "");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Apply the tab-strip status-format for `sessionName`. Per-session
|
|
183
|
+
* (status-format is one of the options that accepts `-t <session>`),
|
|
184
|
+
* so the user's other tmux sessions are unaffected.
|
|
185
|
+
*
|
|
186
|
+
* On tmux >= 3.2 emits the cascading 5-tier compaction format; on
|
|
187
|
+
* older tmux falls back to a single-tier themed format. Failure is
|
|
188
|
+
* non-fatal — returns `{ applied: false, stderr }` so the caller can
|
|
189
|
+
* surface a warning without aborting the launch.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} sessionName
|
|
192
|
+
* @param {TmuxRunner} runner
|
|
193
|
+
* @param {{
|
|
194
|
+
* tmuxVersion?: { major: number, minor: number } | null,
|
|
195
|
+
* theme?: Partial<typeof tabs.DEFAULT_TAB_THEME>,
|
|
196
|
+
* }} [opts]
|
|
197
|
+
* @returns {{ applied: boolean, stderr: string }}
|
|
198
|
+
*/
|
|
199
|
+
function applyTabFormat(sessionName, runner, opts = {}) {
|
|
200
|
+
const theme = { ...tabs.DEFAULT_TAB_THEME, ...(opts.theme || {}) };
|
|
201
|
+
// Use the standard per-window formats instead of overriding the full
|
|
202
|
+
// status-format[0] — broader tmux compatibility, and tmux silently
|
|
203
|
+
// ignores invalid status-format expressions on some versions, which
|
|
204
|
+
// makes overrides hard to debug. window-status-format works on every
|
|
205
|
+
// tmux 2.0+.
|
|
206
|
+
// jakobwesthoff/tmux-from-scratch inspired styling: active tab is a
|
|
207
|
+
// rounded "pill" (Powerline half-round glyphs) over a lighter
|
|
208
|
+
// background, with brand-orange accent on the index:name separator
|
|
209
|
+
// colon. Inactive tabs are plain text with the same accent colon.
|
|
210
|
+
// Session name appears as a rounded pill on the left, hostname as
|
|
211
|
+
// a rounded pill on the right.
|
|
212
|
+
//
|
|
213
|
+
// Requires a Nerd Font / Powerline-patched font in the user's
|
|
214
|
+
// terminal. Without one the glyphs render as tofu boxes; users on
|
|
215
|
+
// a vanilla font can switch back via a future `tabStyle: "flat"`
|
|
216
|
+
// pref.
|
|
217
|
+
const HALF_ROUND_OPEN = ""; //
|
|
218
|
+
const HALF_ROUND_CLOSE = ""; //
|
|
219
|
+
const TRIANGLE_OPEN = ""; //
|
|
220
|
+
const TRIANGLE_CLOSE = ""; //
|
|
221
|
+
const BG = theme.stripBg;
|
|
222
|
+
const PILL_BG = theme.activeNameBg;
|
|
223
|
+
const ACCENT = theme.activeBg;
|
|
224
|
+
const FG = theme.activeNameFg;
|
|
225
|
+
|
|
226
|
+
// Active tab uses the BRAND ACCENT (orange) as its pill background
|
|
227
|
+
// with the strip's dark color as text — maximum contrast against
|
|
228
|
+
// the muted inactive tabs so the user always knows which tab is
|
|
229
|
+
// focused after Alt+1..9 / Alt+Tab.
|
|
230
|
+
const inactiveFormat = ` #I:#W `;
|
|
231
|
+
const activeFormat =
|
|
232
|
+
`#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_OPEN}` +
|
|
233
|
+
`#[bg=${ACCENT},fg=${theme.activeFg},bold] #I:#W ` +
|
|
234
|
+
`#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_CLOSE}`;
|
|
235
|
+
const statusLeft =
|
|
236
|
+
`#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_OPEN}` +
|
|
237
|
+
`#[bg=${PILL_BG},fg=${ACCENT}] #S ` +
|
|
238
|
+
`#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_CLOSE}`;
|
|
239
|
+
const statusRight =
|
|
240
|
+
`#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_OPEN}` +
|
|
241
|
+
`#[bg=${PILL_BG},fg=${ACCENT}] #h ` +
|
|
242
|
+
`#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_CLOSE}`;
|
|
243
|
+
|
|
244
|
+
// Reduce escape-time so pressing Esc inside command-prompt (Alt+n
|
|
245
|
+
// worktree-name prompt, etc.) cancels immediately. tmux's default
|
|
246
|
+
// is 500ms — it waits for a follow-up key in case Esc is the start
|
|
247
|
+
// of an Alt+key sequence. 0ms is "treat Esc as Esc immediately"
|
|
248
|
+
// and matches what most modern terminals support.
|
|
249
|
+
runner.runSync(["set-option", "-sg", "escape-time", "0"]);
|
|
250
|
+
// Force emacs key bindings in the command-prompt so Esc cancels
|
|
251
|
+
// the prompt instead of entering vi-normal mode (which makes the
|
|
252
|
+
// prompt look like it changed color but never closes). If the
|
|
253
|
+
// user's .tmux.conf set status-keys vi globally, our per-session
|
|
254
|
+
// override wins for AgileFlow sessions.
|
|
255
|
+
runner.runSync(["set-option", "-g", "status-keys", "emacs"]);
|
|
256
|
+
runner.runSync(["set-option", "-g", "mode-keys", "emacs"]);
|
|
257
|
+
|
|
258
|
+
// Apply each option with the correct tmux scope. Session-scope opts
|
|
259
|
+
// (status-style, status-left/right, status-justify) take
|
|
260
|
+
// `-t <session>`. Window-scope opts (window-status-format,
|
|
261
|
+
// window-status-current-format, window-status-separator) take
|
|
262
|
+
// `-wg` so every window in every session picks them up. Earlier
|
|
263
|
+
// code applied ALL options with `-t session`, which made tmux
|
|
264
|
+
// silently ignore the window-scope ones — the visible symptom
|
|
265
|
+
// was tmux's default green status bar surviving on every session.
|
|
266
|
+
const ops = [
|
|
267
|
+
{
|
|
268
|
+
scope: "session",
|
|
269
|
+
option: "status-style",
|
|
270
|
+
value: `bg=${BG},fg=${theme.inactiveFg}`,
|
|
271
|
+
},
|
|
272
|
+
{ scope: "session", option: "status-justify", value: "centre" },
|
|
273
|
+
{ scope: "session", option: "status-left", value: statusLeft },
|
|
274
|
+
{ scope: "session", option: "status-left-length", value: "100" },
|
|
275
|
+
{ scope: "session", option: "status-right", value: statusRight },
|
|
276
|
+
{ scope: "session", option: "status-right-length", value: "100" },
|
|
277
|
+
// Number windows from 1 so Alt+1 maps to the first tab (matches
|
|
278
|
+
// Chrome's Ctrl+1 mental model). Tmux default is base-index 0,
|
|
279
|
+
// which means Alt+1 with no other tabs open does nothing.
|
|
280
|
+
{ scope: "session", option: "base-index", value: "1" },
|
|
281
|
+
// Keep tab indices contiguous after a close — without this,
|
|
282
|
+
// closing window 2 leaves indices 1, 3, 4 and Alt+2 becomes
|
|
283
|
+
// dead. tmux renumbers on close so Alt+1..N always works.
|
|
284
|
+
{ scope: "session", option: "renumber-windows", value: "on" },
|
|
285
|
+
{ scope: "window-global", option: "window-status-separator", value: "" },
|
|
286
|
+
{
|
|
287
|
+
scope: "window-global",
|
|
288
|
+
option: "window-status-format",
|
|
289
|
+
value: inactiveFormat,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
scope: "window-global",
|
|
293
|
+
option: "window-status-current-format",
|
|
294
|
+
value: activeFormat,
|
|
295
|
+
},
|
|
296
|
+
];
|
|
297
|
+
let lastResult = { status: 0, stderr: "" };
|
|
298
|
+
const failures = [];
|
|
299
|
+
for (const { scope, option, value } of ops) {
|
|
300
|
+
const args =
|
|
301
|
+
scope === "session"
|
|
302
|
+
? ["set-option", "-t", sessionName, option, value]
|
|
303
|
+
: ["set-option", "-wg", option, value];
|
|
304
|
+
const r = runner.runSync(args);
|
|
305
|
+
lastResult = r;
|
|
306
|
+
if (r.status !== 0) {
|
|
307
|
+
failures.push({ option, stderr: (r.stderr || "").trim() });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (failures.length > 0) {
|
|
311
|
+
// eslint-disable-next-line no-console
|
|
312
|
+
console.error(
|
|
313
|
+
`agileflow launch: tab strip styling — ${failures.length} of ${ops.length} options rejected by tmux:`,
|
|
314
|
+
);
|
|
315
|
+
for (const f of failures) {
|
|
316
|
+
// eslint-disable-next-line no-console
|
|
317
|
+
console.error(` ${f.option}: ${f.stderr || "(no error message)"}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
applied: lastResult.status === 0,
|
|
322
|
+
stderr: lastResult.stderr || "",
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Create a new detached tmux session that immediately runs `bin args...`.
|
|
328
|
+
* Returns the runner's exit status (0 on success).
|
|
329
|
+
*
|
|
330
|
+
* @param {{ name: string, bin: string, args: string[], cwd?: string, statusPosition?: string }} opts
|
|
331
|
+
* @param {TmuxRunner} runner
|
|
332
|
+
* @returns {{ status: number, stderr: string }}
|
|
333
|
+
*/
|
|
334
|
+
function createSession(opts, runner) {
|
|
335
|
+
const args = ["new-session", "-d", "-s", opts.name];
|
|
336
|
+
if (opts.cwd) args.push("-c", opts.cwd);
|
|
337
|
+
args.push(opts.bin, ...opts.args);
|
|
338
|
+
const result = runner.runSync(args);
|
|
339
|
+
if (result.status === 0 && opts.statusPosition) {
|
|
340
|
+
// Per-session option; safe even if the user has a global tmux config.
|
|
341
|
+
runner.runSync([
|
|
342
|
+
"set-option",
|
|
343
|
+
"-t",
|
|
344
|
+
opts.name,
|
|
345
|
+
"status-position",
|
|
346
|
+
opts.statusPosition,
|
|
347
|
+
]);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
status: result.status,
|
|
351
|
+
stderr: result.stderr,
|
|
352
|
+
// Propagate the spawn-itself-failed error so callers can re-throw
|
|
353
|
+
// ENOENT (= tmux not on PATH, e.g., TOCTOU after tmuxAvailable()
|
|
354
|
+
// succeeded) instead of masking it as a generic tmux failure.
|
|
355
|
+
error: result.error || null,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Attach to an existing session in the foreground. Returns the user's
|
|
361
|
+
* exit code from the attach (typically 0 on clean detach).
|
|
362
|
+
*
|
|
363
|
+
* @param {string} name
|
|
364
|
+
* @param {TmuxRunner} runner
|
|
365
|
+
* @returns {Promise<TmuxLaunchResult>}
|
|
366
|
+
*/
|
|
367
|
+
async function attachSession(name, runner) {
|
|
368
|
+
const result = await runner.runAttach(["attach-session", "-t", name]);
|
|
369
|
+
return { exitCode: result.exitCode, signal: result.signal || null };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Keybinds to install per preset. Each entry is the argv suffix passed
|
|
374
|
+
* to `tmux bind-key`, expanded with `-T root -t <session>` at apply
|
|
375
|
+
* time. The `-T root` table means keys fire WITHOUT the tmux prefix
|
|
376
|
+
* (typically Ctrl+B), so users get the v3 `af` script's "tap Alt+q
|
|
377
|
+
* to detach" feel rather than the stock tmux "prefix then bind".
|
|
378
|
+
*
|
|
379
|
+
* Keep this list narrow: every binding has to work with stock tmux,
|
|
380
|
+
* no external helper scripts. Worktree / same-dir spawn shortcuts
|
|
381
|
+
* (the old Alt+N / Alt+S) require shell helpers that don't exist in
|
|
382
|
+
* v4 yet; they'll come in a follow-up slice.
|
|
383
|
+
*
|
|
384
|
+
* @typedef {Object} KeybindEntry
|
|
385
|
+
* @property {string} key - tmux key spec, e.g. "M-q" (Alt+q)
|
|
386
|
+
* @property {string[]} action - tmux command args, e.g. ["detach-client"]
|
|
387
|
+
* @property {string} hint - human description; surfaced in errors if a bind fails
|
|
388
|
+
*/
|
|
389
|
+
|
|
390
|
+
/** @type {Record<string, KeybindEntry[]>} */
|
|
391
|
+
const KEYBIND_PRESET_BINDINGS = {
|
|
392
|
+
default: [
|
|
393
|
+
{
|
|
394
|
+
key: "M-q",
|
|
395
|
+
action: ["detach-client"],
|
|
396
|
+
hint: "Alt+q → detach",
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
// Soft interrupt — send Ctrl+C twice to the foreground pane.
|
|
400
|
+
// Mirrors v3 `af`'s "if Claude is hung, gently nudge it" behavior.
|
|
401
|
+
key: "M-k",
|
|
402
|
+
action: ["send-keys", "C-c", "C-c"],
|
|
403
|
+
hint: "Alt+k → send Ctrl+C twice (soft interrupt)",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
key: "M-K",
|
|
407
|
+
action: ["kill-pane"],
|
|
408
|
+
hint: "Alt+Shift+k → force kill the current pane",
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
key: "M-r",
|
|
412
|
+
action: ["respawn-pane", "-k"],
|
|
413
|
+
hint: "Alt+r → respawn the current pane",
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
// Spawn a parallel same-dir session and switch to it. The bound CLI
|
|
417
|
+
// runs `agileflow launch new` which creates the session detached
|
|
418
|
+
// and calls tmux switch-client — so the user's pane swaps to it.
|
|
419
|
+
// `%AGILEFLOW%` is substituted at apply time with the actual binary
|
|
420
|
+
// path so this works for dogfooded `node bin/agileflow.js` as well
|
|
421
|
+
// as PATH-installed `agileflow`.
|
|
422
|
+
key: "M-s",
|
|
423
|
+
action: ["run-shell", "%AGILEFLOW% launch new"],
|
|
424
|
+
hint: "Alt+s → spawn a same-dir parallel session",
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
// Prompt for a worktree name, then spawn. tmux's command-prompt
|
|
428
|
+
// natively cancels on Escape (or Ctrl+G) without firing the
|
|
429
|
+
// deferred command — the prompt label calls that out so users
|
|
430
|
+
// know they can back out. The agileflow CLI also bails cleanly
|
|
431
|
+
// if `%%` came in empty (user hit Enter with no input).
|
|
432
|
+
key: "M-n",
|
|
433
|
+
action: [
|
|
434
|
+
"command-prompt",
|
|
435
|
+
"-p",
|
|
436
|
+
"worktree name (esc to cancel):",
|
|
437
|
+
"run-shell '%AGILEFLOW% launch new \"%%\"'",
|
|
438
|
+
],
|
|
439
|
+
hint: "Alt+n → prompt for a name, create a worktree, spawn there",
|
|
440
|
+
},
|
|
441
|
+
// v3-equivalent tab (tmux window) keybinds. Living in a separate
|
|
442
|
+
// module so the format builder + keybind table stay testable in
|
|
443
|
+
// isolation; spread into the default preset so they participate in
|
|
444
|
+
// the same apply / unbind sweep as everything else.
|
|
445
|
+
...tabs.TAB_KEYBINDS,
|
|
446
|
+
],
|
|
447
|
+
minimal: [
|
|
448
|
+
{
|
|
449
|
+
key: "M-q",
|
|
450
|
+
action: ["detach-client"],
|
|
451
|
+
hint: "Alt+q → detach",
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
none: [],
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Install the chosen keybind preset. tmux `bind-key` is server-wide
|
|
459
|
+
* (the command doesn't accept `-t <session>`), so applying a preset
|
|
460
|
+
* affects every tmux client on the same server, not just sessions
|
|
461
|
+
* created by `agileflow launch`. This mirrors the v3 `af` script and is
|
|
462
|
+
* the standard tmux config pattern — users who want isolation pick the
|
|
463
|
+
* "none" preset.
|
|
464
|
+
*
|
|
465
|
+
* Bindings go in the `root` table so keys fire without the tmux prefix
|
|
466
|
+
* (typically Ctrl+B), giving the v3 "tap Alt+q to detach" feel.
|
|
467
|
+
*
|
|
468
|
+
* Failures are not fatal: `tmux bind-key` either succeeds or the caller
|
|
469
|
+
* gets a `failures` list to surface as a warning. Returning a
|
|
470
|
+
* structured result keeps the function pure-ish — no console output
|
|
471
|
+
* inside tmux.js.
|
|
472
|
+
*
|
|
473
|
+
* @param {string} preset - one of "default" | "minimal" | "none"
|
|
474
|
+
* @param {TmuxRunner} runner
|
|
475
|
+
* @returns {{ applied: number, failures: { hint: string, stderr: string }[] }}
|
|
476
|
+
*/
|
|
477
|
+
/**
|
|
478
|
+
* Union of all keys touched by any preset. Used to clear stale binds
|
|
479
|
+
* before installing the chosen preset, so switching from "default" to
|
|
480
|
+
* "minimal" doesn't leave Alt+k/Alt+K/Alt+r from the previous run
|
|
481
|
+
* still active in the tmux server. Recomputed once at module load so
|
|
482
|
+
* adding a binding to a preset automatically extends the unbind sweep.
|
|
483
|
+
*/
|
|
484
|
+
const ALL_PRESET_KEYS = (() => {
|
|
485
|
+
const set = new Set();
|
|
486
|
+
for (const list of Object.values(KEYBIND_PRESET_BINDINGS)) {
|
|
487
|
+
for (const b of list) set.add(b.key);
|
|
488
|
+
}
|
|
489
|
+
return [...set];
|
|
490
|
+
})();
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Substitute `%AGILEFLOW%` in a key binding's action with the actual
|
|
494
|
+
* binary path. Pure helper so tests can pin the substitution result.
|
|
495
|
+
*
|
|
496
|
+
* @param {string[]} action
|
|
497
|
+
* @param {string} agileflowBin
|
|
498
|
+
* @returns {string[]}
|
|
499
|
+
*/
|
|
500
|
+
function substituteBinding(action, agileflowBin) {
|
|
501
|
+
return action.map((arg) =>
|
|
502
|
+
typeof arg === "string" ? arg.replace(/%AGILEFLOW%/g, agileflowBin) : arg,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @param {string} preset
|
|
508
|
+
* @param {TmuxRunner} runner
|
|
509
|
+
* @param {{ agileflowBin?: string }} [opts]
|
|
510
|
+
* @returns {{ applied: number, failures: { hint: string, stderr: string }[] }}
|
|
511
|
+
*/
|
|
512
|
+
function applyKeybindPreset(preset, runner, opts = {}) {
|
|
513
|
+
const bindings = KEYBIND_PRESET_BINDINGS[preset] || [];
|
|
514
|
+
let applied = 0;
|
|
515
|
+
/** @type {{ hint: string, stderr: string }[]} */
|
|
516
|
+
const failures = [];
|
|
517
|
+
|
|
518
|
+
// Resolve the agileflow binary path once per preset apply. Falls back
|
|
519
|
+
// to the literal string "agileflow" if argv[1] isn't a real file
|
|
520
|
+
// (matches the alias-installer convention).
|
|
521
|
+
const agileflowBin = opts.agileflowBin || resolveAgileflowBin();
|
|
522
|
+
|
|
523
|
+
// Idempotent reset: unbind every key any preset could install BEFORE
|
|
524
|
+
// applying the new set. Without this, switching default → minimal
|
|
525
|
+
// leaves Alt+k/Alt+K/Alt+r from the previous run still bound (tmux's
|
|
526
|
+
// `bind-key` is server-wide and persists across sessions). Unbinding
|
|
527
|
+
// a key that wasn't bound is harmless — tmux exits non-zero with a
|
|
528
|
+
// "key not found" but we don't care; the post-state is the same.
|
|
529
|
+
for (const key of ALL_PRESET_KEYS) {
|
|
530
|
+
runner.runSync(["unbind-key", "-T", "root", key]);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
for (const b of bindings) {
|
|
534
|
+
const action = substituteBinding(b.action, agileflowBin);
|
|
535
|
+
const result = runner.runSync(["bind-key", "-T", "root", b.key, ...action]);
|
|
536
|
+
if (result.status === 0) {
|
|
537
|
+
applied++;
|
|
538
|
+
} else {
|
|
539
|
+
failures.push({ hint: b.hint, stderr: result.stderr || "" });
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return { applied, failures };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* List existing session names belonging to a specific CLI.
|
|
547
|
+
*
|
|
548
|
+
* Uses `tmux ls -F '#{session_name}'` and filters by the `<cli>-`
|
|
549
|
+
* prefix our `baseSessionName` produces. Returns an empty array if no
|
|
550
|
+
* tmux server is running (tmux ls exits non-zero), the call fails, or
|
|
551
|
+
* no sessions match.
|
|
552
|
+
*
|
|
553
|
+
* @param {string} cli
|
|
554
|
+
* @param {TmuxRunner} runner
|
|
555
|
+
* @returns {string[]}
|
|
556
|
+
*/
|
|
557
|
+
function listSessionsForCli(cli, runner) {
|
|
558
|
+
const result = runner.runSync(["ls", "-F", "#{session_name}"]);
|
|
559
|
+
if (result.status !== 0 || !result.stdout) return [];
|
|
560
|
+
const prefix = `${cli}-`;
|
|
561
|
+
return result.stdout
|
|
562
|
+
.split(/\r?\n/)
|
|
563
|
+
.map((s) => s.trim())
|
|
564
|
+
.filter((s) => s.length > 0 && s.startsWith(prefix));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Kill a tmux session by name. Returns true on success, false on any
|
|
569
|
+
* tmux error (already gone, permission, etc.) — caller decides whether
|
|
570
|
+
* to surface the failure.
|
|
571
|
+
*
|
|
572
|
+
* @param {string} name
|
|
573
|
+
* @param {TmuxRunner} runner
|
|
574
|
+
* @returns {boolean}
|
|
575
|
+
*/
|
|
576
|
+
function killSession(name, runner) {
|
|
577
|
+
const result = runner.runSync(["kill-session", "-t", `=${name}`]);
|
|
578
|
+
return result.status === 0;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Top-level orchestrator: pick a session name, ensure it exists with
|
|
583
|
+
* the user's CLI running inside it, then attach. If the canonical name
|
|
584
|
+
* is already attached elsewhere, spawn a numbered sibling.
|
|
585
|
+
*
|
|
586
|
+
* Caller has already verified tmux is available and we're not nested.
|
|
587
|
+
*
|
|
588
|
+
* @param {{
|
|
589
|
+
* bin: string,
|
|
590
|
+
* args?: string[],
|
|
591
|
+
* cwd?: string,
|
|
592
|
+
* statusPosition?: string,
|
|
593
|
+
* keybindPreset?: string,
|
|
594
|
+
* runner?: TmuxRunner,
|
|
595
|
+
* log?: (msg: string) => void,
|
|
596
|
+
* }} opts
|
|
597
|
+
* @returns {Promise<TmuxLaunchResult>}
|
|
598
|
+
*/
|
|
599
|
+
async function launchInTmux(opts) {
|
|
600
|
+
const runner = opts.runner || defaultRunner();
|
|
601
|
+
const cwd = opts.cwd || process.cwd();
|
|
602
|
+
const base = baseSessionName(path.basename(opts.bin), cwd);
|
|
603
|
+
// Narration: tells the user whether they're resuming an existing
|
|
604
|
+
// session (their context survives) or starting fresh. Defaults to
|
|
605
|
+
// stderr so it doesn't pollute scripts that pipe stdout. Injectable
|
|
606
|
+
// for tests; pass a no-op `log` to silence.
|
|
607
|
+
const log =
|
|
608
|
+
typeof opts.log === "function"
|
|
609
|
+
? opts.log
|
|
610
|
+
: (msg) => {
|
|
611
|
+
// eslint-disable-next-line no-console
|
|
612
|
+
console.error(msg);
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Reattach to the canonical session if it exists. tmux's `attach-session`
|
|
616
|
+
// is the right command for both "exists detached" and "exists attached"
|
|
617
|
+
// — the latter steals or shares the session, mirroring v3 `af` behavior.
|
|
618
|
+
// If the user wants a fresh parallel session, they use `launch --new` (slice 2c).
|
|
619
|
+
// Detect tmux version once per invocation; threaded into the tab
|
|
620
|
+
// format builder so we can degrade to a single-tier format on tmux
|
|
621
|
+
// < 3.2 instead of emitting `#{e|...}` operators it doesn't understand.
|
|
622
|
+
const tmuxVersion = detectTmuxVersion(runner);
|
|
623
|
+
|
|
624
|
+
const existsSync = (name) => sessionExists(name, runner);
|
|
625
|
+
if (existsSync(base)) {
|
|
626
|
+
// Apply prefs on every attach — cheap, and keeps the session in sync
|
|
627
|
+
// if the user changed their pref since the session was created.
|
|
628
|
+
if (opts.statusPosition) {
|
|
629
|
+
runner.runSync([
|
|
630
|
+
"set-option",
|
|
631
|
+
"-t",
|
|
632
|
+
base,
|
|
633
|
+
"status-position",
|
|
634
|
+
opts.statusPosition,
|
|
635
|
+
]);
|
|
636
|
+
}
|
|
637
|
+
if (opts.keybindPreset) {
|
|
638
|
+
// Always invoke applyKeybindPreset — even for "none" — so the
|
|
639
|
+
// unbind sweep runs and clears any leftover binds from a previous
|
|
640
|
+
// "default" or "minimal" run. Without this, switching default → none
|
|
641
|
+
// leaves Alt+k/Alt+K/Alt+r still bound in the tmux server.
|
|
642
|
+
const result = applyKeybindPreset(opts.keybindPreset, runner);
|
|
643
|
+
for (const f of result.failures) {
|
|
644
|
+
log(`agileflow launch: keybind skipped — ${f.hint}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// Re-apply the tab strip every attach so prefs / theme changes
|
|
648
|
+
// since session creation take effect (and so a session created by
|
|
649
|
+
// an older agileflow without a strip picks one up on reattach).
|
|
650
|
+
// Single dark status line — the tab strip on line[0] replaces
|
|
651
|
+
// tmux's default green status bar entirely.
|
|
652
|
+
runner.runSync(["set-option", "-t", base, "status", "1"]);
|
|
653
|
+
applyTabFormat(base, runner, { tmuxVersion });
|
|
654
|
+
log(`agileflow launch: resuming session ${base}`);
|
|
655
|
+
return attachSession(base, runner);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// No existing session — create fresh. Run the agileflow `__exec`
|
|
659
|
+
// wrapper instead of the raw CLI so the per-CLI resume strategy
|
|
660
|
+
// fires (claude `--resume <uuid>` etc.) and so the session is
|
|
661
|
+
// recorded in the cross-reboot registry. The wrapper reads the
|
|
662
|
+
// session name from argv to look itself up in the registry.
|
|
663
|
+
// `base` is confirmed free above; no `nextFreeSessionName` walk needed.
|
|
664
|
+
// Lazy-load to avoid a require cycle (session-registry → tmux is fine,
|
|
665
|
+
// but record-on-create wants the registry which depends on this module
|
|
666
|
+
// transitively via parallel-session).
|
|
667
|
+
const { recordSession } = require("./session-registry.js");
|
|
668
|
+
const agileflowBin = resolveAgileflowBin();
|
|
669
|
+
const name = base;
|
|
670
|
+
const cliId = path.basename(opts.bin);
|
|
671
|
+
recordSession({
|
|
672
|
+
name,
|
|
673
|
+
cli: cliId,
|
|
674
|
+
cwd,
|
|
675
|
+
uuid: null,
|
|
676
|
+
});
|
|
677
|
+
const create = createSession(
|
|
678
|
+
{
|
|
679
|
+
name,
|
|
680
|
+
bin: agileflowBin,
|
|
681
|
+
args: ["launch", "__exec", name],
|
|
682
|
+
cwd,
|
|
683
|
+
statusPosition: opts.statusPosition,
|
|
684
|
+
},
|
|
685
|
+
runner,
|
|
686
|
+
);
|
|
687
|
+
if (create.status !== 0) {
|
|
688
|
+
// Spawn-itself-failed: tmux disappeared between tmuxAvailable() and
|
|
689
|
+
// here. Re-throw the original ENOENT so launch.js's existing TOCTOU
|
|
690
|
+
// branch can produce the install-or-reconfigure suggestion.
|
|
691
|
+
if (create.error) throw create.error;
|
|
692
|
+
|
|
693
|
+
// Concurrent-launch race: another `agileflow launch` from the same
|
|
694
|
+
// cwd created the session between our existsSync probe and our
|
|
695
|
+
// new-session call. The session exists now and is what the user
|
|
696
|
+
// wanted; just attach to it instead of surfacing a misleading
|
|
697
|
+
// "duplicate session" error. Apply the same setup the non-racey
|
|
698
|
+
// reattach path does (keybinds + status height + tab format) so
|
|
699
|
+
// the race-recovered user gets the same UX as everyone else.
|
|
700
|
+
if (sessionExists(name, runner)) {
|
|
701
|
+
log(`agileflow launch: resuming session ${name} (race-recovered)`);
|
|
702
|
+
if (opts.statusPosition) {
|
|
703
|
+
runner.runSync([
|
|
704
|
+
"set-option",
|
|
705
|
+
"-t",
|
|
706
|
+
name,
|
|
707
|
+
"status-position",
|
|
708
|
+
opts.statusPosition,
|
|
709
|
+
]);
|
|
710
|
+
}
|
|
711
|
+
if (opts.keybindPreset) {
|
|
712
|
+
const result = applyKeybindPreset(opts.keybindPreset, runner);
|
|
713
|
+
for (const f of result.failures) {
|
|
714
|
+
log(`agileflow launch: keybind skipped — ${f.hint}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
runner.runSync(["set-option", "-t", name, "status", "1"]);
|
|
718
|
+
applyTabFormat(name, runner, { tmuxVersion });
|
|
719
|
+
return attachSession(name, runner);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const stderr = create.stderr.trim() || "tmux new-session failed";
|
|
723
|
+
const err = new Error(`tmux: ${stderr}`);
|
|
724
|
+
/** @type {any} */ (err).code = "ETMUX_CREATE";
|
|
725
|
+
throw err;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (opts.keybindPreset) {
|
|
729
|
+
// Always invoke applyKeybindPreset — even for "none" — so the unbind
|
|
730
|
+
// sweep runs and clears any prior preset's binds. Same reasoning as
|
|
731
|
+
// the reattach branch above.
|
|
732
|
+
const result = applyKeybindPreset(opts.keybindPreset, runner);
|
|
733
|
+
for (const f of result.failures) {
|
|
734
|
+
log(`agileflow launch: keybind skipped — ${f.hint}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// Single dark status line — the tab strip on status-format[0]
|
|
738
|
+
// replaces tmux's default green status bar entirely. Per-session
|
|
739
|
+
// so other tmux clients are unaffected. Then write the tab format
|
|
740
|
+
// itself. Both are best-effort; failure shouldn't block the attach.
|
|
741
|
+
runner.runSync(["set-option", "-t", name, "status", "1"]);
|
|
742
|
+
applyTabFormat(name, runner, { tmuxVersion });
|
|
743
|
+
log(`agileflow launch: starting new session ${name}`);
|
|
744
|
+
return attachSession(name, runner);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
module.exports = {
|
|
748
|
+
isInsideTmux,
|
|
749
|
+
tmuxAvailable,
|
|
750
|
+
baseSessionName,
|
|
751
|
+
nextFreeSessionName,
|
|
752
|
+
sessionExists,
|
|
753
|
+
createSession,
|
|
754
|
+
attachSession,
|
|
755
|
+
launchInTmux,
|
|
756
|
+
listSessionsForCli,
|
|
757
|
+
killSession,
|
|
758
|
+
applyKeybindPreset,
|
|
759
|
+
substituteBinding,
|
|
760
|
+
detectTmuxVersion,
|
|
761
|
+
applyTabFormat,
|
|
762
|
+
KEYBIND_PRESET_BINDINGS,
|
|
763
|
+
defaultRunner,
|
|
764
|
+
};
|