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,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-CLI resume strategy table.
|
|
3
|
+
*
|
|
4
|
+
* Each entry tells the __exec wrapper:
|
|
5
|
+
* - `resumeArgs(uuid)` — argv to append for "resume this conversation"
|
|
6
|
+
* - `captureUuid(cwd, opts)` — after the CLI exits, return the UUID of the
|
|
7
|
+
* newest conversation associated with this cwd
|
|
8
|
+
* so we can store it back in the registry
|
|
9
|
+
*
|
|
10
|
+
* Strategies covered:
|
|
11
|
+
* - claude: UUID-based; conversations are jsonl files under
|
|
12
|
+
* `~/.claude/projects/<encoded-cwd>/`. Mirrors v3
|
|
13
|
+
* `claude-smart.sh`.
|
|
14
|
+
* - codex: `codex resume --last` resumes the most-recent
|
|
15
|
+
* session. Codex doesn't (yet) take a UUID positionally
|
|
16
|
+
* in a stable way per-cwd, so MVP uses `--last`. Better:
|
|
17
|
+
* parse jsonl files under ~/.codex/sessions/ and target by id (deferred).
|
|
18
|
+
* - cursor-agent: no published resume convention. Restart fresh.
|
|
19
|
+
* - aider: auto-restores from `.aider.chat.history.md` in cwd.
|
|
20
|
+
* No flag needed.
|
|
21
|
+
*
|
|
22
|
+
* All filesystem reads are injectable via `opts.fs` for tests.
|
|
23
|
+
*/
|
|
24
|
+
const fs = require("fs");
|
|
25
|
+
const path = require("path");
|
|
26
|
+
const os = require("os");
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Encode a cwd into Claude's project directory naming convention.
|
|
30
|
+
* Claude stores conversations under `~/.claude/projects/<encoded>/`
|
|
31
|
+
* where `<encoded>` is the absolute path with `/` replaced by `-`
|
|
32
|
+
* (and a leading `-` for the root).
|
|
33
|
+
*
|
|
34
|
+
* @param {string} cwd
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function encodeClaudeProjectDir(cwd) {
|
|
38
|
+
// Normalize backslashes to forward slashes first so Windows paths
|
|
39
|
+
// (`C:\Users\me\app`) and POSIX paths (`/home/me/app`) hit the same
|
|
40
|
+
// encoding path. Then strip leading slashes and convert any remaining
|
|
41
|
+
// separator to `-`. Claude itself produces names like
|
|
42
|
+
// `-home-user-myproject` for `/home/user/myproject`.
|
|
43
|
+
const slashed = cwd.replace(/\\/g, "/");
|
|
44
|
+
const normalized = slashed.replace(/^\/+/, "");
|
|
45
|
+
return "-" + normalized.replace(/\//g, "-");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find the newest non-agent .jsonl file in a Claude project directory
|
|
50
|
+
* and return its UUID (filename minus `.jsonl`). Returns null when no
|
|
51
|
+
* conversation has been recorded yet.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} cwd
|
|
54
|
+
* @param {{
|
|
55
|
+
* home?: string,
|
|
56
|
+
* readdirSync?: typeof fs.readdirSync,
|
|
57
|
+
* statSync?: typeof fs.statSync,
|
|
58
|
+
* }} [opts]
|
|
59
|
+
* @returns {string | null}
|
|
60
|
+
*/
|
|
61
|
+
function captureClaudeUuid(cwd, opts = {}) {
|
|
62
|
+
const home = opts.home || os.homedir();
|
|
63
|
+
const readdirSync = opts.readdirSync || fs.readdirSync;
|
|
64
|
+
const statSync = opts.statSync || fs.statSync;
|
|
65
|
+
const projectDir = path.join(
|
|
66
|
+
home,
|
|
67
|
+
".claude",
|
|
68
|
+
"projects",
|
|
69
|
+
encodeClaudeProjectDir(cwd),
|
|
70
|
+
);
|
|
71
|
+
/** @type {string[]} */
|
|
72
|
+
let entries;
|
|
73
|
+
try {
|
|
74
|
+
entries = readdirSync(projectDir);
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
let bestUuid = null;
|
|
79
|
+
let bestMtime = -Infinity;
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
82
|
+
// Skip agent transcripts — those are sub-conversations spawned by
|
|
83
|
+
// a parent claude session, not the main conversation we want to
|
|
84
|
+
// resume. v3 used the same `agent-` prefix filter.
|
|
85
|
+
if (entry.startsWith("agent-")) continue;
|
|
86
|
+
const full = path.join(projectDir, entry);
|
|
87
|
+
let stat;
|
|
88
|
+
try {
|
|
89
|
+
stat = statSync(full);
|
|
90
|
+
} catch {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const mtime = stat.mtimeMs || 0;
|
|
94
|
+
if (mtime > bestMtime) {
|
|
95
|
+
bestMtime = mtime;
|
|
96
|
+
bestUuid = entry.slice(0, -".jsonl".length);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return bestUuid;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Find the newest codex session whose first-line `payload.cwd` matches
|
|
104
|
+
* the given working directory.
|
|
105
|
+
*
|
|
106
|
+
* Codex stores sessions under
|
|
107
|
+
* `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<timestamp>-<UUID>.jsonl`.
|
|
108
|
+
* The first JSONL line is a `session_meta` record containing
|
|
109
|
+
* `payload.cwd` and `payload.id`. Walking that tree once per launch
|
|
110
|
+
* isn't free, but it's still a few dozen files even for active users
|
|
111
|
+
* and only runs after a session exits — so the cost is amortized.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} cwd
|
|
114
|
+
* @param {{
|
|
115
|
+
* home?: string,
|
|
116
|
+
* readdirSync?: typeof fs.readdirSync,
|
|
117
|
+
* statSync?: typeof fs.statSync,
|
|
118
|
+
* readFileSync?: typeof fs.readFileSync,
|
|
119
|
+
* }} [opts]
|
|
120
|
+
* @returns {string | null}
|
|
121
|
+
*/
|
|
122
|
+
function captureCodexUuid(cwd, opts = {}) {
|
|
123
|
+
const home = opts.home || os.homedir();
|
|
124
|
+
const readdirSync = opts.readdirSync || fs.readdirSync;
|
|
125
|
+
const statSync = opts.statSync || fs.statSync;
|
|
126
|
+
const readFileSync = opts.readFileSync || fs.readFileSync;
|
|
127
|
+
|
|
128
|
+
const sessionsRoot = path.join(home, ".codex", "sessions");
|
|
129
|
+
/** @type {string[]} */
|
|
130
|
+
const jsonlFiles = [];
|
|
131
|
+
|
|
132
|
+
// Recurse year → month → day → files. Tree depth is fixed at 3.
|
|
133
|
+
/** @param {string} dir */
|
|
134
|
+
function collect(dir) {
|
|
135
|
+
let entries;
|
|
136
|
+
try {
|
|
137
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
138
|
+
} catch {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
for (const ent of entries) {
|
|
142
|
+
const full = path.join(dir, ent.name);
|
|
143
|
+
if (ent.isDirectory()) {
|
|
144
|
+
collect(full);
|
|
145
|
+
} else if (ent.isFile() && ent.name.endsWith(".jsonl")) {
|
|
146
|
+
jsonlFiles.push(full);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
collect(sessionsRoot);
|
|
151
|
+
|
|
152
|
+
let bestUuid = null;
|
|
153
|
+
let bestMtime = -Infinity;
|
|
154
|
+
for (const file of jsonlFiles) {
|
|
155
|
+
let stat;
|
|
156
|
+
try {
|
|
157
|
+
stat = statSync(file);
|
|
158
|
+
} catch {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const mtime = typeof stat.mtimeMs === "number" ? stat.mtimeMs : 0;
|
|
162
|
+
if (mtime <= bestMtime) continue;
|
|
163
|
+
// Cheap match: read just the first line.
|
|
164
|
+
let head;
|
|
165
|
+
try {
|
|
166
|
+
head = readFileSync(file, "utf8").split("\n", 1)[0];
|
|
167
|
+
} catch {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(head);
|
|
173
|
+
} catch {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const payload = parsed && parsed.payload;
|
|
177
|
+
if (!payload || typeof payload !== "object") continue;
|
|
178
|
+
if (payload.cwd !== cwd) continue;
|
|
179
|
+
if (typeof payload.id !== "string" || !payload.id) continue;
|
|
180
|
+
bestUuid = payload.id;
|
|
181
|
+
bestMtime = mtime;
|
|
182
|
+
}
|
|
183
|
+
return bestUuid;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @typedef {Object} ResumeStrategy
|
|
188
|
+
* @property {(uuid: string | null) => string[]} resumeArgs
|
|
189
|
+
* @property {(cwd: string, opts?: any) => string | null} captureUuid
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
/** @type {Record<string, ResumeStrategy>} */
|
|
193
|
+
const RESUME_STRATEGIES = {
|
|
194
|
+
claude: {
|
|
195
|
+
resumeArgs: (uuid) => (uuid ? ["--resume", uuid] : []),
|
|
196
|
+
captureUuid: captureClaudeUuid,
|
|
197
|
+
},
|
|
198
|
+
codex: {
|
|
199
|
+
// With a stored UUID we resume that specific session, which is
|
|
200
|
+
// critical when the user has multiple parallel codex sessions
|
|
201
|
+
// across different cwds — `resume --last` would pick whichever
|
|
202
|
+
// they touched most recently, not the one tied to THIS session.
|
|
203
|
+
// Without a UUID (first launch in a cwd, or capture failed) we
|
|
204
|
+
// fall back to `--last`.
|
|
205
|
+
resumeArgs: (uuid) => {
|
|
206
|
+
if (uuid) return ["resume", uuid];
|
|
207
|
+
return ["resume", "--last"];
|
|
208
|
+
},
|
|
209
|
+
captureUuid: captureCodexUuid,
|
|
210
|
+
},
|
|
211
|
+
"cursor-agent": {
|
|
212
|
+
resumeArgs: () => [],
|
|
213
|
+
captureUuid: () => null,
|
|
214
|
+
},
|
|
215
|
+
aider: {
|
|
216
|
+
// aider auto-restores the cwd's chat history from
|
|
217
|
+
// `.aider.chat.history.md` on every invocation. No flag needed.
|
|
218
|
+
resumeArgs: () => [],
|
|
219
|
+
captureUuid: () => null,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Pick a strategy by CLI id; unknown CLIs get the "spawn fresh" no-op.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} cli
|
|
227
|
+
* @returns {ResumeStrategy}
|
|
228
|
+
*/
|
|
229
|
+
function getResumeStrategy(cli) {
|
|
230
|
+
return (
|
|
231
|
+
RESUME_STRATEGIES[cli] || {
|
|
232
|
+
resumeArgs: () => [],
|
|
233
|
+
captureUuid: () => null,
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
RESUME_STRATEGIES,
|
|
240
|
+
getResumeStrategy,
|
|
241
|
+
captureClaudeUuid,
|
|
242
|
+
captureCodexUuid,
|
|
243
|
+
encodeClaudeProjectDir,
|
|
244
|
+
};
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closed-window FIFO log for `agileflow launch`.
|
|
3
|
+
*
|
|
4
|
+
* Backs the `Alt+T → reopen last closed tab` keybind. When the user
|
|
5
|
+
* closes a window via `Alt+w`, the `__close-window` subcommand
|
|
6
|
+
* captures `#W` (name) + `#{pane_current_path}` (cwd) and pushes them
|
|
7
|
+
* here BEFORE issuing `kill-window`. `__restore-window` pops the most
|
|
8
|
+
* recent entry and runs `new-window -c <cwd> -n <name>`.
|
|
9
|
+
*
|
|
10
|
+
* On-disk shape (`~/.agileflow/launch-closed-windows.json`):
|
|
11
|
+
* {
|
|
12
|
+
* "version": 1,
|
|
13
|
+
* "sessions": {
|
|
14
|
+
* "claude-myapp": [
|
|
15
|
+
* { "id": "ab12cd", "name": "src", "cwd": "/...", "closedAt": "..." }
|
|
16
|
+
* ]
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* Per-session stacks are isolated so closing a tab in session A
|
|
21
|
+
* doesn't pollute session B's restore history. Each stack is capped
|
|
22
|
+
* at MAX_PER_SESSION entries (oldest evicted on push).
|
|
23
|
+
*
|
|
24
|
+
* Concurrency: same O_EXCL lock pattern as session-registry.js — two
|
|
25
|
+
* sessions running `__close-window` simultaneously won't lose each
|
|
26
|
+
* other's entries.
|
|
27
|
+
*/
|
|
28
|
+
const fs = require("fs");
|
|
29
|
+
const os = require("os");
|
|
30
|
+
const path = require("path");
|
|
31
|
+
const crypto = require("crypto");
|
|
32
|
+
|
|
33
|
+
const FILENAME = "launch-closed-windows.json";
|
|
34
|
+
const LOCK_SUFFIX = ".lock";
|
|
35
|
+
const LOCK_STALE_MS = 5000;
|
|
36
|
+
const LOCK_MAX_ATTEMPTS = 100;
|
|
37
|
+
const LOCK_RETRY_MS = 10;
|
|
38
|
+
const MAX_PER_SESSION = 20;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} ClosedEntry
|
|
42
|
+
* @property {string} id - short random id for log/debug, not used for lookup
|
|
43
|
+
* @property {string} name - tmux window name (#W) at close time
|
|
44
|
+
* @property {string} cwd - pane's working directory at close time
|
|
45
|
+
* @property {string} closedAt - ISO timestamp
|
|
46
|
+
*
|
|
47
|
+
* @typedef {Object} ClosedLogShape
|
|
48
|
+
* @property {1} version
|
|
49
|
+
* @property {Record<string, ClosedEntry[]>} sessions
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/** @param {number} ms */
|
|
53
|
+
function busyWait(ms) {
|
|
54
|
+
const target = Date.now() + ms;
|
|
55
|
+
while (Date.now() < target) {
|
|
56
|
+
/* spin */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} [home]
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
function logPath(home) {
|
|
65
|
+
return path.join(home || os.homedir(), ".agileflow", FILENAME);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @returns {ClosedLogShape} */
|
|
69
|
+
function emptyLog() {
|
|
70
|
+
return { version: 1, sessions: {} };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* O_EXCL lock around `fn`. Same retry / stale-takeover policy as
|
|
75
|
+
* session-registry's withRegistryLock. Lock file lives next to the
|
|
76
|
+
* log file with a `.lock` suffix so the two registries' locks don't
|
|
77
|
+
* collide.
|
|
78
|
+
*
|
|
79
|
+
* @template T
|
|
80
|
+
* @param {string | undefined} home
|
|
81
|
+
* @param {() => T} fn
|
|
82
|
+
* @returns {T}
|
|
83
|
+
*/
|
|
84
|
+
function withLock(home, fn) {
|
|
85
|
+
const lockFile = logPath(home) + LOCK_SUFFIX;
|
|
86
|
+
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
87
|
+
/** @type {number | null} */
|
|
88
|
+
let lockFd = null;
|
|
89
|
+
for (let attempt = 0; attempt < LOCK_MAX_ATTEMPTS; attempt++) {
|
|
90
|
+
try {
|
|
91
|
+
lockFd = fs.openSync(lockFile, "wx");
|
|
92
|
+
break;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (!err || err.code !== "EEXIST") throw err;
|
|
95
|
+
try {
|
|
96
|
+
const stat = fs.statSync(lockFile);
|
|
97
|
+
if (Date.now() - (stat.mtimeMs || 0) > LOCK_STALE_MS) {
|
|
98
|
+
try {
|
|
99
|
+
fs.unlinkSync(lockFile);
|
|
100
|
+
} catch {
|
|
101
|
+
/* swallow */
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
/* lockfile vanished between EEXIST and stat — loop */
|
|
107
|
+
}
|
|
108
|
+
busyWait(LOCK_RETRY_MS);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (lockFd === null) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`could not acquire launch-closed-windows lock after ${LOCK_MAX_ATTEMPTS} attempts`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return fn();
|
|
118
|
+
} finally {
|
|
119
|
+
try {
|
|
120
|
+
fs.closeSync(lockFd);
|
|
121
|
+
} catch {
|
|
122
|
+
/* swallow */
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
fs.unlinkSync(lockFile);
|
|
126
|
+
} catch {
|
|
127
|
+
/* swallow */
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read the log, forgiving on any I/O or JSON failure (same posture as
|
|
134
|
+
* session-registry). Malformed file = empty log; the user shouldn't be
|
|
135
|
+
* blocked from closing or restoring tabs because of corruption.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} [home]
|
|
138
|
+
* @returns {ClosedLogShape}
|
|
139
|
+
*/
|
|
140
|
+
function loadLog(home) {
|
|
141
|
+
const file = logPath(home);
|
|
142
|
+
let raw;
|
|
143
|
+
try {
|
|
144
|
+
raw = fs.readFileSync(file, "utf8");
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err && err.code === "ENOENT") return emptyLog();
|
|
147
|
+
return emptyLog();
|
|
148
|
+
}
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(raw);
|
|
152
|
+
} catch {
|
|
153
|
+
return emptyLog();
|
|
154
|
+
}
|
|
155
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
156
|
+
return emptyLog();
|
|
157
|
+
}
|
|
158
|
+
const sessions =
|
|
159
|
+
parsed.sessions && typeof parsed.sessions === "object"
|
|
160
|
+
? parsed.sessions
|
|
161
|
+
: {};
|
|
162
|
+
/** @type {Record<string, ClosedEntry[]>} */
|
|
163
|
+
const sane = {};
|
|
164
|
+
for (const [sessionName, list] of Object.entries(sessions)) {
|
|
165
|
+
if (!Array.isArray(list)) continue;
|
|
166
|
+
/** @type {ClosedEntry[]} */
|
|
167
|
+
const cleaned = [];
|
|
168
|
+
for (const e of list) {
|
|
169
|
+
if (!e || typeof e !== "object") continue;
|
|
170
|
+
if (typeof e.name !== "string") continue;
|
|
171
|
+
if (typeof e.cwd !== "string" || !e.cwd) continue;
|
|
172
|
+
cleaned.push({
|
|
173
|
+
id: typeof e.id === "string" ? e.id : shortId(),
|
|
174
|
+
name: e.name,
|
|
175
|
+
cwd: e.cwd,
|
|
176
|
+
closedAt: typeof e.closedAt === "string" ? e.closedAt : "",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (cleaned.length > 0) sane[sessionName] = cleaned;
|
|
180
|
+
}
|
|
181
|
+
return { version: 1, sessions: sane };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Atomic write via tmp + rename. Mirrors session-registry's pattern.
|
|
186
|
+
*
|
|
187
|
+
* @param {ClosedLogShape} log
|
|
188
|
+
* @param {string} [home]
|
|
189
|
+
* @returns {string}
|
|
190
|
+
*/
|
|
191
|
+
function writeLog(log, home) {
|
|
192
|
+
const file = logPath(home);
|
|
193
|
+
const dir = path.dirname(file);
|
|
194
|
+
const tmp = path.join(dir, `.${FILENAME}.tmp-${process.pid}`);
|
|
195
|
+
const content = JSON.stringify(log, null, 2) + "\n";
|
|
196
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
197
|
+
try {
|
|
198
|
+
fs.writeFileSync(tmp, content, "utf8");
|
|
199
|
+
fs.renameSync(tmp, file);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
try {
|
|
202
|
+
fs.unlinkSync(tmp);
|
|
203
|
+
} catch {
|
|
204
|
+
/* swallow */
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
return file;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** @returns {string} */
|
|
212
|
+
function shortId() {
|
|
213
|
+
return crypto.randomBytes(3).toString("hex");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Push a closed window onto the FIFO for `sessionName`. Returns the
|
|
218
|
+
* pushed entry (so callers can log/inspect it). Caps the per-session
|
|
219
|
+
* stack at MAX_PER_SESSION; the oldest entry is evicted when over.
|
|
220
|
+
*
|
|
221
|
+
* @param {{ sessionName: string, name: string, cwd: string }} args
|
|
222
|
+
* @param {string} [home]
|
|
223
|
+
* @returns {ClosedEntry}
|
|
224
|
+
*/
|
|
225
|
+
function pushClosed(args, home) {
|
|
226
|
+
if (!args || typeof args !== "object") {
|
|
227
|
+
throw new TypeError("pushClosed: args required");
|
|
228
|
+
}
|
|
229
|
+
if (typeof args.sessionName !== "string" || !args.sessionName) {
|
|
230
|
+
throw new TypeError("pushClosed: sessionName must be a non-empty string");
|
|
231
|
+
}
|
|
232
|
+
if (typeof args.name !== "string") {
|
|
233
|
+
throw new TypeError("pushClosed: name must be a string");
|
|
234
|
+
}
|
|
235
|
+
if (typeof args.cwd !== "string" || !args.cwd) {
|
|
236
|
+
throw new TypeError("pushClosed: cwd must be a non-empty string");
|
|
237
|
+
}
|
|
238
|
+
/** @type {ClosedEntry} */
|
|
239
|
+
const entry = {
|
|
240
|
+
id: shortId(),
|
|
241
|
+
name: args.name,
|
|
242
|
+
cwd: args.cwd,
|
|
243
|
+
closedAt: new Date().toISOString(),
|
|
244
|
+
};
|
|
245
|
+
withLock(home, () => {
|
|
246
|
+
const log = loadLog(home);
|
|
247
|
+
const list = log.sessions[args.sessionName] || [];
|
|
248
|
+
list.push(entry);
|
|
249
|
+
while (list.length > MAX_PER_SESSION) list.shift();
|
|
250
|
+
log.sessions[args.sessionName] = list;
|
|
251
|
+
writeLog(log, home);
|
|
252
|
+
});
|
|
253
|
+
return entry;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Pop the most recent closed entry for `sessionName`. Returns null
|
|
258
|
+
* when the stack is empty (or the session never had any). Done under
|
|
259
|
+
* the lock so two concurrent `__restore-window` processes don't both
|
|
260
|
+
* resurrect the same entry.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} sessionName
|
|
263
|
+
* @param {string} [home]
|
|
264
|
+
* @returns {ClosedEntry | null}
|
|
265
|
+
*/
|
|
266
|
+
function popClosed(sessionName, home) {
|
|
267
|
+
if (typeof sessionName !== "string" || !sessionName) return null;
|
|
268
|
+
return withLock(home, () => {
|
|
269
|
+
const log = loadLog(home);
|
|
270
|
+
const list = log.sessions[sessionName];
|
|
271
|
+
if (!list || list.length === 0) return null;
|
|
272
|
+
const entry = list.pop();
|
|
273
|
+
if (list.length === 0) delete log.sessions[sessionName];
|
|
274
|
+
else log.sessions[sessionName] = list;
|
|
275
|
+
writeLog(log, home);
|
|
276
|
+
return entry || null;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Look at the most recent entry without removing it. Cheap read —
|
|
282
|
+
* no lock taken; readers tolerate a brief inconsistency window with
|
|
283
|
+
* concurrent writers.
|
|
284
|
+
*
|
|
285
|
+
* @param {string} sessionName
|
|
286
|
+
* @param {string} [home]
|
|
287
|
+
* @returns {ClosedEntry | null}
|
|
288
|
+
*/
|
|
289
|
+
function peekClosed(sessionName, home) {
|
|
290
|
+
if (typeof sessionName !== "string" || !sessionName) return null;
|
|
291
|
+
const log = loadLog(home);
|
|
292
|
+
const list = log.sessions[sessionName];
|
|
293
|
+
if (!list || list.length === 0) return null;
|
|
294
|
+
return list[list.length - 1];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Sweep entries older than `maxAgeMs` across all sessions. Returns the
|
|
299
|
+
* number of entries removed. Intended for a future doctor/prune
|
|
300
|
+
* integration; safe to call from anywhere.
|
|
301
|
+
*
|
|
302
|
+
* @param {number} maxAgeMs
|
|
303
|
+
* @param {string} [home]
|
|
304
|
+
* @returns {number}
|
|
305
|
+
*/
|
|
306
|
+
function clearOlderThan(maxAgeMs, home) {
|
|
307
|
+
if (typeof maxAgeMs !== "number" || maxAgeMs < 0) return 0;
|
|
308
|
+
return withLock(home, () => {
|
|
309
|
+
const log = loadLog(home);
|
|
310
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
311
|
+
let removed = 0;
|
|
312
|
+
for (const [sessionName, list] of Object.entries(log.sessions)) {
|
|
313
|
+
const kept = list.filter((e) => {
|
|
314
|
+
const t = Date.parse(e.closedAt || "");
|
|
315
|
+
if (Number.isNaN(t)) return true; // keep entries with no parseable timestamp
|
|
316
|
+
return t >= cutoff;
|
|
317
|
+
});
|
|
318
|
+
removed += list.length - kept.length;
|
|
319
|
+
if (kept.length === 0) delete log.sessions[sessionName];
|
|
320
|
+
else log.sessions[sessionName] = kept;
|
|
321
|
+
}
|
|
322
|
+
if (removed > 0) writeLog(log, home);
|
|
323
|
+
return removed;
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
module.exports = {
|
|
328
|
+
FILENAME,
|
|
329
|
+
MAX_PER_SESSION,
|
|
330
|
+
logPath,
|
|
331
|
+
emptyLog,
|
|
332
|
+
loadLog,
|
|
333
|
+
writeLog,
|
|
334
|
+
pushClosed,
|
|
335
|
+
popClosed,
|
|
336
|
+
peekClosed,
|
|
337
|
+
clearOlderThan,
|
|
338
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default preferences for `agileflow launch`.
|
|
3
|
+
*
|
|
4
|
+
* `launch-prefs.json` is the first user-level config in v4 (lives under
|
|
5
|
+
* `~/.agileflow/`). Returned when the file is absent and merged into a
|
|
6
|
+
* partial user file so missing keys take sensible values.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} LaunchCliPrefs
|
|
9
|
+
* @property {string} preferred - CLI to invoke by default
|
|
10
|
+
* @property {string[]} fallbackOrder - order to try if preferred is missing
|
|
11
|
+
*
|
|
12
|
+
* @typedef {Object} LaunchTmuxPrefs
|
|
13
|
+
* @property {boolean} enabled - false skips tmux entirely
|
|
14
|
+
* @property {'top' | 'bottom'} statusPosition - tmux status-position value
|
|
15
|
+
*
|
|
16
|
+
* @typedef {Object} LaunchKeybindPrefs
|
|
17
|
+
* @property {'default' | 'minimal' | 'none'} preset - keybind preset name
|
|
18
|
+
*
|
|
19
|
+
* @typedef {Object} LaunchAliasPref
|
|
20
|
+
* @property {boolean} enabled - install `af` symlink to ~/.local/bin/af
|
|
21
|
+
*
|
|
22
|
+
* @typedef {Object} LaunchAliasesPrefs
|
|
23
|
+
* @property {LaunchAliasPref} af
|
|
24
|
+
*
|
|
25
|
+
* @typedef {Object} LaunchPrefs
|
|
26
|
+
* @property {1} version
|
|
27
|
+
* @property {LaunchCliPrefs} cli
|
|
28
|
+
* @property {LaunchTmuxPrefs} tmux
|
|
29
|
+
* @property {LaunchKeybindPrefs} keybinds
|
|
30
|
+
* @property {LaunchAliasesPrefs} aliases
|
|
31
|
+
* @property {string[]} pinned - reserved for slice 3 (pinning UI)
|
|
32
|
+
* @property {string} [lastUpdated] - ISO timestamp stamped by writePrefs
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const KNOWN_CLI_IDS = ["claude", "codex", "cursor-agent", "aider"];
|
|
36
|
+
const STATUS_POSITIONS = ["top", "bottom"];
|
|
37
|
+
const KEYBIND_PRESETS = ["default", "minimal", "none"];
|
|
38
|
+
|
|
39
|
+
/** @returns {LaunchPrefs} */
|
|
40
|
+
function defaultPrefs() {
|
|
41
|
+
return {
|
|
42
|
+
version: 1,
|
|
43
|
+
cli: {
|
|
44
|
+
preferred: "claude",
|
|
45
|
+
fallbackOrder: [...KNOWN_CLI_IDS],
|
|
46
|
+
},
|
|
47
|
+
tmux: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
statusPosition: "bottom",
|
|
50
|
+
},
|
|
51
|
+
keybinds: {
|
|
52
|
+
preset: "default",
|
|
53
|
+
},
|
|
54
|
+
aliases: {
|
|
55
|
+
af: { enabled: false },
|
|
56
|
+
},
|
|
57
|
+
pinned: [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
defaultPrefs,
|
|
63
|
+
KNOWN_CLI_IDS,
|
|
64
|
+
STATUS_POSITIONS,
|
|
65
|
+
KEYBIND_PRESETS,
|
|
66
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect which AI CLIs are installed on the user's PATH.
|
|
3
|
+
*
|
|
4
|
+
* `agileflow launch` wraps whichever CLI the user has chosen — but the
|
|
5
|
+
* onboarding picker should only offer options that actually resolve.
|
|
6
|
+
* This module owns the canonical list of known AI CLIs and the routine
|
|
7
|
+
* that filters it against the system.
|
|
8
|
+
*
|
|
9
|
+
* `commandExists` is injectable so tests don't shell out.
|
|
10
|
+
*/
|
|
11
|
+
const { commandExists: realCommandExists } = require("../../lib/path-check.js");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} CliDescriptor
|
|
15
|
+
* @property {string} id - stable id used in launch-prefs.json
|
|
16
|
+
* @property {string} bin - binary name on PATH
|
|
17
|
+
* @property {string} label - human-readable name for pickers
|
|
18
|
+
* @property {string} hint - one-line description shown in pickers
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** @type {CliDescriptor[]} */
|
|
22
|
+
const KNOWN_CLIS = [
|
|
23
|
+
{
|
|
24
|
+
id: "claude",
|
|
25
|
+
bin: "claude",
|
|
26
|
+
label: "Claude Code",
|
|
27
|
+
hint: "Anthropic's official terminal CLI",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "codex",
|
|
31
|
+
bin: "codex",
|
|
32
|
+
label: "OpenAI Codex CLI",
|
|
33
|
+
hint: "OpenAI's terminal coding agent",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "cursor-agent",
|
|
37
|
+
bin: "cursor-agent",
|
|
38
|
+
label: "Cursor Agent",
|
|
39
|
+
hint: "Cursor's standalone CLI agent",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "aider",
|
|
43
|
+
bin: "aider",
|
|
44
|
+
label: "Aider",
|
|
45
|
+
hint: "Open-source pair programming CLI",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Return descriptors for CLIs whose binary resolves on PATH.
|
|
51
|
+
*
|
|
52
|
+
* @param {(name: string) => boolean} [exists] - injectable for tests
|
|
53
|
+
* @returns {CliDescriptor[]}
|
|
54
|
+
*/
|
|
55
|
+
function availableClis(exists = realCommandExists) {
|
|
56
|
+
return KNOWN_CLIS.filter((c) => exists(c.bin));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find a descriptor by id. Returns null if unknown.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} id
|
|
63
|
+
* @returns {CliDescriptor | null}
|
|
64
|
+
*/
|
|
65
|
+
function findCli(id) {
|
|
66
|
+
return KNOWN_CLIS.find((c) => c.id === id) || null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { KNOWN_CLIS, availableClis, findCli };
|