agileflow 3.4.3 → 4.0.0-alpha.2
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 +238 -473
- package/README.md +22 -114
- package/bin/agileflow.js +15 -0
- package/bin/hooks/pre-bash.js +35 -0
- package/bin/hooks/pre-compact.js +34 -0
- package/bin/hooks/pre-edit.js +32 -0
- package/bin/hooks/pre-write.js +32 -0
- package/bin/hooks/session-start.js +42 -0
- package/bin/hooks/stop.js +34 -0
- package/content/plugins/ads/plugin.yaml +14 -0
- package/content/plugins/audit/plugin.yaml +14 -0
- package/content/plugins/core/hooks/babysit-mentor-injector.js +55 -0
- package/content/plugins/core/hooks/context-loader.js +169 -0
- package/content/plugins/core/hooks/damage-control-bash.js +78 -0
- package/content/plugins/core/hooks/damage-control-edit.js +76 -0
- package/content/plugins/core/hooks/damage-control-patterns.yaml +100 -0
- package/content/plugins/core/hooks/damage-control-write.js +72 -0
- package/content/plugins/core/hooks/pre-compact-state.js +90 -0
- package/content/plugins/core/hooks/session-welcome.js +19 -0
- package/content/plugins/core/plugin.yaml +82 -0
- package/content/plugins/core/skills/agileflow-adr/SKILL.md +179 -0
- package/content/plugins/core/skills/agileflow-babysit-mentor/SKILL.md +144 -0
- package/content/plugins/core/skills/agileflow-epic-planner/SKILL.md +179 -0
- package/content/plugins/core/skills/agileflow-status-updater/SKILL.md +132 -0
- package/content/plugins/core/skills/agileflow-story-writer/SKILL.md +200 -0
- package/content/plugins/council/plugin.yaml +14 -0
- package/content/plugins/seo/plugin.yaml +14 -0
- package/package.json +29 -49
- package/src/cli/commands/doctor.js +159 -0
- package/src/cli/commands/hook.js +80 -0
- package/src/cli/commands/setup.js +292 -0
- package/src/cli/commands/status.js +47 -0
- package/src/cli/commands/update.js +83 -0
- package/src/cli/index.js +73 -0
- package/src/cli/wizard/behaviors-picker.js +108 -0
- package/src/cli/wizard/ide-picker.js +57 -0
- package/src/cli/wizard/personalization.js +64 -0
- package/src/cli/wizard/plugin-picker.js +106 -0
- package/src/lib/hash.js +41 -0
- package/src/runtime/config/defaults.js +61 -0
- package/src/runtime/config/loader.js +117 -0
- package/src/runtime/config/schema.json +99 -0
- package/src/runtime/config/writer.js +55 -0
- package/src/runtime/hooks/aggregator.js +157 -0
- package/src/runtime/hooks/chain.js +93 -0
- package/src/runtime/hooks/logger.js +68 -0
- package/src/runtime/hooks/manifest-loader.js +228 -0
- package/src/runtime/hooks/orchestrator.js +322 -0
- package/src/runtime/ide/capabilities.js +111 -0
- package/src/runtime/ide/claude-code-settings.js +234 -0
- package/src/runtime/ide/claude-code-skills.js +202 -0
- package/src/runtime/installer/file-index.js +112 -0
- package/src/runtime/installer/install.js +329 -0
- package/src/runtime/installer/stash.js +61 -0
- package/src/runtime/installer/sync-engine.js +205 -0
- package/src/runtime/plugins/registry.js +132 -0
- package/src/runtime/plugins/resolver.js +138 -0
- package/src/runtime/plugins/validator.js +196 -0
- package/src/runtime/skills/validator.js +335 -0
- package/lib/README.md +0 -178
- package/lib/api-routes.js +0 -625
- package/lib/api-server.js +0 -278
- package/lib/cache-provider.js +0 -155
- package/lib/codebase-indexer.js +0 -819
- package/lib/colors.generated.js +0 -117
- package/lib/colors.js +0 -341
- package/lib/consent.js +0 -232
- package/lib/content-sanitizer.js +0 -464
- package/lib/correlation.js +0 -277
- package/lib/drivers/claude-driver.ts +0 -312
- package/lib/drivers/codex-driver.ts +0 -464
- package/lib/drivers/driver-manager.ts +0 -159
- package/lib/drivers/gemini-driver.ts +0 -498
- package/lib/drivers/index.ts +0 -17
- package/lib/error-codes.js +0 -590
- package/lib/errors.js +0 -670
- package/lib/feature-flags.js +0 -171
- package/lib/feedback.js +0 -595
- package/lib/file-cache.js +0 -541
- package/lib/flag-detection.js +0 -344
- package/lib/format-error.js +0 -156
- package/lib/gate-runner.js +0 -282
- package/lib/generator-factory.js +0 -333
- package/lib/git-operations.js +0 -266
- package/lib/lazy-require.js +0 -59
- package/lib/lock-file.js +0 -144
- package/lib/logger.js +0 -106
- package/lib/merge-operations.js +0 -1006
- package/lib/path-resolver.js +0 -544
- package/lib/path-utils.js +0 -49
- package/lib/paths.js +0 -291
- package/lib/placeholder-registry.js +0 -822
- package/lib/process-executor.js +0 -214
- package/lib/progress.js +0 -334
- package/lib/protocol/driver.ts +0 -354
- package/lib/protocol/index.ts +0 -12
- package/lib/protocol/ir.ts +0 -271
- package/lib/registry-cache.js +0 -80
- package/lib/registry-di.js +0 -358
- package/lib/result-schema.js +0 -363
- package/lib/result.js +0 -210
- package/lib/session-display.js +0 -331
- package/lib/session-operations.js +0 -611
- package/lib/session-registry.js +0 -484
- package/lib/session-state-machine.js +0 -465
- package/lib/session-switching.js +0 -191
- package/lib/skill-loader.js +0 -213
- package/lib/smart-json-file.js +0 -682
- package/lib/state-machine.js +0 -286
- package/lib/table-formatter.js +0 -519
- package/lib/template-loader.js +0 -143
- package/lib/transient-status.js +0 -374
- package/lib/ui-manager.js +0 -612
- package/lib/validate-args.js +0 -213
- package/lib/validate-commands.js +0 -308
- package/lib/validate-names.js +0 -143
- package/lib/validate-paths.js +0 -434
- package/lib/validate.js +0 -134
- package/lib/worktree-operations.js +0 -201
- package/lib/yaml-utils.js +0 -164
- package/scripts/README.md +0 -267
- package/scripts/af +0 -34
- package/scripts/agent-loop.js +0 -879
- package/scripts/agileflow-configure.js +0 -368
- package/scripts/agileflow-statusline.sh +0 -857
- package/scripts/agileflow-welcome.js +0 -2246
- package/scripts/api-server-runner.js +0 -177
- package/scripts/archive-completed-stories.sh +0 -308
- package/scripts/auto-self-improve.js +0 -326
- package/scripts/automation-run-due.js +0 -128
- package/scripts/babysit-clear-restore.js +0 -154
- package/scripts/babysit-context-restore.js +0 -89
- package/scripts/backfill-ideation-status.js +0 -128
- package/scripts/batch-pmap-loop.js +0 -551
- package/scripts/check-sessions.js +0 -116
- package/scripts/check-update.js +0 -282
- package/scripts/ci-summary.js +0 -294
- package/scripts/claude-smart.sh +0 -85
- package/scripts/claude-tmux.sh +0 -737
- package/scripts/claude-watchdog.sh +0 -225
- package/scripts/clear-active-command.js +0 -48
- package/scripts/compress-status.sh +0 -116
- package/scripts/context-loader.js +0 -310
- package/scripts/damage-control/bash-tool-damage-control.js +0 -22
- package/scripts/damage-control/edit-tool-damage-control.js +0 -19
- package/scripts/damage-control/patterns.yaml +0 -227
- package/scripts/damage-control/write-tool-damage-control.js +0 -19
- package/scripts/damage-control-bash.js +0 -51
- package/scripts/damage-control-edit.js +0 -48
- package/scripts/damage-control-multi-agent.js +0 -231
- package/scripts/damage-control-write.js +0 -48
- package/scripts/dependency-check.js +0 -311
- package/scripts/document-repl.js +0 -793
- package/scripts/expertise-metrics.sh +0 -264
- package/scripts/generate-all.sh +0 -77
- package/scripts/generate-colors.js +0 -314
- package/scripts/generators/agent-registry.js +0 -183
- package/scripts/generators/command-registry.js +0 -166
- package/scripts/generators/index.js +0 -85
- package/scripts/generators/inject-babysit.js +0 -191
- package/scripts/generators/inject-help.js +0 -125
- package/scripts/generators/inject-readme.js +0 -166
- package/scripts/generators/skill-registry.js +0 -188
- package/scripts/get-env.js +0 -225
- package/scripts/init.sh +0 -76
- package/scripts/lib/README-portable-tasks.md +0 -424
- package/scripts/lib/ac-test-matcher.js +0 -452
- package/scripts/lib/audit-cleanup.js +0 -250
- package/scripts/lib/audit-registry.js +0 -340
- package/scripts/lib/automation-registry.js +0 -544
- package/scripts/lib/automation-runner.js +0 -476
- package/scripts/lib/browser-qa-evidence.js +0 -409
- package/scripts/lib/browser-qa-status.js +0 -192
- package/scripts/lib/bus-utils.js +0 -473
- package/scripts/lib/colors.generated.sh +0 -82
- package/scripts/lib/colors.sh +0 -46
- package/scripts/lib/command-prereqs.js +0 -280
- package/scripts/lib/concurrency-limiter.js +0 -511
- package/scripts/lib/configure-detect.js +0 -596
- package/scripts/lib/configure-features.js +0 -1927
- package/scripts/lib/configure-repair.js +0 -327
- package/scripts/lib/configure-utils.js +0 -114
- package/scripts/lib/context-formatter.js +0 -1158
- package/scripts/lib/context-loader.js +0 -840
- package/scripts/lib/counter.js +0 -103
- package/scripts/lib/damage-control-utils.js +0 -619
- package/scripts/lib/feature-catalog.js +0 -332
- package/scripts/lib/file-lock.js +0 -392
- package/scripts/lib/file-tracking.js +0 -735
- package/scripts/lib/frontmatter-parser.js +0 -133
- package/scripts/lib/gate-enforcer.js +0 -295
- package/scripts/lib/hook-metrics.js +0 -324
- package/scripts/lib/ideation-index.js +0 -1205
- package/scripts/lib/json-utils.sh +0 -162
- package/scripts/lib/lifecycle-detector.js +0 -125
- package/scripts/lib/model-profiles.js +0 -118
- package/scripts/lib/portable-tasks-cli.js +0 -274
- package/scripts/lib/portable-tasks.js +0 -479
- package/scripts/lib/process-cleanup.js +0 -527
- package/scripts/lib/quality-gates.js +0 -788
- package/scripts/lib/scale-detector.js +0 -396
- package/scripts/lib/sessionRegistry.js +0 -678
- package/scripts/lib/signal-detectors.js +0 -867
- package/scripts/lib/skill-catalog.js +0 -557
- package/scripts/lib/skill-recommender.js +0 -311
- package/scripts/lib/state-migrator.js +0 -353
- package/scripts/lib/status-task-bridge.js +0 -522
- package/scripts/lib/status-writer.js +0 -255
- package/scripts/lib/story-claiming.js +0 -704
- package/scripts/lib/story-state-machine.js +0 -437
- package/scripts/lib/sync-ideation-status.js +0 -291
- package/scripts/lib/task-registry-cache.js +0 -490
- package/scripts/lib/task-registry.js +0 -1191
- package/scripts/lib/task-sync.js +0 -230
- package/scripts/lib/tdd-phase-manager.js +0 -455
- package/scripts/lib/team-events.js +0 -510
- package/scripts/lib/tmux-audit-monitor.js +0 -612
- package/scripts/lib/tmux-group-colors.js +0 -113
- package/scripts/lib/tool-registry.yaml +0 -241
- package/scripts/lib/tool-shed.js +0 -441
- package/scripts/lib/validation-registry.js +0 -177
- package/scripts/messaging-bridge.js +0 -561
- package/scripts/migrate-ideation-index.js +0 -553
- package/scripts/native-team-observer.js +0 -219
- package/scripts/obtain-context.js +0 -272
- package/scripts/pre-push-check.sh +0 -46
- package/scripts/precompact-context.sh +0 -306
- package/scripts/query-codebase.js +0 -543
- package/scripts/ralph-loop.js +0 -1278
- package/scripts/resume-session.sh +0 -121
- package/scripts/screenshot-verifier.js +0 -215
- package/scripts/session-boundary.js +0 -138
- package/scripts/session-coordinator.sh +0 -232
- package/scripts/session-manager.js +0 -546
- package/scripts/smart-detect.js +0 -449
- package/scripts/spawn-audit-sessions.js +0 -877
- package/scripts/spawn-parallel.js +0 -751
- package/scripts/strip-ai-attribution.js +0 -63
- package/scripts/task-completed-gate.js +0 -237
- package/scripts/team-manager.js +0 -596
- package/scripts/team-status-display.js +0 -200
- package/scripts/teammate-idle-gate.js +0 -237
- package/scripts/test-session-boundary.js +0 -80
- package/scripts/tmux-close-windows.sh +0 -180
- package/scripts/tmux-restore-window.sh +0 -67
- package/scripts/tmux-save-closed-window.sh +0 -35
- package/scripts/tui/App.js +0 -151
- package/scripts/tui/Dashboard.js +0 -277
- package/scripts/tui/blessed/data/watcher.js +0 -180
- package/scripts/tui/blessed/index.js +0 -244
- package/scripts/tui/blessed/panels/output.js +0 -101
- package/scripts/tui/blessed/panels/sessions.js +0 -150
- package/scripts/tui/blessed/panels/trace.js +0 -97
- package/scripts/tui/blessed/ui/help.js +0 -77
- package/scripts/tui/blessed/ui/screen.js +0 -52
- package/scripts/tui/blessed/ui/statusbar.js +0 -47
- package/scripts/tui/blessed/ui/tabbar.js +0 -99
- package/scripts/tui/index.js +0 -70
- package/scripts/tui/lib/crashRecovery.js +0 -304
- package/scripts/tui/lib/eventStream.js +0 -309
- package/scripts/tui/lib/keyboard.js +0 -261
- package/scripts/tui/lib/loopControl.js +0 -371
- package/scripts/tui/panels/OutputPanel.js +0 -240
- package/scripts/tui/panels/SessionPanel.js +0 -170
- package/scripts/tui/panels/TracePanel.js +0 -298
- package/scripts/tui/simple-tui.js +0 -510
- package/scripts/validate-expertise.sh +0 -263
- package/scripts/validate-tokens.sh +0 -73
- package/scripts/validators/README.md +0 -143
- package/scripts/validators/component-validator.js +0 -239
- package/scripts/validators/json-schema-validator.js +0 -186
- package/scripts/validators/markdown-validator.js +0 -152
- package/scripts/validators/migration-validator.js +0 -129
- package/scripts/validators/security-validator.js +0 -380
- package/scripts/validators/story-format-validator.js +0 -197
- package/scripts/validators/test-result-validator.js +0 -114
- package/scripts/validators/workflow-validator.js +0 -247
- package/scripts/welcome-deferred.js +0 -437
- package/scripts/worktree-create.sh +0 -111
- package/src/core/agents/a11y-analyzer-aria.md +0 -155
- package/src/core/agents/a11y-analyzer-forms.md +0 -162
- package/src/core/agents/a11y-analyzer-keyboard.md +0 -175
- package/src/core/agents/a11y-analyzer-semantic.md +0 -153
- package/src/core/agents/a11y-analyzer-visual.md +0 -158
- package/src/core/agents/a11y-consensus.md +0 -248
- package/src/core/agents/accessibility.md +0 -515
- package/src/core/agents/adr-writer.md +0 -463
- package/src/core/agents/ads-audit-budget.md +0 -181
- package/src/core/agents/ads-audit-compliance.md +0 -169
- package/src/core/agents/ads-audit-creative.md +0 -164
- package/src/core/agents/ads-audit-google.md +0 -226
- package/src/core/agents/ads-audit-meta.md +0 -183
- package/src/core/agents/ads-audit-tracking.md +0 -197
- package/src/core/agents/ads-consensus.md +0 -396
- package/src/core/agents/ads-generate.md +0 -145
- package/src/core/agents/ads-performance-tracker.md +0 -197
- package/src/core/agents/analytics.md +0 -617
- package/src/core/agents/api-quality-analyzer-conventions.md +0 -148
- package/src/core/agents/api-quality-analyzer-docs.md +0 -176
- package/src/core/agents/api-quality-analyzer-errors.md +0 -183
- package/src/core/agents/api-quality-analyzer-pagination.md +0 -171
- package/src/core/agents/api-quality-analyzer-versioning.md +0 -143
- package/src/core/agents/api-quality-consensus.md +0 -214
- package/src/core/agents/api-validator.md +0 -183
- package/src/core/agents/api.md +0 -665
- package/src/core/agents/arch-analyzer-circular.md +0 -148
- package/src/core/agents/arch-analyzer-complexity.md +0 -171
- package/src/core/agents/arch-analyzer-coupling.md +0 -146
- package/src/core/agents/arch-analyzer-layering.md +0 -151
- package/src/core/agents/arch-analyzer-patterns.md +0 -162
- package/src/core/agents/arch-consensus.md +0 -227
- package/src/core/agents/brainstorm-analyzer-features.md +0 -169
- package/src/core/agents/brainstorm-analyzer-growth.md +0 -161
- package/src/core/agents/brainstorm-analyzer-integration.md +0 -172
- package/src/core/agents/brainstorm-analyzer-market.md +0 -147
- package/src/core/agents/brainstorm-analyzer-ux.md +0 -167
- package/src/core/agents/brainstorm-consensus.md +0 -237
- package/src/core/agents/browser-qa.md +0 -328
- package/src/core/agents/ci.md +0 -511
- package/src/core/agents/code-reviewer.md +0 -288
- package/src/core/agents/codebase-query.md +0 -266
- package/src/core/agents/completeness-analyzer-api.md +0 -190
- package/src/core/agents/completeness-analyzer-conditional.md +0 -201
- package/src/core/agents/completeness-analyzer-handlers.md +0 -159
- package/src/core/agents/completeness-analyzer-imports.md +0 -159
- package/src/core/agents/completeness-analyzer-routes.md +0 -182
- package/src/core/agents/completeness-analyzer-state.md +0 -188
- package/src/core/agents/completeness-analyzer-stubs.md +0 -198
- package/src/core/agents/completeness-consensus.md +0 -286
- package/src/core/agents/compliance.md +0 -509
- package/src/core/agents/council-advocate.md +0 -206
- package/src/core/agents/council-analyst.md +0 -252
- package/src/core/agents/council-optimist.md +0 -170
- package/src/core/agents/database.md +0 -601
- package/src/core/agents/datamigration.md +0 -699
- package/src/core/agents/design.md +0 -525
- package/src/core/agents/devops.md +0 -720
- package/src/core/agents/documentation.md +0 -504
- package/src/core/agents/epic-planner.md +0 -480
- package/src/core/agents/error-analyzer.md +0 -201
- package/src/core/agents/integrations.md +0 -603
- package/src/core/agents/legal-analyzer-a11y.md +0 -110
- package/src/core/agents/legal-analyzer-ai.md +0 -117
- package/src/core/agents/legal-analyzer-consumer.md +0 -108
- package/src/core/agents/legal-analyzer-content.md +0 -113
- package/src/core/agents/legal-analyzer-international.md +0 -115
- package/src/core/agents/legal-analyzer-licensing.md +0 -115
- package/src/core/agents/legal-analyzer-privacy.md +0 -108
- package/src/core/agents/legal-analyzer-security.md +0 -112
- package/src/core/agents/legal-analyzer-terms.md +0 -111
- package/src/core/agents/legal-consensus.md +0 -242
- package/src/core/agents/logic-analyzer-edge.md +0 -170
- package/src/core/agents/logic-analyzer-flow.md +0 -253
- package/src/core/agents/logic-analyzer-invariant.md +0 -206
- package/src/core/agents/logic-analyzer-race.md +0 -266
- package/src/core/agents/logic-analyzer-type.md +0 -217
- package/src/core/agents/logic-consensus.md +0 -253
- package/src/core/agents/mentor.md +0 -654
- package/src/core/agents/mobile.md +0 -501
- package/src/core/agents/monitoring.md +0 -537
- package/src/core/agents/multi-expert.md +0 -311
- package/src/core/agents/orchestrator.md +0 -749
- package/src/core/agents/perf-analyzer-assets.md +0 -174
- package/src/core/agents/perf-analyzer-bundle.md +0 -165
- package/src/core/agents/perf-analyzer-caching.md +0 -160
- package/src/core/agents/perf-analyzer-compute.md +0 -165
- package/src/core/agents/perf-analyzer-memory.md +0 -182
- package/src/core/agents/perf-analyzer-network.md +0 -157
- package/src/core/agents/perf-analyzer-queries.md +0 -155
- package/src/core/agents/perf-analyzer-rendering.md +0 -156
- package/src/core/agents/perf-consensus.md +0 -280
- package/src/core/agents/performance.md +0 -492
- package/src/core/agents/product.md +0 -535
- package/src/core/agents/qa.md +0 -765
- package/src/core/agents/readme-updater.md +0 -579
- package/src/core/agents/refactor.md +0 -558
- package/src/core/agents/research.md +0 -453
- package/src/core/agents/rlm-subcore.md +0 -207
- package/src/core/agents/schema-validator.md +0 -454
- package/src/core/agents/security-analyzer-api.md +0 -199
- package/src/core/agents/security-analyzer-auth.md +0 -160
- package/src/core/agents/security-analyzer-authz.md +0 -168
- package/src/core/agents/security-analyzer-deps.md +0 -147
- package/src/core/agents/security-analyzer-infra.md +0 -176
- package/src/core/agents/security-analyzer-injection.md +0 -148
- package/src/core/agents/security-analyzer-input.md +0 -191
- package/src/core/agents/security-analyzer-secrets.md +0 -175
- package/src/core/agents/security-consensus.md +0 -276
- package/src/core/agents/security.md +0 -486
- package/src/core/agents/seo-analyzer-content.md +0 -167
- package/src/core/agents/seo-analyzer-images.md +0 -187
- package/src/core/agents/seo-analyzer-performance.md +0 -206
- package/src/core/agents/seo-analyzer-schema.md +0 -176
- package/src/core/agents/seo-analyzer-sitemap.md +0 -172
- package/src/core/agents/seo-analyzer-technical.md +0 -144
- package/src/core/agents/seo-consensus.md +0 -289
- package/src/core/agents/team-coordinator.md +0 -333
- package/src/core/agents/team-lead.md +0 -171
- package/src/core/agents/test-analyzer-assertions.md +0 -181
- package/src/core/agents/test-analyzer-coverage.md +0 -183
- package/src/core/agents/test-analyzer-fragility.md +0 -185
- package/src/core/agents/test-analyzer-integration.md +0 -155
- package/src/core/agents/test-analyzer-maintenance.md +0 -173
- package/src/core/agents/test-analyzer-mocking.md +0 -178
- package/src/core/agents/test-analyzer-patterns.md +0 -189
- package/src/core/agents/test-analyzer-structure.md +0 -177
- package/src/core/agents/test-consensus.md +0 -294
- package/src/core/agents/testing.md +0 -527
- package/src/core/agents/ui-validator.md +0 -331
- package/src/core/agents/ui.md +0 -1227
- package/src/core/commands/adr/list.md +0 -191
- package/src/core/commands/adr/update.md +0 -258
- package/src/core/commands/adr/view.md +0 -274
- package/src/core/commands/adr.md +0 -394
- package/src/core/commands/ads/audit.md +0 -453
- package/src/core/commands/ads/budget.md +0 -97
- package/src/core/commands/ads/competitor.md +0 -112
- package/src/core/commands/ads/creative.md +0 -85
- package/src/core/commands/ads/generate.md +0 -238
- package/src/core/commands/ads/google.md +0 -112
- package/src/core/commands/ads/health.md +0 -327
- package/src/core/commands/ads/landing.md +0 -119
- package/src/core/commands/ads/linkedin.md +0 -112
- package/src/core/commands/ads/meta.md +0 -91
- package/src/core/commands/ads/microsoft.md +0 -115
- package/src/core/commands/ads/plan.md +0 -321
- package/src/core/commands/ads/test-plan.md +0 -317
- package/src/core/commands/ads/tiktok.md +0 -129
- package/src/core/commands/ads/track.md +0 -288
- package/src/core/commands/ads/youtube.md +0 -124
- package/src/core/commands/ads.md +0 -140
- package/src/core/commands/agent.md +0 -256
- package/src/core/commands/api.md +0 -267
- package/src/core/commands/assign.md +0 -369
- package/src/core/commands/audit.md +0 -531
- package/src/core/commands/auto.md +0 -556
- package/src/core/commands/automate.md +0 -415
- package/src/core/commands/babysit.md +0 -643
- package/src/core/commands/baseline.md +0 -743
- package/src/core/commands/batch.md +0 -551
- package/src/core/commands/blockers.md +0 -602
- package/src/core/commands/board.md +0 -509
- package/src/core/commands/browser-qa.md +0 -240
- package/src/core/commands/changelog.md +0 -582
- package/src/core/commands/choose.md +0 -430
- package/src/core/commands/ci.md +0 -330
- package/src/core/commands/code/accessibility.md +0 -363
- package/src/core/commands/code/api.md +0 -313
- package/src/core/commands/code/architecture.md +0 -313
- package/src/core/commands/code/completeness.md +0 -519
- package/src/core/commands/code/legal.md +0 -509
- package/src/core/commands/code/logic.md +0 -432
- package/src/core/commands/code/performance.md +0 -506
- package/src/core/commands/code/security.md +0 -509
- package/src/core/commands/code/test.md +0 -505
- package/src/core/commands/compress.md +0 -408
- package/src/core/commands/configure.md +0 -1159
- package/src/core/commands/context/export.md +0 -296
- package/src/core/commands/context/full.md +0 -353
- package/src/core/commands/context/note.md +0 -380
- package/src/core/commands/council.md +0 -592
- package/src/core/commands/debt.md +0 -491
- package/src/core/commands/deploy.md +0 -864
- package/src/core/commands/deps.md +0 -728
- package/src/core/commands/diagnose.md +0 -404
- package/src/core/commands/docs.md +0 -469
- package/src/core/commands/epic/edit.md +0 -213
- package/src/core/commands/epic/list.md +0 -190
- package/src/core/commands/epic/view.md +0 -267
- package/src/core/commands/epic.md +0 -477
- package/src/core/commands/export.md +0 -238
- package/src/core/commands/feedback.md +0 -603
- package/src/core/commands/handoff.md +0 -386
- package/src/core/commands/help.md +0 -194
- package/src/core/commands/ideate/brief.md +0 -363
- package/src/core/commands/ideate/discover.md +0 -399
- package/src/core/commands/ideate/features.md +0 -497
- package/src/core/commands/ideate/history.md +0 -403
- package/src/core/commands/ideate/new.md +0 -900
- package/src/core/commands/impact.md +0 -407
- package/src/core/commands/install.md +0 -529
- package/src/core/commands/learn/explain.md +0 -118
- package/src/core/commands/learn/glossary.md +0 -135
- package/src/core/commands/learn/patterns.md +0 -138
- package/src/core/commands/learn/tour.md +0 -126
- package/src/core/commands/maintain.md +0 -558
- package/src/core/commands/metrics.md +0 -844
- package/src/core/commands/migrate/codemods.md +0 -151
- package/src/core/commands/migrate/plan.md +0 -131
- package/src/core/commands/migrate/scan.md +0 -114
- package/src/core/commands/migrate/validate.md +0 -119
- package/src/core/commands/multi-expert.md +0 -447
- package/src/core/commands/packages.md +0 -535
- package/src/core/commands/pr.md +0 -337
- package/src/core/commands/readme-sync.md +0 -329
- package/src/core/commands/research/analyze.md +0 -798
- package/src/core/commands/research/ask.md +0 -864
- package/src/core/commands/research/import.md +0 -1025
- package/src/core/commands/research/list.md +0 -273
- package/src/core/commands/research/synthesize.md +0 -928
- package/src/core/commands/research/view.md +0 -323
- package/src/core/commands/retro.md +0 -795
- package/src/core/commands/review.md +0 -694
- package/src/core/commands/rlm.md +0 -446
- package/src/core/commands/roadmap/analyze.md +0 -400
- package/src/core/commands/rpi.md +0 -633
- package/src/core/commands/seo/audit.md +0 -444
- package/src/core/commands/seo/competitor.md +0 -174
- package/src/core/commands/seo/content.md +0 -107
- package/src/core/commands/seo/geo.md +0 -229
- package/src/core/commands/seo/hreflang.md +0 -140
- package/src/core/commands/seo/images.md +0 -96
- package/src/core/commands/seo/page.md +0 -198
- package/src/core/commands/seo/plan.md +0 -163
- package/src/core/commands/seo/programmatic.md +0 -131
- package/src/core/commands/seo/references/cwv-thresholds.md +0 -64
- package/src/core/commands/seo/references/eeat-framework.md +0 -110
- package/src/core/commands/seo/references/quality-gates.md +0 -91
- package/src/core/commands/seo/references/schema-types.md +0 -102
- package/src/core/commands/seo/schema.md +0 -183
- package/src/core/commands/seo/sitemap.md +0 -97
- package/src/core/commands/seo/technical.md +0 -100
- package/src/core/commands/seo.md +0 -107
- package/src/core/commands/session/cleanup.md +0 -452
- package/src/core/commands/session/end.md +0 -865
- package/src/core/commands/session/history.md +0 -293
- package/src/core/commands/session/init.md +0 -210
- package/src/core/commands/session/new.md +0 -827
- package/src/core/commands/session/resume.md +0 -291
- package/src/core/commands/session/spawn.md +0 -205
- package/src/core/commands/session/status.md +0 -274
- package/src/core/commands/skill/list.md +0 -139
- package/src/core/commands/skill/recommend.md +0 -216
- package/src/core/commands/sprint.md +0 -714
- package/src/core/commands/status/undo.md +0 -191
- package/src/core/commands/status.md +0 -423
- package/src/core/commands/story/edit.md +0 -204
- package/src/core/commands/story/list.md +0 -199
- package/src/core/commands/story/view.md +0 -312
- package/src/core/commands/story-validate.md +0 -491
- package/src/core/commands/story.md +0 -465
- package/src/core/commands/tdd-next.md +0 -238
- package/src/core/commands/tdd.md +0 -211
- package/src/core/commands/team/guide.md +0 -688
- package/src/core/commands/team/list.md +0 -59
- package/src/core/commands/team/start.md +0 -130
- package/src/core/commands/team/status.md +0 -66
- package/src/core/commands/team/stop.md +0 -78
- package/src/core/commands/template.md +0 -644
- package/src/core/commands/tests.md +0 -731
- package/src/core/commands/update.md +0 -591
- package/src/core/commands/validate-expertise.md +0 -305
- package/src/core/commands/velocity.md +0 -630
- package/src/core/commands/verify.md +0 -534
- package/src/core/commands/whats-new.md +0 -201
- package/src/core/commands/workflow.md +0 -449
- package/src/core/council/sessions/.gitkeep +0 -0
- package/src/core/council/shared_reasoning.template.md +0 -106
- package/src/core/experts/README.md +0 -236
- package/src/core/experts/_core-expertise.yaml +0 -105
- package/src/core/experts/accessibility/expertise.yaml +0 -115
- package/src/core/experts/accessibility/question.md +0 -41
- package/src/core/experts/accessibility/self-improve.md +0 -45
- package/src/core/experts/accessibility/workflow.md +0 -59
- package/src/core/experts/adr-writer/expertise.yaml +0 -138
- package/src/core/experts/adr-writer/question.md +0 -56
- package/src/core/experts/adr-writer/self-improve.md +0 -106
- package/src/core/experts/adr-writer/workflow.md +0 -184
- package/src/core/experts/analytics/expertise.yaml +0 -119
- package/src/core/experts/analytics/question.md +0 -74
- package/src/core/experts/analytics/self-improve.md +0 -163
- package/src/core/experts/analytics/workflow.md +0 -272
- package/src/core/experts/api/expertise.yaml +0 -124
- package/src/core/experts/api/question.md +0 -74
- package/src/core/experts/api/self-improve.md +0 -122
- package/src/core/experts/api/workflow.md +0 -248
- package/src/core/experts/ci/expertise.yaml +0 -106
- package/src/core/experts/ci/question.md +0 -69
- package/src/core/experts/ci/self-improve.md +0 -100
- package/src/core/experts/ci/workflow.md +0 -145
- package/src/core/experts/codebase-query/expertise.yaml +0 -121
- package/src/core/experts/codebase-query/question.md +0 -73
- package/src/core/experts/codebase-query/self-improve.md +0 -105
- package/src/core/experts/compliance/expertise.yaml +0 -101
- package/src/core/experts/compliance/question.md +0 -56
- package/src/core/experts/compliance/self-improve.md +0 -106
- package/src/core/experts/compliance/workflow.md +0 -184
- package/src/core/experts/database/expertise.yaml +0 -109
- package/src/core/experts/database/question.md +0 -74
- package/src/core/experts/database/self-improve.md +0 -121
- package/src/core/experts/database/workflow.md +0 -234
- package/src/core/experts/datamigration/expertise.yaml +0 -141
- package/src/core/experts/datamigration/question.md +0 -56
- package/src/core/experts/datamigration/self-improve.md +0 -106
- package/src/core/experts/datamigration/workflow.md +0 -184
- package/src/core/experts/design/expertise.yaml +0 -116
- package/src/core/experts/design/question.md +0 -56
- package/src/core/experts/design/self-improve.md +0 -106
- package/src/core/experts/design/workflow.md +0 -184
- package/src/core/experts/devops/expertise.yaml +0 -116
- package/src/core/experts/devops/question.md +0 -68
- package/src/core/experts/devops/self-improve.md +0 -102
- package/src/core/experts/devops/workflow.md +0 -142
- package/src/core/experts/documentation/expertise.yaml +0 -126
- package/src/core/experts/documentation/question.md +0 -41
- package/src/core/experts/documentation/self-improve.md +0 -45
- package/src/core/experts/documentation/workflow.md +0 -55
- package/src/core/experts/epic-planner/expertise.yaml +0 -144
- package/src/core/experts/epic-planner/question.md +0 -56
- package/src/core/experts/epic-planner/self-improve.md +0 -106
- package/src/core/experts/epic-planner/workflow.md +0 -184
- package/src/core/experts/integrations/expertise.yaml +0 -113
- package/src/core/experts/integrations/question.md +0 -74
- package/src/core/experts/integrations/self-improve.md +0 -151
- package/src/core/experts/integrations/workflow.md +0 -246
- package/src/core/experts/mentor/expertise.yaml +0 -125
- package/src/core/experts/mentor/question.md +0 -56
- package/src/core/experts/mentor/self-improve.md +0 -106
- package/src/core/experts/mentor/workflow.md +0 -184
- package/src/core/experts/mobile/expertise.yaml +0 -136
- package/src/core/experts/mobile/question.md +0 -72
- package/src/core/experts/mobile/self-improve.md +0 -140
- package/src/core/experts/mobile/workflow.md +0 -240
- package/src/core/experts/monitoring/expertise.yaml +0 -132
- package/src/core/experts/monitoring/question.md +0 -76
- package/src/core/experts/monitoring/self-improve.md +0 -150
- package/src/core/experts/monitoring/workflow.md +0 -264
- package/src/core/experts/performance/expertise.yaml +0 -68
- package/src/core/experts/performance/question.md +0 -41
- package/src/core/experts/performance/self-improve.md +0 -45
- package/src/core/experts/performance/workflow.md +0 -61
- package/src/core/experts/product/expertise.yaml +0 -143
- package/src/core/experts/product/question.md +0 -56
- package/src/core/experts/product/self-improve.md +0 -106
- package/src/core/experts/product/workflow.md +0 -184
- package/src/core/experts/qa/expertise.yaml +0 -110
- package/src/core/experts/qa/question.md +0 -56
- package/src/core/experts/qa/self-improve.md +0 -106
- package/src/core/experts/qa/workflow.md +0 -184
- package/src/core/experts/readme-updater/expertise.yaml +0 -141
- package/src/core/experts/readme-updater/question.md +0 -56
- package/src/core/experts/readme-updater/self-improve.md +0 -106
- package/src/core/experts/readme-updater/workflow.md +0 -184
- package/src/core/experts/refactor/expertise.yaml +0 -135
- package/src/core/experts/refactor/question.md +0 -41
- package/src/core/experts/refactor/self-improve.md +0 -45
- package/src/core/experts/refactor/workflow.md +0 -57
- package/src/core/experts/research/expertise.yaml +0 -143
- package/src/core/experts/research/question.md +0 -56
- package/src/core/experts/research/self-improve.md +0 -106
- package/src/core/experts/research/workflow.md +0 -184
- package/src/core/experts/security/expertise.yaml +0 -117
- package/src/core/experts/security/question.md +0 -77
- package/src/core/experts/security/self-improve.md +0 -102
- package/src/core/experts/security/workflow.md +0 -152
- package/src/core/experts/templates/expertise-template.yaml +0 -67
- package/src/core/experts/templates/question-template.md +0 -56
- package/src/core/experts/templates/self-improve-template.md +0 -106
- package/src/core/experts/templates/workflow-template.md +0 -184
- package/src/core/experts/testing/expertise.yaml +0 -112
- package/src/core/experts/testing/question.md +0 -68
- package/src/core/experts/testing/self-improve.md +0 -102
- package/src/core/experts/testing/workflow.md +0 -143
- package/src/core/experts/ui/expertise.yaml +0 -133
- package/src/core/experts/ui/question.md +0 -74
- package/src/core/experts/ui/self-improve.md +0 -122
- package/src/core/experts/ui/workflow.md +0 -262
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +0 -424
- package/src/core/knowledge/ads/ad-optimization-logic.md +0 -590
- package/src/core/knowledge/ads/ad-technical-specifications.md +0 -385
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +0 -506
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +0 -445
- package/src/core/profiles/COMPARISON.md +0 -170
- package/src/core/profiles/README.md +0 -178
- package/src/core/profiles/claude-code.yaml +0 -111
- package/src/core/profiles/codex.yaml +0 -103
- package/src/core/profiles/cursor.yaml +0 -134
- package/src/core/profiles/examples.js +0 -250
- package/src/core/profiles/loader.js +0 -235
- package/src/core/profiles/windsurf.yaml +0 -159
- package/src/core/skills/_learnings/README.md +0 -91
- package/src/core/skills/_learnings/_template.yaml +0 -106
- package/src/core/skills/_learnings/code-review.yaml +0 -118
- package/src/core/skills/_learnings/commit.yaml +0 -69
- package/src/core/skills/_learnings/story-writer.yaml +0 -71
- package/src/core/teams/backend.json +0 -41
- package/src/core/teams/builder-validator.json +0 -51
- package/src/core/teams/code-review.json +0 -41
- package/src/core/teams/frontend.json +0 -41
- package/src/core/teams/fullstack.json +0 -41
- package/src/core/teams/logic-audit.json +0 -53
- package/src/core/teams/perf-audit.json +0 -71
- package/src/core/teams/qa.json +0 -41
- package/src/core/teams/security-audit.json +0 -71
- package/src/core/teams/solo.json +0 -35
- package/src/core/teams/test-audit.json +0 -71
- package/src/core/templates/CONTEXT.md.example +0 -49
- package/src/core/templates/README-template.md +0 -16
- package/src/core/templates/adr-template.md +0 -28
- package/src/core/templates/agent-coordination-pattern.md +0 -38
- package/src/core/templates/agent-profile-template.md +0 -51
- package/src/core/templates/agileflow-metadata.json +0 -150
- package/src/core/templates/browser-qa-spec.yaml +0 -94
- package/src/core/templates/ci-workflow.yml +0 -74
- package/src/core/templates/claude-settings.advanced.example.json +0 -75
- package/src/core/templates/claude-settings.example.json +0 -26
- package/src/core/templates/command-documentation.md +0 -187
- package/src/core/templates/command-prerequisites.yaml +0 -169
- package/src/core/templates/comms-note-template.md +0 -24
- package/src/core/templates/damage-control-patterns.yaml +0 -243
- package/src/core/templates/environment.json +0 -18
- package/src/core/templates/epic-template.md +0 -27
- package/src/core/templates/plan-template.md +0 -125
- package/src/core/templates/preserve-rules-common.md +0 -107
- package/src/core/templates/preserve-rules.json +0 -42
- package/src/core/templates/proactive-action-spec.md +0 -29
- package/src/core/templates/product-brief.md +0 -136
- package/src/core/templates/quality-gate-priorities.md +0 -34
- package/src/core/templates/research-template.md +0 -44
- package/src/core/templates/session-harness-protocol.md +0 -128
- package/src/core/templates/session-state.json +0 -56
- package/src/core/templates/story-lifecycle.md +0 -213
- package/src/core/templates/story-template.md +0 -92
- package/src/core/templates/tdd-test-template.js +0 -241
- package/src/core/templates/worktrees-guide.md +0 -231
- package/tools/agileflow-npx.js +0 -52
- package/tools/cli/agileflow-cli.js +0 -72
- package/tools/cli/commands/config.js +0 -285
- package/tools/cli/commands/doctor.js +0 -496
- package/tools/cli/commands/list.js +0 -385
- package/tools/cli/commands/session.js +0 -1176
- package/tools/cli/commands/setup.js +0 -255
- package/tools/cli/commands/status.js +0 -101
- package/tools/cli/commands/tui.js +0 -56
- package/tools/cli/commands/uninstall.js +0 -155
- package/tools/cli/commands/update.js +0 -299
- package/tools/cli/installers/core/installer.js +0 -892
- package/tools/cli/installers/ide/_base-ide.js +0 -518
- package/tools/cli/installers/ide/_interface.js +0 -238
- package/tools/cli/installers/ide/claude-code.js +0 -432
- package/tools/cli/installers/ide/codex.js +0 -426
- package/tools/cli/installers/ide/cursor.js +0 -217
- package/tools/cli/installers/ide/manager.js +0 -222
- package/tools/cli/installers/ide/windsurf.js +0 -282
- package/tools/cli/lib/command-context.js +0 -382
- package/tools/cli/lib/config-manager.js +0 -446
- package/tools/cli/lib/content-injector.js +0 -969
- package/tools/cli/lib/content-transformer.js +0 -496
- package/tools/cli/lib/docs-setup.js +0 -464
- package/tools/cli/lib/error-handler.js +0 -165
- package/tools/cli/lib/ide-error-factory.js +0 -421
- package/tools/cli/lib/ide-errors.js +0 -367
- package/tools/cli/lib/ide-generator.js +0 -357
- package/tools/cli/lib/ide-health-monitor.js +0 -364
- package/tools/cli/lib/ide-registry.js +0 -297
- package/tools/cli/lib/npm-utils.js +0 -103
- package/tools/cli/lib/self-update.js +0 -148
- package/tools/cli/lib/ui.js +0 -211
- package/tools/cli/lib/utils.js +0 -87
- package/tools/cli/lib/validation-middleware.js +0 -491
- package/tools/cli/lib/version-checker.js +0 -95
- package/tools/postinstall.js +0 -190
|
@@ -1,1205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ideation-index.js - Ideation History & Deduplication System
|
|
3
|
-
*
|
|
4
|
-
* Provides persistent idea tracking, deduplication, and history querying
|
|
5
|
-
* for the /agileflow:ideate:new command. Tracks all generated ideas with unique IDs,
|
|
6
|
-
* detects duplicates across reports, and provides status tracking.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* const {
|
|
10
|
-
* loadIdeationIndex,
|
|
11
|
-
* saveIdeationIndex,
|
|
12
|
-
* addIdeaToIndex,
|
|
13
|
-
* findDuplicates,
|
|
14
|
-
* updateIdeaStatus,
|
|
15
|
-
* getIdeasByStatus,
|
|
16
|
-
* getRecurringIdeas,
|
|
17
|
-
* } = require('./lib/ideation-index');
|
|
18
|
-
*
|
|
19
|
-
* // Load or create index
|
|
20
|
-
* const index = loadIdeationIndex(rootDir);
|
|
21
|
-
*
|
|
22
|
-
* // Add a new idea
|
|
23
|
-
* const result = addIdeaToIndex(index, idea, 'ideation-20260130.md');
|
|
24
|
-
*
|
|
25
|
-
* // Find duplicates
|
|
26
|
-
* const duplicates = findDuplicates(index, idea);
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
const fs = require('fs');
|
|
30
|
-
const path = require('path');
|
|
31
|
-
const crypto = require('crypto');
|
|
32
|
-
const { tryOptional } = require('../../lib/errors');
|
|
33
|
-
|
|
34
|
-
// Default index file location
|
|
35
|
-
const DEFAULT_INDEX_PATH = 'docs/00-meta/ideation-index.json';
|
|
36
|
-
|
|
37
|
-
// Similarity threshold for duplicate detection (0-1)
|
|
38
|
-
const SIMILARITY_THRESHOLD = 0.75;
|
|
39
|
-
|
|
40
|
-
// Schema version for migrations
|
|
41
|
-
const SCHEMA_VERSION = '1.0.0';
|
|
42
|
-
|
|
43
|
-
// ============================================================================
|
|
44
|
-
// SCHEMA & DEFAULTS
|
|
45
|
-
// ============================================================================
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create a new empty ideation index with default structure
|
|
49
|
-
* @returns {object} Empty index structure
|
|
50
|
-
*/
|
|
51
|
-
function createEmptyIndex() {
|
|
52
|
-
return {
|
|
53
|
-
schema_version: SCHEMA_VERSION,
|
|
54
|
-
updated: new Date().toISOString(),
|
|
55
|
-
ideas: {},
|
|
56
|
-
reports: {},
|
|
57
|
-
next_id: 1,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Validate index structure and migrate if needed
|
|
63
|
-
* @param {object} data - Raw index data
|
|
64
|
-
* @returns {object} Validated/migrated index
|
|
65
|
-
*/
|
|
66
|
-
function validateAndMigrateIndex(data) {
|
|
67
|
-
if (!data || typeof data !== 'object') {
|
|
68
|
-
return createEmptyIndex();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Ensure required fields exist
|
|
72
|
-
const index = {
|
|
73
|
-
schema_version: data.schema_version || SCHEMA_VERSION,
|
|
74
|
-
updated: data.updated || new Date().toISOString(),
|
|
75
|
-
ideas: data.ideas || {},
|
|
76
|
-
reports: data.reports || {},
|
|
77
|
-
next_id: data.next_id || 1,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Future: Add migration logic for schema version upgrades
|
|
81
|
-
|
|
82
|
-
return index;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ============================================================================
|
|
86
|
-
// FILE I/O
|
|
87
|
-
// ============================================================================
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get the path to the ideation index file
|
|
91
|
-
* @param {string} rootDir - Project root directory
|
|
92
|
-
* @returns {string} Full path to index file
|
|
93
|
-
*/
|
|
94
|
-
function getIndexPath(rootDir) {
|
|
95
|
-
return path.join(rootDir, DEFAULT_INDEX_PATH);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Load ideation index from disk
|
|
100
|
-
* @param {string} rootDir - Project root directory
|
|
101
|
-
* @returns {{ ok: boolean, data?: object, error?: string, created?: boolean }}
|
|
102
|
-
*/
|
|
103
|
-
function loadIdeationIndex(rootDir) {
|
|
104
|
-
const indexPath = getIndexPath(rootDir);
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
if (!fs.existsSync(indexPath)) {
|
|
108
|
-
// Create new index
|
|
109
|
-
const newIndex = createEmptyIndex();
|
|
110
|
-
return { ok: true, data: newIndex, created: true };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const content = fs.readFileSync(indexPath, 'utf8');
|
|
114
|
-
if (!content.trim()) {
|
|
115
|
-
const newIndex = createEmptyIndex();
|
|
116
|
-
return { ok: true, data: newIndex, created: true };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const data = JSON.parse(content);
|
|
120
|
-
const index = validateAndMigrateIndex(data);
|
|
121
|
-
return { ok: true, data: index };
|
|
122
|
-
} catch (err) {
|
|
123
|
-
return { ok: false, error: `Failed to load ideation index: ${err.message}` };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Save ideation index to disk with atomic write
|
|
129
|
-
* @param {string} rootDir - Project root directory
|
|
130
|
-
* @param {object} index - Index data to save
|
|
131
|
-
* @returns {{ ok: boolean, error?: string }}
|
|
132
|
-
*/
|
|
133
|
-
function saveIdeationIndex(rootDir, index) {
|
|
134
|
-
const indexPath = getIndexPath(rootDir);
|
|
135
|
-
const tempPath = `${indexPath}.tmp`;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
// Ensure directory exists
|
|
139
|
-
const dir = path.dirname(indexPath);
|
|
140
|
-
if (!fs.existsSync(dir)) {
|
|
141
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Update timestamp
|
|
145
|
-
index.updated = new Date().toISOString();
|
|
146
|
-
|
|
147
|
-
// Atomic write: write to temp file, then rename
|
|
148
|
-
const content = JSON.stringify(index, null, 2) + '\n';
|
|
149
|
-
fs.writeFileSync(tempPath, content);
|
|
150
|
-
fs.renameSync(tempPath, indexPath);
|
|
151
|
-
|
|
152
|
-
return { ok: true };
|
|
153
|
-
} catch (err) {
|
|
154
|
-
// Clean up temp file if it exists
|
|
155
|
-
tryOptional(() => fs.unlinkSync(tempPath), 'cleanup temp');
|
|
156
|
-
return { ok: false, error: `Failed to save ideation index: ${err.message}` };
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ============================================================================
|
|
161
|
-
// FINGERPRINTING & SIMILARITY
|
|
162
|
-
// ============================================================================
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Normalize a string for comparison (lowercase, remove punctuation, collapse whitespace)
|
|
166
|
-
* @param {string} str - String to normalize
|
|
167
|
-
* @returns {string} Normalized string
|
|
168
|
-
*/
|
|
169
|
-
function normalizeString(str) {
|
|
170
|
-
if (!str || typeof str !== 'string') return '';
|
|
171
|
-
return str
|
|
172
|
-
.toLowerCase()
|
|
173
|
-
.replace(/[^\w\s]/g, ' ')
|
|
174
|
-
.replace(/\s+/g, ' ')
|
|
175
|
-
.trim();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Normalize file paths for comparison
|
|
180
|
-
* @param {string[]} files - Array of file paths
|
|
181
|
-
* @returns {string[]} Sorted, normalized file paths
|
|
182
|
-
*/
|
|
183
|
-
function normalizeFiles(files) {
|
|
184
|
-
if (!Array.isArray(files)) return [];
|
|
185
|
-
return files
|
|
186
|
-
.map(f => (typeof f === 'string' ? f.replace(/^[`'"]+|[`'"]+$/g, '').trim() : ''))
|
|
187
|
-
.filter(Boolean)
|
|
188
|
-
.sort();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Generate a fingerprint (SHA256 hash) for an idea
|
|
193
|
-
* @param {string} title - Idea title
|
|
194
|
-
* @param {string[]} files - Files affected
|
|
195
|
-
* @returns {string} Hex fingerprint
|
|
196
|
-
*/
|
|
197
|
-
function generateIdeaFingerprint(title, files = []) {
|
|
198
|
-
const normalizedTitle = normalizeString(title);
|
|
199
|
-
const normalizedFiles = normalizeFiles(files);
|
|
200
|
-
const data = `${normalizedTitle}|${normalizedFiles.join(',')}`;
|
|
201
|
-
return crypto.createHash('sha256').update(data).digest('hex').substring(0, 16);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Calculate Levenshtein distance between two strings
|
|
206
|
-
* @param {string} a - First string
|
|
207
|
-
* @param {string} b - Second string
|
|
208
|
-
* @returns {number} Edit distance
|
|
209
|
-
*/
|
|
210
|
-
function levenshteinDistance(a, b) {
|
|
211
|
-
if (a.length === 0) return b.length;
|
|
212
|
-
if (b.length === 0) return a.length;
|
|
213
|
-
|
|
214
|
-
const matrix = [];
|
|
215
|
-
|
|
216
|
-
for (let i = 0; i <= b.length; i++) {
|
|
217
|
-
matrix[i] = [i];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
for (let j = 0; j <= a.length; j++) {
|
|
221
|
-
matrix[0][j] = j;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
for (let i = 1; i <= b.length; i++) {
|
|
225
|
-
for (let j = 1; j <= a.length; j++) {
|
|
226
|
-
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
227
|
-
matrix[i][j] = matrix[i - 1][j - 1];
|
|
228
|
-
} else {
|
|
229
|
-
matrix[i][j] = Math.min(
|
|
230
|
-
matrix[i - 1][j - 1] + 1, // substitution
|
|
231
|
-
matrix[i][j - 1] + 1, // insertion
|
|
232
|
-
matrix[i - 1][j] + 1 // deletion
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return matrix[b.length][a.length];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Calculate similarity between two strings (0-1, higher is more similar)
|
|
243
|
-
* @param {string} a - First string
|
|
244
|
-
* @param {string} b - Second string
|
|
245
|
-
* @returns {number} Similarity score (0-1)
|
|
246
|
-
*/
|
|
247
|
-
function stringSimilarity(a, b) {
|
|
248
|
-
const normA = normalizeString(a);
|
|
249
|
-
const normB = normalizeString(b);
|
|
250
|
-
|
|
251
|
-
if (normA === normB) return 1;
|
|
252
|
-
if (!normA || !normB) return 0;
|
|
253
|
-
|
|
254
|
-
const maxLen = Math.max(normA.length, normB.length);
|
|
255
|
-
if (maxLen === 0) return 1;
|
|
256
|
-
|
|
257
|
-
const distance = levenshteinDistance(normA, normB);
|
|
258
|
-
return 1 - distance / maxLen;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Calculate file overlap between two sets of files
|
|
263
|
-
* @param {string[]} files1 - First file set
|
|
264
|
-
* @param {string[]} files2 - Second file set
|
|
265
|
-
* @returns {number} Overlap ratio (0-1)
|
|
266
|
-
*/
|
|
267
|
-
function fileOverlap(files1, files2) {
|
|
268
|
-
const norm1 = normalizeFiles(files1);
|
|
269
|
-
const norm2 = normalizeFiles(files2);
|
|
270
|
-
|
|
271
|
-
if (norm1.length === 0 && norm2.length === 0) return 0;
|
|
272
|
-
if (norm1.length === 0 || norm2.length === 0) return 0;
|
|
273
|
-
|
|
274
|
-
const set1 = new Set(norm1);
|
|
275
|
-
const intersection = norm2.filter(f => set1.has(f)).length;
|
|
276
|
-
const union = new Set([...norm1, ...norm2]).size;
|
|
277
|
-
|
|
278
|
-
return union > 0 ? intersection / union : 0;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Calculate combined similarity score for two ideas
|
|
283
|
-
* @param {object} idea1 - First idea
|
|
284
|
-
* @param {object} idea2 - Second idea
|
|
285
|
-
* @returns {{ score: number, titleSimilarity: number, fileOverlap: number }}
|
|
286
|
-
*/
|
|
287
|
-
function calculateIdeaSimilarity(idea1, idea2) {
|
|
288
|
-
const titleSim = stringSimilarity(idea1.title || '', idea2.title || '');
|
|
289
|
-
const fileSim = fileOverlap(idea1.files || [], idea2.files || []);
|
|
290
|
-
|
|
291
|
-
// Weighted combination: title matters more (70%), files (30%)
|
|
292
|
-
const score = titleSim * 0.7 + fileSim * 0.3;
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
score,
|
|
296
|
-
titleSimilarity: titleSim,
|
|
297
|
-
fileOverlap: fileSim,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// ============================================================================
|
|
302
|
-
// IDEA MANAGEMENT
|
|
303
|
-
// ============================================================================
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Find duplicate ideas in the index
|
|
307
|
-
* @param {object} index - Ideation index
|
|
308
|
-
* @param {object} idea - Idea to check { title, files }
|
|
309
|
-
* @param {object} options - Options
|
|
310
|
-
* @param {number} options.threshold - Similarity threshold (default: 0.75)
|
|
311
|
-
* @returns {Array<{ id: string, idea: object, similarity: object }>}
|
|
312
|
-
*/
|
|
313
|
-
function findDuplicates(index, idea, options = {}) {
|
|
314
|
-
const { threshold = SIMILARITY_THRESHOLD } = options;
|
|
315
|
-
const duplicates = [];
|
|
316
|
-
|
|
317
|
-
// First check fingerprint for exact matches
|
|
318
|
-
const fingerprint = generateIdeaFingerprint(idea.title, idea.files);
|
|
319
|
-
|
|
320
|
-
for (const [id, existing] of Object.entries(index.ideas || {})) {
|
|
321
|
-
// Skip if fingerprint matches (exact duplicate)
|
|
322
|
-
if (existing.fingerprint === fingerprint) {
|
|
323
|
-
duplicates.push({
|
|
324
|
-
id,
|
|
325
|
-
idea: existing,
|
|
326
|
-
similarity: { score: 1, titleSimilarity: 1, fileOverlap: 1 },
|
|
327
|
-
exact: true,
|
|
328
|
-
});
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Calculate similarity
|
|
333
|
-
const similarity = calculateIdeaSimilarity(idea, existing);
|
|
334
|
-
if (similarity.score >= threshold) {
|
|
335
|
-
duplicates.push({
|
|
336
|
-
id,
|
|
337
|
-
idea: existing,
|
|
338
|
-
similarity,
|
|
339
|
-
exact: false,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Sort by similarity score descending
|
|
345
|
-
duplicates.sort((a, b) => b.similarity.score - a.similarity.score);
|
|
346
|
-
|
|
347
|
-
return duplicates;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Add an idea to the index
|
|
352
|
-
* @param {object} index - Ideation index
|
|
353
|
-
* @param {object} idea - Idea to add
|
|
354
|
-
* @param {string} idea.title - Idea title
|
|
355
|
-
* @param {string} idea.category - Category (Security, Performance, etc.)
|
|
356
|
-
* @param {string[]} idea.files - Files affected
|
|
357
|
-
* @param {string} idea.confidence - Confidence level (HIGH, MEDIUM)
|
|
358
|
-
* @param {string[]} idea.experts - Contributing experts
|
|
359
|
-
* @param {string} reportName - Source report filename
|
|
360
|
-
* @returns {{ ok: boolean, id?: string, duplicate?: object, error?: string }}
|
|
361
|
-
*/
|
|
362
|
-
function addIdeaToIndex(index, idea, reportName) {
|
|
363
|
-
if (!idea || !idea.title) {
|
|
364
|
-
return { ok: false, error: 'Idea title is required' };
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Check for duplicates first
|
|
368
|
-
const duplicates = findDuplicates(index, idea);
|
|
369
|
-
if (duplicates.length > 0 && duplicates[0].similarity.score > 0.9) {
|
|
370
|
-
// This is a recurring idea - add occurrence instead of creating new
|
|
371
|
-
const existing = duplicates[0];
|
|
372
|
-
const existingIdea = index.ideas[existing.id];
|
|
373
|
-
|
|
374
|
-
// Add new occurrence
|
|
375
|
-
if (!existingIdea.occurrences) {
|
|
376
|
-
existingIdea.occurrences = [];
|
|
377
|
-
}
|
|
378
|
-
existingIdea.occurrences.push({
|
|
379
|
-
report: reportName,
|
|
380
|
-
date: new Date().toISOString().split('T')[0],
|
|
381
|
-
experts: idea.experts || [],
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Update last seen
|
|
385
|
-
existingIdea.last_seen = new Date().toISOString().split('T')[0];
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
ok: true,
|
|
389
|
-
id: existing.id,
|
|
390
|
-
duplicate: existing,
|
|
391
|
-
recurring: true,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Generate new ID
|
|
396
|
-
const id = `IDEA-${String(index.next_id).padStart(4, '0')}`;
|
|
397
|
-
index.next_id++;
|
|
398
|
-
|
|
399
|
-
// Create idea entry
|
|
400
|
-
const now = new Date().toISOString().split('T')[0];
|
|
401
|
-
const newIdea = {
|
|
402
|
-
id,
|
|
403
|
-
title: idea.title,
|
|
404
|
-
title_normalized: normalizeString(idea.title),
|
|
405
|
-
fingerprint: generateIdeaFingerprint(idea.title, idea.files),
|
|
406
|
-
category: idea.category || 'Uncategorized',
|
|
407
|
-
source_report: reportName,
|
|
408
|
-
first_seen: now,
|
|
409
|
-
last_seen: now,
|
|
410
|
-
confidence: idea.confidence || 'MEDIUM',
|
|
411
|
-
files: idea.files || [],
|
|
412
|
-
status: 'pending',
|
|
413
|
-
linked_story: null,
|
|
414
|
-
linked_epic: null,
|
|
415
|
-
occurrences: [
|
|
416
|
-
{
|
|
417
|
-
report: reportName,
|
|
418
|
-
date: now,
|
|
419
|
-
experts: idea.experts || [],
|
|
420
|
-
},
|
|
421
|
-
],
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
// Add to index
|
|
425
|
-
index.ideas[id] = newIdea;
|
|
426
|
-
|
|
427
|
-
// Update report entry
|
|
428
|
-
if (!index.reports[reportName]) {
|
|
429
|
-
index.reports[reportName] = {
|
|
430
|
-
generated: now,
|
|
431
|
-
scope: null,
|
|
432
|
-
depth: null,
|
|
433
|
-
idea_count: 0,
|
|
434
|
-
ideas: [],
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
index.reports[reportName].ideas.push(id);
|
|
438
|
-
index.reports[reportName].idea_count++;
|
|
439
|
-
|
|
440
|
-
return { ok: true, id, recurring: false };
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Update the status of an idea
|
|
445
|
-
* @param {object} index - Ideation index
|
|
446
|
-
* @param {string} ideaId - Idea ID (e.g., 'IDEA-0001')
|
|
447
|
-
* @param {string} status - New status ('pending', 'in-progress', 'implemented', 'rejected')
|
|
448
|
-
* @param {object} options - Additional options
|
|
449
|
-
* @param {string} options.linkedStory - Link to a story (e.g., 'US-0095')
|
|
450
|
-
* @param {string} options.linkedEpic - Link to an epic (e.g., 'EP-0017')
|
|
451
|
-
* @returns {{ ok: boolean, error?: string }}
|
|
452
|
-
*/
|
|
453
|
-
function updateIdeaStatus(index, ideaId, status, options = {}) {
|
|
454
|
-
const idea = index.ideas[ideaId];
|
|
455
|
-
if (!idea) {
|
|
456
|
-
return { ok: false, error: `Idea not found: ${ideaId}` };
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const validStatuses = ['pending', 'in-progress', 'implemented', 'rejected'];
|
|
460
|
-
if (!validStatuses.includes(status)) {
|
|
461
|
-
return {
|
|
462
|
-
ok: false,
|
|
463
|
-
error: `Invalid status: ${status}. Must be one of: ${validStatuses.join(', ')}`,
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
idea.status = status;
|
|
468
|
-
|
|
469
|
-
if (options.linkedStory) {
|
|
470
|
-
idea.linked_story = options.linkedStory;
|
|
471
|
-
}
|
|
472
|
-
if (options.linkedEpic) {
|
|
473
|
-
idea.linked_epic = options.linkedEpic;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return { ok: true };
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// ============================================================================
|
|
480
|
-
// QUERIES
|
|
481
|
-
// ============================================================================
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Get ideas filtered by status
|
|
485
|
-
* @param {object} index - Ideation index
|
|
486
|
-
* @param {string} status - Status to filter by
|
|
487
|
-
* @returns {Array<object>} Matching ideas
|
|
488
|
-
*/
|
|
489
|
-
function getIdeasByStatus(index, status) {
|
|
490
|
-
return Object.values(index.ideas || {}).filter(idea => idea.status === status);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Get recurring ideas (seen 2+ times)
|
|
495
|
-
* @param {object} index - Ideation index
|
|
496
|
-
* @param {object} options - Options
|
|
497
|
-
* @param {boolean} options.excludeImplemented - Exclude implemented ideas
|
|
498
|
-
* @returns {Array<{ idea: object, occurrenceCount: number }>}
|
|
499
|
-
*/
|
|
500
|
-
function getRecurringIdeas(index, options = {}) {
|
|
501
|
-
const { excludeImplemented = true } = options;
|
|
502
|
-
|
|
503
|
-
return Object.values(index.ideas || {})
|
|
504
|
-
.filter(idea => {
|
|
505
|
-
if (excludeImplemented && idea.status === 'implemented') return false;
|
|
506
|
-
return (idea.occurrences || []).length >= 2;
|
|
507
|
-
})
|
|
508
|
-
.map(idea => ({
|
|
509
|
-
idea,
|
|
510
|
-
occurrenceCount: (idea.occurrences || []).length,
|
|
511
|
-
}))
|
|
512
|
-
.sort((a, b) => b.occurrenceCount - a.occurrenceCount);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Get index summary statistics
|
|
517
|
-
* @param {object} index - Ideation index
|
|
518
|
-
* @returns {object} Summary stats
|
|
519
|
-
*/
|
|
520
|
-
function getIndexSummary(index) {
|
|
521
|
-
const ideas = Object.values(index.ideas || {});
|
|
522
|
-
|
|
523
|
-
const byStatus = {
|
|
524
|
-
pending: 0,
|
|
525
|
-
'in-progress': 0,
|
|
526
|
-
implemented: 0,
|
|
527
|
-
rejected: 0,
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const byCategory = {};
|
|
531
|
-
let recurringCount = 0;
|
|
532
|
-
|
|
533
|
-
for (const idea of ideas) {
|
|
534
|
-
// Count by status
|
|
535
|
-
byStatus[idea.status] = (byStatus[idea.status] || 0) + 1;
|
|
536
|
-
|
|
537
|
-
// Count by category
|
|
538
|
-
const cat = idea.category || 'Uncategorized';
|
|
539
|
-
byCategory[cat] = (byCategory[cat] || 0) + 1;
|
|
540
|
-
|
|
541
|
-
// Count recurring
|
|
542
|
-
if ((idea.occurrences || []).length >= 2) {
|
|
543
|
-
recurringCount++;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return {
|
|
548
|
-
totalIdeas: ideas.length,
|
|
549
|
-
totalReports: Object.keys(index.reports || {}).length,
|
|
550
|
-
byStatus,
|
|
551
|
-
byCategory,
|
|
552
|
-
recurringCount,
|
|
553
|
-
lastUpdated: index.updated,
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Get idea by ID
|
|
559
|
-
* @param {object} index - Ideation index
|
|
560
|
-
* @param {string} ideaId - Idea ID
|
|
561
|
-
* @returns {object|null} Idea or null if not found
|
|
562
|
-
*/
|
|
563
|
-
function getIdeaById(index, ideaId) {
|
|
564
|
-
return index.ideas[ideaId] || null;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Search ideas by title keyword
|
|
569
|
-
* @param {object} index - Ideation index
|
|
570
|
-
* @param {string} query - Search query
|
|
571
|
-
* @returns {Array<object>} Matching ideas
|
|
572
|
-
*/
|
|
573
|
-
function searchIdeas(index, query) {
|
|
574
|
-
const normalizedQuery = normalizeString(query);
|
|
575
|
-
if (!normalizedQuery) return [];
|
|
576
|
-
|
|
577
|
-
return Object.values(index.ideas || {}).filter(idea => {
|
|
578
|
-
const normalizedTitle = idea.title_normalized || normalizeString(idea.title);
|
|
579
|
-
return normalizedTitle.includes(normalizedQuery);
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Update report metadata
|
|
585
|
-
* @param {object} index - Ideation index
|
|
586
|
-
* @param {string} reportName - Report filename
|
|
587
|
-
* @param {object} metadata - Metadata to update
|
|
588
|
-
* @returns {{ ok: boolean }}
|
|
589
|
-
*/
|
|
590
|
-
function updateReportMetadata(index, reportName, metadata) {
|
|
591
|
-
if (!index.reports[reportName]) {
|
|
592
|
-
index.reports[reportName] = {
|
|
593
|
-
generated: new Date().toISOString().split('T')[0],
|
|
594
|
-
scope: null,
|
|
595
|
-
depth: null,
|
|
596
|
-
idea_count: 0,
|
|
597
|
-
ideas: [],
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
Object.assign(index.reports[reportName], metadata);
|
|
602
|
-
return { ok: true };
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// ============================================================================
|
|
606
|
-
// TREND ANALYSIS (US-0210)
|
|
607
|
-
// ============================================================================
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Get category hotspots - percentage of recurring ideas per category
|
|
611
|
-
* @param {object} index - Ideation index
|
|
612
|
-
* @returns {Array<{ category: string, total: number, recurring: number, percentage: number }>}
|
|
613
|
-
*/
|
|
614
|
-
function getCategoryHotspots(index) {
|
|
615
|
-
const categories = {};
|
|
616
|
-
|
|
617
|
-
for (const idea of Object.values(index.ideas || {})) {
|
|
618
|
-
const cat = idea.category || 'Uncategorized';
|
|
619
|
-
if (!categories[cat]) {
|
|
620
|
-
categories[cat] = { total: 0, recurring: 0, implemented: 0, pending: 0 };
|
|
621
|
-
}
|
|
622
|
-
categories[cat].total++;
|
|
623
|
-
|
|
624
|
-
const occurrenceCount = (idea.occurrences || []).length;
|
|
625
|
-
if (occurrenceCount >= 2) {
|
|
626
|
-
categories[cat].recurring++;
|
|
627
|
-
}
|
|
628
|
-
if (idea.status === 'implemented') {
|
|
629
|
-
categories[cat].implemented++;
|
|
630
|
-
}
|
|
631
|
-
if (idea.status === 'pending') {
|
|
632
|
-
categories[cat].pending++;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return Object.entries(categories)
|
|
637
|
-
.map(([category, stats]) => ({
|
|
638
|
-
category,
|
|
639
|
-
total: stats.total,
|
|
640
|
-
recurring: stats.recurring,
|
|
641
|
-
implemented: stats.implemented,
|
|
642
|
-
pending: stats.pending,
|
|
643
|
-
recurringPercentage: stats.total > 0 ? Math.round((stats.recurring / stats.total) * 100) : 0,
|
|
644
|
-
implementedPercentage:
|
|
645
|
-
stats.total > 0 ? Math.round((stats.implemented / stats.total) * 100) : 0,
|
|
646
|
-
}))
|
|
647
|
-
.sort((a, b) => b.recurringPercentage - a.recurringPercentage);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Get implementation velocity - ideas resolved per month
|
|
652
|
-
* @param {object} index - Ideation index
|
|
653
|
-
* @returns {{ monthly: Array<{ month: string, implemented: number, new: number }>, averageVelocity: number }}
|
|
654
|
-
*/
|
|
655
|
-
function getImplementationVelocity(index) {
|
|
656
|
-
const monthlyNew = {};
|
|
657
|
-
const monthlyImplemented = {};
|
|
658
|
-
|
|
659
|
-
// Track when ideas were first seen (new)
|
|
660
|
-
for (const idea of Object.values(index.ideas || {})) {
|
|
661
|
-
const firstSeen = idea.first_seen;
|
|
662
|
-
if (firstSeen) {
|
|
663
|
-
const month = firstSeen.substring(0, 7); // YYYY-MM
|
|
664
|
-
monthlyNew[month] = (monthlyNew[month] || 0) + 1;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// Track when ideas were implemented (based on linked story creation)
|
|
668
|
-
// Since we don't have implementation dates, approximate from last_seen when status is implemented
|
|
669
|
-
if (idea.status === 'implemented' && idea.last_seen) {
|
|
670
|
-
const month = idea.last_seen.substring(0, 7);
|
|
671
|
-
monthlyImplemented[month] = (monthlyImplemented[month] || 0) + 1;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Get all months
|
|
676
|
-
const allMonths = new Set([...Object.keys(monthlyNew), ...Object.keys(monthlyImplemented)]);
|
|
677
|
-
const sortedMonths = Array.from(allMonths).sort();
|
|
678
|
-
|
|
679
|
-
const monthly = sortedMonths.map(month => ({
|
|
680
|
-
month,
|
|
681
|
-
new: monthlyNew[month] || 0,
|
|
682
|
-
implemented: monthlyImplemented[month] || 0,
|
|
683
|
-
}));
|
|
684
|
-
|
|
685
|
-
// Calculate average velocity (implemented per month)
|
|
686
|
-
const totalImplemented = Object.values(monthlyImplemented).reduce((a, b) => a + b, 0);
|
|
687
|
-
const numMonths = sortedMonths.length || 1;
|
|
688
|
-
const averageVelocity = Math.round((totalImplemented / numMonths) * 10) / 10;
|
|
689
|
-
|
|
690
|
-
return { monthly, averageVelocity };
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
/**
|
|
694
|
-
* Get stale ideas - ideas that appeared multiple times but never addressed
|
|
695
|
-
* @param {object} index - Ideation index
|
|
696
|
-
* @param {number} minOccurrences - Minimum occurrences to be considered stale (default: 4)
|
|
697
|
-
* @returns {Array<{ idea: object, occurrenceCount: number, staleDays: number }>}
|
|
698
|
-
*/
|
|
699
|
-
function getStaleIdeas(index, minOccurrences = 4) {
|
|
700
|
-
const now = new Date();
|
|
701
|
-
|
|
702
|
-
return Object.values(index.ideas || {})
|
|
703
|
-
.filter(idea => {
|
|
704
|
-
if (idea.status === 'implemented' || idea.status === 'rejected') return false;
|
|
705
|
-
return (idea.occurrences || []).length >= minOccurrences;
|
|
706
|
-
})
|
|
707
|
-
.map(idea => {
|
|
708
|
-
const firstSeen = idea.first_seen ? new Date(idea.first_seen) : now;
|
|
709
|
-
const staleDays = Math.floor((now - firstSeen) / (1000 * 60 * 60 * 24));
|
|
710
|
-
return {
|
|
711
|
-
idea,
|
|
712
|
-
occurrenceCount: (idea.occurrences || []).length,
|
|
713
|
-
staleDays,
|
|
714
|
-
};
|
|
715
|
-
})
|
|
716
|
-
.sort((a, b) => b.occurrenceCount - a.occurrenceCount || b.staleDays - a.staleDays);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* Get expert agreement patterns - which expert pairs agree most often
|
|
721
|
-
* @param {object} index - Ideation index
|
|
722
|
-
* @returns {Array<{ pair: [string, string], agreements: number, ideas: string[] }>}
|
|
723
|
-
*/
|
|
724
|
-
function getExpertAgreementPatterns(index) {
|
|
725
|
-
const pairAgreements = {};
|
|
726
|
-
|
|
727
|
-
for (const idea of Object.values(index.ideas || {})) {
|
|
728
|
-
// Collect all experts across all occurrences
|
|
729
|
-
const allExperts = new Set();
|
|
730
|
-
for (const occ of idea.occurrences || []) {
|
|
731
|
-
for (const expert of occ.experts || []) {
|
|
732
|
-
allExperts.add(expert);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// Generate pairs and track agreements
|
|
737
|
-
const expertList = Array.from(allExperts).sort();
|
|
738
|
-
for (let i = 0; i < expertList.length; i++) {
|
|
739
|
-
for (let j = i + 1; j < expertList.length; j++) {
|
|
740
|
-
const pairKey = `${expertList[i]}|${expertList[j]}`;
|
|
741
|
-
if (!pairAgreements[pairKey]) {
|
|
742
|
-
pairAgreements[pairKey] = { ideas: [] };
|
|
743
|
-
}
|
|
744
|
-
pairAgreements[pairKey].ideas.push(idea.id);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
return Object.entries(pairAgreements)
|
|
750
|
-
.map(([pairKey, data]) => {
|
|
751
|
-
const [expert1, expert2] = pairKey.split('|');
|
|
752
|
-
return {
|
|
753
|
-
pair: [expert1, expert2],
|
|
754
|
-
agreements: data.ideas.length,
|
|
755
|
-
ideas: data.ideas,
|
|
756
|
-
};
|
|
757
|
-
})
|
|
758
|
-
.filter(p => p.agreements >= 2)
|
|
759
|
-
.sort((a, b) => b.agreements - a.agreements);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Generate full trend analysis
|
|
764
|
-
* @param {object} index - Ideation index
|
|
765
|
-
* @returns {object} Complete trend analysis
|
|
766
|
-
*/
|
|
767
|
-
function getTrendAnalysis(index) {
|
|
768
|
-
return {
|
|
769
|
-
categoryHotspots: getCategoryHotspots(index),
|
|
770
|
-
velocity: getImplementationVelocity(index),
|
|
771
|
-
staleIdeas: getStaleIdeas(index, 4),
|
|
772
|
-
expertAgreement: getExpertAgreementPatterns(index),
|
|
773
|
-
summary: getIndexSummary(index),
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// ============================================================================
|
|
778
|
-
// COMPARISON MODE (US-0211)
|
|
779
|
-
// ============================================================================
|
|
780
|
-
|
|
781
|
-
/**
|
|
782
|
-
* Compare two ideation reports and classify ideas
|
|
783
|
-
* @param {object} index - Ideation index
|
|
784
|
-
* @param {string} report1Name - First report name (e.g., 'ideation-20260114.md')
|
|
785
|
-
* @param {string} report2Name - Second report name
|
|
786
|
-
* @returns {{ resolved: array, new: array, persisted: array, dropped: array, error?: string }}
|
|
787
|
-
*/
|
|
788
|
-
function compareReports(index, report1Name, report2Name) {
|
|
789
|
-
// Normalize report names (allow shorthand like '20260114')
|
|
790
|
-
const normalizeReportName = name => {
|
|
791
|
-
if (!name) return null;
|
|
792
|
-
if (name.startsWith('ideation-') && name.endsWith('.md')) return name;
|
|
793
|
-
if (name.match(/^\d{8}$/)) return `ideation-${name}.md`;
|
|
794
|
-
return name.endsWith('.md') ? name : `${name}.md`;
|
|
795
|
-
};
|
|
796
|
-
|
|
797
|
-
const r1 = normalizeReportName(report1Name);
|
|
798
|
-
const r2 = normalizeReportName(report2Name);
|
|
799
|
-
|
|
800
|
-
if (!index.reports[r1]) {
|
|
801
|
-
return {
|
|
802
|
-
error: `Report not found: ${r1}. Available: ${Object.keys(index.reports).join(', ')}`,
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
if (!index.reports[r2]) {
|
|
806
|
-
return {
|
|
807
|
-
error: `Report not found: ${r2}. Available: ${Object.keys(index.reports).join(', ')}`,
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
const report1Ideas = new Set(index.reports[r1].ideas || []);
|
|
812
|
-
const report2Ideas = new Set(index.reports[r2].ideas || []);
|
|
813
|
-
|
|
814
|
-
const resolved = []; // In report1, now implemented
|
|
815
|
-
const newIdeas = []; // Only in report2
|
|
816
|
-
const persisted = []; // In both reports
|
|
817
|
-
const dropped = []; // Only in report1, not recurring
|
|
818
|
-
|
|
819
|
-
// Analyze report1 ideas
|
|
820
|
-
for (const ideaId of report1Ideas) {
|
|
821
|
-
const idea = index.ideas[ideaId];
|
|
822
|
-
if (!idea) continue;
|
|
823
|
-
|
|
824
|
-
if (report2Ideas.has(ideaId)) {
|
|
825
|
-
// In both - persisted
|
|
826
|
-
persisted.push({
|
|
827
|
-
id: ideaId,
|
|
828
|
-
title: idea.title,
|
|
829
|
-
category: idea.category,
|
|
830
|
-
status: idea.status,
|
|
831
|
-
occurrenceCount: (idea.occurrences || []).length,
|
|
832
|
-
});
|
|
833
|
-
} else if (idea.status === 'implemented') {
|
|
834
|
-
// Only in report1, now implemented - resolved
|
|
835
|
-
resolved.push({
|
|
836
|
-
id: ideaId,
|
|
837
|
-
title: idea.title,
|
|
838
|
-
category: idea.category,
|
|
839
|
-
linkedStory: idea.linked_story,
|
|
840
|
-
linkedEpic: idea.linked_epic,
|
|
841
|
-
});
|
|
842
|
-
} else {
|
|
843
|
-
// Only in report1, not implemented, didn't recur - dropped
|
|
844
|
-
dropped.push({
|
|
845
|
-
id: ideaId,
|
|
846
|
-
title: idea.title,
|
|
847
|
-
category: idea.category,
|
|
848
|
-
status: idea.status,
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
// Analyze report2-only ideas
|
|
854
|
-
for (const ideaId of report2Ideas) {
|
|
855
|
-
if (report1Ideas.has(ideaId)) continue; // Already handled as persisted
|
|
856
|
-
|
|
857
|
-
const idea = index.ideas[ideaId];
|
|
858
|
-
if (!idea) continue;
|
|
859
|
-
|
|
860
|
-
newIdeas.push({
|
|
861
|
-
id: ideaId,
|
|
862
|
-
title: idea.title,
|
|
863
|
-
category: idea.category,
|
|
864
|
-
confidence: idea.confidence,
|
|
865
|
-
status: idea.status,
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
return {
|
|
870
|
-
report1: r1,
|
|
871
|
-
report2: r2,
|
|
872
|
-
report1Date: index.reports[r1].generated,
|
|
873
|
-
report2Date: index.reports[r2].generated,
|
|
874
|
-
resolved,
|
|
875
|
-
new: newIdeas,
|
|
876
|
-
persisted,
|
|
877
|
-
dropped,
|
|
878
|
-
counts: {
|
|
879
|
-
resolved: resolved.length,
|
|
880
|
-
new: newIdeas.length,
|
|
881
|
-
persisted: persisted.length,
|
|
882
|
-
dropped: dropped.length,
|
|
883
|
-
},
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* List available reports for comparison
|
|
889
|
-
* @param {object} index - Ideation index
|
|
890
|
-
* @returns {Array<{ name: string, date: string, ideaCount: number }>}
|
|
891
|
-
*/
|
|
892
|
-
function listReports(index) {
|
|
893
|
-
return Object.entries(index.reports || {})
|
|
894
|
-
.map(([name, data]) => ({
|
|
895
|
-
name,
|
|
896
|
-
date: data.generated,
|
|
897
|
-
ideaCount: data.idea_count || (data.ideas || []).length,
|
|
898
|
-
scope: data.scope,
|
|
899
|
-
depth: data.depth,
|
|
900
|
-
}))
|
|
901
|
-
.sort((a, b) => (a.date || '').localeCompare(b.date || ''));
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// ============================================================================
|
|
905
|
-
// FOCUSED RE-IDEATION (US-0209)
|
|
906
|
-
// ============================================================================
|
|
907
|
-
|
|
908
|
-
/**
|
|
909
|
-
* Get focused context for a specific idea (for re-ideation)
|
|
910
|
-
* @param {object} index - Ideation index
|
|
911
|
-
* @param {string} ideaId - Idea ID (e.g., 'IDEA-0023')
|
|
912
|
-
* @returns {{ ok: boolean, idea?: object, history?: array, error?: string }}
|
|
913
|
-
*/
|
|
914
|
-
function getIdeaForFocus(index, ideaId) {
|
|
915
|
-
const normalizedId = ideaId.toUpperCase();
|
|
916
|
-
const idea = index.ideas[normalizedId];
|
|
917
|
-
|
|
918
|
-
if (!idea) {
|
|
919
|
-
// Try finding by partial ID
|
|
920
|
-
const matches = Object.keys(index.ideas).filter(
|
|
921
|
-
id => id.includes(normalizedId) || normalizedId.includes(id.replace('IDEA-', ''))
|
|
922
|
-
);
|
|
923
|
-
if (matches.length === 1) {
|
|
924
|
-
return getIdeaForFocus(index, matches[0]);
|
|
925
|
-
}
|
|
926
|
-
if (matches.length > 1) {
|
|
927
|
-
return { ok: false, error: `Ambiguous ID. Matches: ${matches.join(', ')}` };
|
|
928
|
-
}
|
|
929
|
-
return { ok: false, error: `Idea not found: ${ideaId}` };
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// Build full context for focused re-ideation
|
|
933
|
-
const context = {
|
|
934
|
-
ok: true,
|
|
935
|
-
idea: {
|
|
936
|
-
id: idea.id,
|
|
937
|
-
title: idea.title,
|
|
938
|
-
category: idea.category,
|
|
939
|
-
status: idea.status,
|
|
940
|
-
confidence: idea.confidence,
|
|
941
|
-
files: idea.files || [],
|
|
942
|
-
linkedStory: idea.linked_story,
|
|
943
|
-
linkedEpic: idea.linked_epic,
|
|
944
|
-
},
|
|
945
|
-
history: {
|
|
946
|
-
firstSeen: idea.first_seen,
|
|
947
|
-
lastSeen: idea.last_seen,
|
|
948
|
-
occurrenceCount: (idea.occurrences || []).length,
|
|
949
|
-
occurrences: (idea.occurrences || []).map(occ => ({
|
|
950
|
-
report: occ.report,
|
|
951
|
-
date: occ.date,
|
|
952
|
-
experts: occ.experts || [],
|
|
953
|
-
})),
|
|
954
|
-
},
|
|
955
|
-
allExperts: [...new Set((idea.occurrences || []).flatMap(occ => occ.experts || []))],
|
|
956
|
-
sourceReport: idea.source_report,
|
|
957
|
-
};
|
|
958
|
-
|
|
959
|
-
return context;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
/**
|
|
963
|
-
* Get ideas by category
|
|
964
|
-
* @param {object} index - Ideation index
|
|
965
|
-
* @param {string} category - Category to filter by
|
|
966
|
-
* @returns {Array<object>} Matching ideas
|
|
967
|
-
*/
|
|
968
|
-
function getIdeasByCategory(index, category) {
|
|
969
|
-
const normalizedCat = category.toLowerCase();
|
|
970
|
-
return Object.values(index.ideas || {}).filter(idea => {
|
|
971
|
-
const ideaCat = (idea.category || '').toLowerCase();
|
|
972
|
-
return ideaCat.includes(normalizedCat) || normalizedCat.includes(ideaCat);
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// ============================================================================
|
|
977
|
-
// CLI INTERFACE
|
|
978
|
-
// ============================================================================
|
|
979
|
-
|
|
980
|
-
function main() {
|
|
981
|
-
const args = process.argv.slice(2);
|
|
982
|
-
const command = args[0];
|
|
983
|
-
const rootDir = process.cwd();
|
|
984
|
-
|
|
985
|
-
const result = loadIdeationIndex(rootDir);
|
|
986
|
-
if (!result.ok) {
|
|
987
|
-
console.error(JSON.stringify({ ok: false, error: result.error }));
|
|
988
|
-
process.exit(1);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
const index = result.data;
|
|
992
|
-
|
|
993
|
-
switch (command) {
|
|
994
|
-
case 'summary': {
|
|
995
|
-
const summary = getIndexSummary(index);
|
|
996
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
997
|
-
break;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
case 'status': {
|
|
1001
|
-
const status = args[1] || 'pending';
|
|
1002
|
-
const ideas = getIdeasByStatus(index, status);
|
|
1003
|
-
console.log(JSON.stringify(ideas, null, 2));
|
|
1004
|
-
break;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
case 'recurring': {
|
|
1008
|
-
const recurring = getRecurringIdeas(index);
|
|
1009
|
-
console.log(JSON.stringify(recurring, null, 2));
|
|
1010
|
-
break;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
case 'get': {
|
|
1014
|
-
const ideaId = args[1];
|
|
1015
|
-
if (!ideaId) {
|
|
1016
|
-
console.log(JSON.stringify({ ok: false, error: 'Idea ID required' }));
|
|
1017
|
-
break;
|
|
1018
|
-
}
|
|
1019
|
-
const idea = getIdeaById(index, ideaId);
|
|
1020
|
-
console.log(JSON.stringify(idea, null, 2));
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
case 'search': {
|
|
1025
|
-
const query = args[1];
|
|
1026
|
-
if (!query) {
|
|
1027
|
-
console.log(JSON.stringify({ ok: false, error: 'Search query required' }));
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
1030
|
-
const ideas = searchIdeas(index, query);
|
|
1031
|
-
console.log(JSON.stringify(ideas, null, 2));
|
|
1032
|
-
break;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
case 'trends': {
|
|
1036
|
-
const trends = getTrendAnalysis(index);
|
|
1037
|
-
console.log(JSON.stringify(trends, null, 2));
|
|
1038
|
-
break;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
case 'hotspots': {
|
|
1042
|
-
const hotspots = getCategoryHotspots(index);
|
|
1043
|
-
console.log(JSON.stringify(hotspots, null, 2));
|
|
1044
|
-
break;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
case 'velocity': {
|
|
1048
|
-
const velocity = getImplementationVelocity(index);
|
|
1049
|
-
console.log(JSON.stringify(velocity, null, 2));
|
|
1050
|
-
break;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
case 'stale': {
|
|
1054
|
-
const minOccurrences = parseInt(args[1], 10) || 4;
|
|
1055
|
-
const stale = getStaleIdeas(index, minOccurrences);
|
|
1056
|
-
console.log(JSON.stringify(stale, null, 2));
|
|
1057
|
-
break;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
case 'agreements': {
|
|
1061
|
-
const agreements = getExpertAgreementPatterns(index);
|
|
1062
|
-
console.log(JSON.stringify(agreements, null, 2));
|
|
1063
|
-
break;
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
case 'compare': {
|
|
1067
|
-
const report1 = args[1];
|
|
1068
|
-
const report2 = args[2];
|
|
1069
|
-
if (!report1 || !report2) {
|
|
1070
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: compare <report1> <report2>' }));
|
|
1071
|
-
break;
|
|
1072
|
-
}
|
|
1073
|
-
const comparison = compareReports(index, report1, report2);
|
|
1074
|
-
console.log(JSON.stringify(comparison, null, 2));
|
|
1075
|
-
break;
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
case 'reports': {
|
|
1079
|
-
const reports = listReports(index);
|
|
1080
|
-
console.log(JSON.stringify(reports, null, 2));
|
|
1081
|
-
break;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
case 'focus': {
|
|
1085
|
-
const ideaId = args[1];
|
|
1086
|
-
if (!ideaId) {
|
|
1087
|
-
console.log(
|
|
1088
|
-
JSON.stringify({ ok: false, error: 'Idea ID required. Usage: focus IDEA-0023' })
|
|
1089
|
-
);
|
|
1090
|
-
break;
|
|
1091
|
-
}
|
|
1092
|
-
const focused = getIdeaForFocus(index, ideaId);
|
|
1093
|
-
console.log(JSON.stringify(focused, null, 2));
|
|
1094
|
-
break;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
case 'category': {
|
|
1098
|
-
const category = args[1];
|
|
1099
|
-
if (!category) {
|
|
1100
|
-
console.log(
|
|
1101
|
-
JSON.stringify({ ok: false, error: 'Category required. Usage: category Security' })
|
|
1102
|
-
);
|
|
1103
|
-
break;
|
|
1104
|
-
}
|
|
1105
|
-
const ideas = getIdeasByCategory(index, category);
|
|
1106
|
-
console.log(JSON.stringify(ideas, null, 2));
|
|
1107
|
-
break;
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
case 'help':
|
|
1111
|
-
default:
|
|
1112
|
-
console.log(`
|
|
1113
|
-
Ideation Index - Idea tracking and deduplication
|
|
1114
|
-
|
|
1115
|
-
Commands:
|
|
1116
|
-
summary Show index summary statistics
|
|
1117
|
-
status <status> List ideas by status (pending|in-progress|implemented|rejected)
|
|
1118
|
-
recurring List recurring ideas (seen 2+ times)
|
|
1119
|
-
get <id> Get idea by ID (e.g., IDEA-0001)
|
|
1120
|
-
search <query> Search ideas by title keyword
|
|
1121
|
-
|
|
1122
|
-
Trend Analysis (US-0210):
|
|
1123
|
-
trends Full trend analysis (hotspots + velocity + stale + agreements)
|
|
1124
|
-
hotspots Category hotspots (recurring ideas per category)
|
|
1125
|
-
velocity Implementation velocity (ideas resolved per month)
|
|
1126
|
-
stale [min] Stale ideas (appeared min+ times, never addressed, default: 4)
|
|
1127
|
-
agreements Expert agreement patterns (which pairs agree most)
|
|
1128
|
-
|
|
1129
|
-
Comparison Mode (US-0211):
|
|
1130
|
-
compare <r1> <r2> Compare two reports (e.g., compare 20260114 20260130)
|
|
1131
|
-
reports List all available reports
|
|
1132
|
-
|
|
1133
|
-
Focused Re-ideation (US-0209):
|
|
1134
|
-
focus <id> Get full context for focused re-ideation
|
|
1135
|
-
category <cat> List ideas by category
|
|
1136
|
-
|
|
1137
|
-
Examples:
|
|
1138
|
-
node ideation-index.js summary
|
|
1139
|
-
node ideation-index.js trends
|
|
1140
|
-
node ideation-index.js compare 20260114 20260130
|
|
1141
|
-
node ideation-index.js focus IDEA-0023
|
|
1142
|
-
`);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
// ============================================================================
|
|
1147
|
-
// EXPORTS
|
|
1148
|
-
// ============================================================================
|
|
1149
|
-
|
|
1150
|
-
module.exports = {
|
|
1151
|
-
// Constants
|
|
1152
|
-
SCHEMA_VERSION,
|
|
1153
|
-
SIMILARITY_THRESHOLD,
|
|
1154
|
-
DEFAULT_INDEX_PATH,
|
|
1155
|
-
|
|
1156
|
-
// Schema
|
|
1157
|
-
createEmptyIndex,
|
|
1158
|
-
validateAndMigrateIndex,
|
|
1159
|
-
|
|
1160
|
-
// File I/O
|
|
1161
|
-
getIndexPath,
|
|
1162
|
-
loadIdeationIndex,
|
|
1163
|
-
saveIdeationIndex,
|
|
1164
|
-
|
|
1165
|
-
// Fingerprinting & Similarity
|
|
1166
|
-
normalizeString,
|
|
1167
|
-
normalizeFiles,
|
|
1168
|
-
generateIdeaFingerprint,
|
|
1169
|
-
stringSimilarity,
|
|
1170
|
-
fileOverlap,
|
|
1171
|
-
calculateIdeaSimilarity,
|
|
1172
|
-
|
|
1173
|
-
// Idea Management
|
|
1174
|
-
findDuplicates,
|
|
1175
|
-
addIdeaToIndex,
|
|
1176
|
-
updateIdeaStatus,
|
|
1177
|
-
|
|
1178
|
-
// Queries
|
|
1179
|
-
getIdeasByStatus,
|
|
1180
|
-
getRecurringIdeas,
|
|
1181
|
-
getIndexSummary,
|
|
1182
|
-
getIdeaById,
|
|
1183
|
-
searchIdeas,
|
|
1184
|
-
updateReportMetadata,
|
|
1185
|
-
|
|
1186
|
-
// Trend Analysis (US-0210)
|
|
1187
|
-
getCategoryHotspots,
|
|
1188
|
-
getImplementationVelocity,
|
|
1189
|
-
getStaleIdeas,
|
|
1190
|
-
getExpertAgreementPatterns,
|
|
1191
|
-
getTrendAnalysis,
|
|
1192
|
-
|
|
1193
|
-
// Comparison Mode (US-0211)
|
|
1194
|
-
compareReports,
|
|
1195
|
-
listReports,
|
|
1196
|
-
|
|
1197
|
-
// Focused Re-ideation (US-0209)
|
|
1198
|
-
getIdeaForFocus,
|
|
1199
|
-
getIdeasByCategory,
|
|
1200
|
-
};
|
|
1201
|
-
|
|
1202
|
-
// Run CLI if executed directly
|
|
1203
|
-
if (require.main === module) {
|
|
1204
|
-
main();
|
|
1205
|
-
}
|