@jonit-dev/night-watch-cli 1.7.30 → 1.7.31
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/bin/night-watch.mjs +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/{src/cli.js → cli.js} +1 -0
- package/dist/cli.js.map +1 -0
- package/dist/{src/commands → commands}/audit.d.ts +2 -2
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/{src/commands → commands}/audit.js +29 -33
- package/dist/commands/audit.js.map +1 -0
- package/dist/{src/commands → commands}/board.d.ts +1 -1
- package/dist/commands/board.d.ts.map +1 -0
- package/dist/commands/board.js +664 -0
- package/dist/commands/board.js.map +1 -0
- package/dist/{src/commands → commands}/cancel.d.ts +3 -3
- package/dist/commands/cancel.d.ts.map +1 -0
- package/dist/{src/commands → commands}/cancel.js +18 -20
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/dashboard/tab-actions.d.ts.map +1 -0
- package/dist/commands/dashboard/tab-actions.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-config.d.ts +3 -3
- package/dist/commands/dashboard/tab-config.d.ts.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-config.js +250 -187
- package/dist/commands/dashboard/tab-config.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-logs.d.ts +1 -1
- package/dist/commands/dashboard/tab-logs.d.ts.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-logs.js +62 -38
- package/dist/commands/dashboard/tab-logs.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-schedules.d.ts +1 -1
- package/dist/commands/dashboard/tab-schedules.d.ts.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-schedules.js +85 -76
- package/dist/commands/dashboard/tab-schedules.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-status.d.ts +7 -7
- package/dist/commands/dashboard/tab-status.d.ts.map +1 -0
- package/dist/{src/commands → commands}/dashboard/tab-status.js +98 -95
- package/dist/commands/dashboard/tab-status.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard/types.d.ts +3 -4
- package/dist/commands/dashboard/types.d.ts.map +1 -0
- package/dist/commands/dashboard/types.js.map +1 -0
- package/dist/{src/commands → commands}/dashboard.d.ts +2 -2
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/{src/commands → commands}/dashboard.js +32 -33
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/{src/commands → commands}/doctor.d.ts +2 -2
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/{src/commands → commands}/doctor.js +40 -43
- package/dist/commands/doctor.js.map +1 -0
- package/dist/{src/commands → commands}/history.d.ts +1 -1
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/{src/commands → commands}/history.js +11 -18
- package/dist/commands/history.js.map +1 -0
- package/dist/{src/commands → commands}/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/{src/commands → commands}/init.js +57 -36
- package/dist/commands/init.js.map +1 -0
- package/dist/{src/commands → commands}/install.d.ts +2 -2
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/{src/commands → commands}/install.js +48 -50
- package/dist/commands/install.js.map +1 -0
- package/dist/{src/commands → commands}/logs.d.ts +1 -1
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/{src/commands → commands}/logs.js +29 -30
- package/dist/commands/logs.js.map +1 -0
- package/dist/{src/commands → commands}/prd-state.d.ts +1 -1
- package/dist/commands/prd-state.d.ts.map +1 -0
- package/dist/{src/commands → commands}/prd-state.js +14 -14
- package/dist/commands/prd-state.js.map +1 -0
- package/dist/{src/commands → commands}/prd.d.ts +1 -1
- package/dist/commands/prd.d.ts.map +1 -0
- package/dist/{src/commands → commands}/prd.js +57 -66
- package/dist/commands/prd.js.map +1 -0
- package/dist/{src/commands → commands}/prds.d.ts +1 -1
- package/dist/commands/prds.d.ts.map +1 -0
- package/dist/{src/commands → commands}/prds.js +51 -53
- package/dist/commands/prds.js.map +1 -0
- package/dist/{src/commands → commands}/prs.d.ts +1 -1
- package/dist/commands/prs.d.ts.map +1 -0
- package/dist/{src/commands → commands}/prs.js +22 -24
- package/dist/commands/prs.js.map +1 -0
- package/dist/{src/commands → commands}/qa.d.ts +2 -2
- package/dist/commands/qa.d.ts.map +1 -0
- package/dist/{src/commands → commands}/qa.js +50 -51
- package/dist/commands/qa.js.map +1 -0
- package/dist/{src/commands → commands}/retry.d.ts +1 -1
- package/dist/commands/retry.d.ts.map +1 -0
- package/dist/{src/commands → commands}/retry.js +9 -10
- package/dist/commands/retry.js.map +1 -0
- package/dist/{src/commands → commands}/review.d.ts +2 -2
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/{src/commands → commands}/review.js +68 -59
- package/dist/commands/review.js.map +1 -0
- package/dist/{src/commands → commands}/run.d.ts +2 -2
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/{src/commands → commands}/run.js +87 -83
- package/dist/commands/run.js.map +1 -0
- package/dist/{src/commands → commands}/serve.d.ts +2 -2
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/{src/commands → commands}/serve.js +18 -18
- package/dist/commands/serve.js.map +1 -0
- package/dist/{src/commands → commands}/slice.d.ts +2 -2
- package/dist/commands/slice.d.ts.map +1 -0
- package/dist/{src/commands → commands}/slice.js +50 -46
- package/dist/commands/slice.js.map +1 -0
- package/dist/{src/commands → commands}/state.d.ts +1 -1
- package/dist/commands/state.d.ts.map +1 -0
- package/dist/{src/commands → commands}/state.js +20 -22
- package/dist/commands/state.js.map +1 -0
- package/dist/{src/commands → commands}/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/{src/commands → commands}/status.js +75 -54
- package/dist/commands/status.js.map +1 -0
- package/dist/{src/commands → commands}/uninstall.d.ts +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/{src/commands → commands}/uninstall.js +12 -14
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/{src/commands → commands}/update.d.ts +1 -1
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/{src/commands → commands}/update.js +23 -23
- package/dist/commands/update.js.map +1 -0
- package/package.json +18 -42
- package/LICENSE +0 -21
- package/README.md +0 -132
- package/dist/shared/types.d.ts +0 -226
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js +0 -7
- package/dist/shared/types.js.map +0 -1
- package/dist/src/agents/soul-compiler.d.ts +0 -11
- package/dist/src/agents/soul-compiler.d.ts.map +0 -1
- package/dist/src/agents/soul-compiler.js +0 -157
- package/dist/src/agents/soul-compiler.js.map +0 -1
- package/dist/src/board/factory.d.ts +0 -3
- package/dist/src/board/factory.d.ts.map +0 -1
- package/dist/src/board/factory.js +0 -10
- package/dist/src/board/factory.js.map +0 -1
- package/dist/src/board/providers/github-graphql.d.ts +0 -16
- package/dist/src/board/providers/github-graphql.d.ts.map +0 -1
- package/dist/src/board/providers/github-graphql.js +0 -43
- package/dist/src/board/providers/github-graphql.js.map +0 -1
- package/dist/src/board/providers/github-projects.d.ts +0 -51
- package/dist/src/board/providers/github-projects.d.ts.map +0 -1
- package/dist/src/board/providers/github-projects.js +0 -672
- package/dist/src/board/providers/github-projects.js.map +0 -1
- package/dist/src/board/types.d.ts +0 -60
- package/dist/src/board/types.d.ts.map +0 -1
- package/dist/src/board/types.js +0 -4
- package/dist/src/board/types.js.map +0 -1
- package/dist/src/cli.d.ts +0 -3
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/commands/audit.d.ts.map +0 -1
- package/dist/src/commands/audit.js.map +0 -1
- package/dist/src/commands/board.d.ts.map +0 -1
- package/dist/src/commands/board.js +0 -294
- package/dist/src/commands/board.js.map +0 -1
- package/dist/src/commands/cancel.d.ts.map +0 -1
- package/dist/src/commands/cancel.js.map +0 -1
- package/dist/src/commands/dashboard/tab-actions.d.ts.map +0 -1
- package/dist/src/commands/dashboard/tab-actions.js.map +0 -1
- package/dist/src/commands/dashboard/tab-config.d.ts.map +0 -1
- package/dist/src/commands/dashboard/tab-config.js.map +0 -1
- package/dist/src/commands/dashboard/tab-logs.d.ts.map +0 -1
- package/dist/src/commands/dashboard/tab-logs.js.map +0 -1
- package/dist/src/commands/dashboard/tab-schedules.d.ts.map +0 -1
- package/dist/src/commands/dashboard/tab-schedules.js.map +0 -1
- package/dist/src/commands/dashboard/tab-status.d.ts.map +0 -1
- package/dist/src/commands/dashboard/tab-status.js.map +0 -1
- package/dist/src/commands/dashboard/types.d.ts.map +0 -1
- package/dist/src/commands/dashboard/types.js.map +0 -1
- package/dist/src/commands/dashboard.d.ts.map +0 -1
- package/dist/src/commands/dashboard.js.map +0 -1
- package/dist/src/commands/doctor.d.ts.map +0 -1
- package/dist/src/commands/doctor.js.map +0 -1
- package/dist/src/commands/history.d.ts.map +0 -1
- package/dist/src/commands/history.js.map +0 -1
- package/dist/src/commands/init.d.ts.map +0 -1
- package/dist/src/commands/init.js.map +0 -1
- package/dist/src/commands/install.d.ts.map +0 -1
- package/dist/src/commands/install.js.map +0 -1
- package/dist/src/commands/logs.d.ts.map +0 -1
- package/dist/src/commands/logs.js.map +0 -1
- package/dist/src/commands/prd-state.d.ts.map +0 -1
- package/dist/src/commands/prd-state.js.map +0 -1
- package/dist/src/commands/prd.d.ts.map +0 -1
- package/dist/src/commands/prd.js.map +0 -1
- package/dist/src/commands/prds.d.ts.map +0 -1
- package/dist/src/commands/prds.js.map +0 -1
- package/dist/src/commands/prs.d.ts.map +0 -1
- package/dist/src/commands/prs.js.map +0 -1
- package/dist/src/commands/qa.d.ts.map +0 -1
- package/dist/src/commands/qa.js.map +0 -1
- package/dist/src/commands/retry.d.ts.map +0 -1
- package/dist/src/commands/retry.js.map +0 -1
- package/dist/src/commands/review.d.ts.map +0 -1
- package/dist/src/commands/review.js.map +0 -1
- package/dist/src/commands/run.d.ts.map +0 -1
- package/dist/src/commands/run.js.map +0 -1
- package/dist/src/commands/serve.d.ts.map +0 -1
- package/dist/src/commands/serve.js.map +0 -1
- package/dist/src/commands/slice.d.ts.map +0 -1
- package/dist/src/commands/slice.js.map +0 -1
- package/dist/src/commands/state.d.ts.map +0 -1
- package/dist/src/commands/state.js.map +0 -1
- package/dist/src/commands/status.d.ts.map +0 -1
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/commands/uninstall.d.ts.map +0 -1
- package/dist/src/commands/uninstall.js.map +0 -1
- package/dist/src/commands/update.d.ts.map +0 -1
- package/dist/src/commands/update.js.map +0 -1
- package/dist/src/config.d.ts +0 -23
- package/dist/src/config.d.ts.map +0 -1
- package/dist/src/config.js +0 -671
- package/dist/src/config.js.map +0 -1
- package/dist/src/constants.d.ts +0 -67
- package/dist/src/constants.d.ts.map +0 -1
- package/dist/src/constants.js +0 -131
- package/dist/src/constants.js.map +0 -1
- package/dist/src/server/index.d.ts +0 -23
- package/dist/src/server/index.d.ts.map +0 -1
- package/dist/src/server/index.js +0 -1704
- package/dist/src/server/index.js.map +0 -1
- package/dist/src/slack/channel-manager.d.ts +0 -32
- package/dist/src/slack/channel-manager.d.ts.map +0 -1
- package/dist/src/slack/channel-manager.js +0 -128
- package/dist/src/slack/channel-manager.js.map +0 -1
- package/dist/src/slack/client.d.ts +0 -76
- package/dist/src/slack/client.d.ts.map +0 -1
- package/dist/src/slack/client.js +0 -193
- package/dist/src/slack/client.js.map +0 -1
- package/dist/src/slack/deliberation.d.ts +0 -87
- package/dist/src/slack/deliberation.d.ts.map +0 -1
- package/dist/src/slack/deliberation.js +0 -1354
- package/dist/src/slack/deliberation.js.map +0 -1
- package/dist/src/slack/index.d.ts +0 -6
- package/dist/src/slack/index.d.ts.map +0 -1
- package/dist/src/slack/index.js +0 -5
- package/dist/src/slack/index.js.map +0 -1
- package/dist/src/slack/interaction-listener.d.ts +0 -130
- package/dist/src/slack/interaction-listener.d.ts.map +0 -1
- package/dist/src/slack/interaction-listener.js +0 -1386
- package/dist/src/slack/interaction-listener.js.map +0 -1
- package/dist/src/storage/json-state-migrator.d.ts +0 -24
- package/dist/src/storage/json-state-migrator.d.ts.map +0 -1
- package/dist/src/storage/json-state-migrator.js +0 -197
- package/dist/src/storage/json-state-migrator.js.map +0 -1
- package/dist/src/storage/repositories/index.d.ts +0 -25
- package/dist/src/storage/repositories/index.d.ts.map +0 -1
- package/dist/src/storage/repositories/index.js +0 -45
- package/dist/src/storage/repositories/index.js.map +0 -1
- package/dist/src/storage/repositories/interfaces.d.ts +0 -60
- package/dist/src/storage/repositories/interfaces.d.ts.map +0 -1
- package/dist/src/storage/repositories/interfaces.js +0 -6
- package/dist/src/storage/repositories/interfaces.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts +0 -33
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.js +0 -715
- package/dist/src/storage/repositories/sqlite/agent-persona-repository.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/execution-history-repository.d.ts +0 -21
- package/dist/src/storage/repositories/sqlite/execution-history-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/execution-history-repository.js +0 -94
- package/dist/src/storage/repositories/sqlite/execution-history-repository.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/prd-state-repository.d.ts +0 -17
- package/dist/src/storage/repositories/sqlite/prd-state-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/prd-state-repository.js +0 -74
- package/dist/src/storage/repositories/sqlite/prd-state-repository.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/project-registry-repository.d.ts +0 -17
- package/dist/src/storage/repositories/sqlite/project-registry-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/project-registry-repository.js +0 -43
- package/dist/src/storage/repositories/sqlite/project-registry-repository.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.d.ts +0 -14
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.js +0 -47
- package/dist/src/storage/repositories/sqlite/roadmap-state-repository.js.map +0 -1
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.d.ts +0 -20
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.d.ts.map +0 -1
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.js +0 -88
- package/dist/src/storage/repositories/sqlite/slack-discussion-repository.js.map +0 -1
- package/dist/src/storage/sqlite/client.d.ts +0 -23
- package/dist/src/storage/sqlite/client.d.ts.map +0 -1
- package/dist/src/storage/sqlite/client.js +0 -47
- package/dist/src/storage/sqlite/client.js.map +0 -1
- package/dist/src/storage/sqlite/migrations.d.ts +0 -11
- package/dist/src/storage/sqlite/migrations.d.ts.map +0 -1
- package/dist/src/storage/sqlite/migrations.js +0 -94
- package/dist/src/storage/sqlite/migrations.js.map +0 -1
- package/dist/src/templates/prd-template.d.ts +0 -11
- package/dist/src/templates/prd-template.d.ts.map +0 -1
- package/dist/src/templates/prd-template.js +0 -166
- package/dist/src/templates/prd-template.js.map +0 -1
- package/dist/src/templates/slicer-prompt.d.ts +0 -54
- package/dist/src/templates/slicer-prompt.d.ts.map +0 -1
- package/dist/src/templates/slicer-prompt.js +0 -163
- package/dist/src/templates/slicer-prompt.js.map +0 -1
- package/dist/src/types.d.ts +0 -140
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -5
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils/avatar-generator.d.ts +0 -6
- package/dist/src/utils/avatar-generator.d.ts.map +0 -1
- package/dist/src/utils/avatar-generator.js +0 -133
- package/dist/src/utils/avatar-generator.js.map +0 -1
- package/dist/src/utils/checks.d.ts +0 -55
- package/dist/src/utils/checks.d.ts.map +0 -1
- package/dist/src/utils/checks.js +0 -246
- package/dist/src/utils/checks.js.map +0 -1
- package/dist/src/utils/config-writer.d.ts +0 -16
- package/dist/src/utils/config-writer.d.ts.map +0 -1
- package/dist/src/utils/config-writer.js +0 -45
- package/dist/src/utils/config-writer.js.map +0 -1
- package/dist/src/utils/crontab.d.ts +0 -62
- package/dist/src/utils/crontab.d.ts.map +0 -1
- package/dist/src/utils/crontab.js +0 -168
- package/dist/src/utils/crontab.js.map +0 -1
- package/dist/src/utils/execution-history.d.ts +0 -54
- package/dist/src/utils/execution-history.d.ts.map +0 -1
- package/dist/src/utils/execution-history.js +0 -80
- package/dist/src/utils/execution-history.js.map +0 -1
- package/dist/src/utils/github.d.ts +0 -40
- package/dist/src/utils/github.d.ts.map +0 -1
- package/dist/src/utils/github.js +0 -126
- package/dist/src/utils/github.js.map +0 -1
- package/dist/src/utils/notify.d.ts +0 -64
- package/dist/src/utils/notify.d.ts.map +0 -1
- package/dist/src/utils/notify.js +0 -405
- package/dist/src/utils/notify.js.map +0 -1
- package/dist/src/utils/prd-states.d.ts +0 -16
- package/dist/src/utils/prd-states.d.ts.map +0 -1
- package/dist/src/utils/prd-states.js +0 -28
- package/dist/src/utils/prd-states.js.map +0 -1
- package/dist/src/utils/registry.d.ts +0 -45
- package/dist/src/utils/registry.d.ts.map +0 -1
- package/dist/src/utils/registry.js +0 -86
- package/dist/src/utils/registry.js.map +0 -1
- package/dist/src/utils/roadmap-parser.d.ts +0 -45
- package/dist/src/utils/roadmap-parser.d.ts.map +0 -1
- package/dist/src/utils/roadmap-parser.js +0 -136
- package/dist/src/utils/roadmap-parser.js.map +0 -1
- package/dist/src/utils/roadmap-scanner.d.ts +0 -92
- package/dist/src/utils/roadmap-scanner.d.ts.map +0 -1
- package/dist/src/utils/roadmap-scanner.js +0 -349
- package/dist/src/utils/roadmap-scanner.js.map +0 -1
- package/dist/src/utils/roadmap-state.d.ts +0 -90
- package/dist/src/utils/roadmap-state.d.ts.map +0 -1
- package/dist/src/utils/roadmap-state.js +0 -154
- package/dist/src/utils/roadmap-state.js.map +0 -1
- package/dist/src/utils/script-result.d.ts +0 -12
- package/dist/src/utils/script-result.d.ts.map +0 -1
- package/dist/src/utils/script-result.js +0 -46
- package/dist/src/utils/script-result.js.map +0 -1
- package/dist/src/utils/shell.d.ts +0 -27
- package/dist/src/utils/shell.d.ts.map +0 -1
- package/dist/src/utils/shell.js +0 -64
- package/dist/src/utils/shell.js.map +0 -1
- package/dist/src/utils/status-data.d.ts +0 -148
- package/dist/src/utils/status-data.d.ts.map +0 -1
- package/dist/src/utils/status-data.js +0 -548
- package/dist/src/utils/status-data.js.map +0 -1
- package/dist/src/utils/ui.d.ts +0 -55
- package/dist/src/utils/ui.d.ts.map +0 -1
- package/dist/src/utils/ui.js +0 -121
- package/dist/src/utils/ui.js.map +0 -1
- package/scripts/night-watch-audit-cron.sh +0 -165
- package/scripts/night-watch-cron.sh +0 -484
- package/scripts/night-watch-helpers.sh +0 -499
- package/scripts/night-watch-pr-reviewer-cron.sh +0 -528
- package/scripts/night-watch-qa-cron.sh +0 -281
- package/scripts/night-watch-slicer-cron.sh +0 -90
- package/scripts/test-helpers.bats +0 -77
- package/templates/night-watch-audit.md +0 -87
- package/templates/night-watch-pr-reviewer.md +0 -174
- package/templates/night-watch-qa.md +0 -157
- package/templates/night-watch-slicer.md +0 -219
- package/templates/night-watch.config.json +0 -30
- package/templates/night-watch.md +0 -94
- package/templates/prd-executor.md +0 -235
- package/templates/prd.md +0 -26
- package/web/dist/assets/index-BiJf9LFT.js +0 -458
- package/web/dist/assets/index-OpSgvsYu.css +0 -1
- package/web/dist/avatars/carlos.webp +0 -0
- package/web/dist/avatars/dev.webp +0 -0
- package/web/dist/avatars/maya.webp +0 -0
- package/web/dist/avatars/priya.webp +0 -0
- package/web/dist/index.html +0 -82
- /package/dist/{src/commands → commands}/dashboard/tab-actions.d.ts +0 -0
- /package/dist/{src/commands → commands}/dashboard/tab-actions.js +0 -0
- /package/dist/{src/commands → commands}/dashboard/types.js +0 -0
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board command group — manage the PRD tracking board
|
|
3
|
+
*/
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as readline from 'readline';
|
|
8
|
+
import { BOARD_COLUMNS, CATEGORY_LABELS, HORIZON_LABELS, NIGHT_WATCH_LABELS, PRIORITY_LABELS, createBoardProvider, createTable, dim, extractCategory, extractHorizon, extractPriority, getUncheckedItems, header, info, isValidCategory, isValidHorizon, isValidPriority, loadConfig, parseRoadmap, saveConfig, sortByPriority, success, warn, } from '@night-watch/core';
|
|
9
|
+
import { findMatchingIssue, getLabelsForSection } from '@night-watch/core';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
/** Wrap an async action body so provider errors surface as clean messages. */
|
|
12
|
+
async function run(fn) {
|
|
13
|
+
try {
|
|
14
|
+
await fn();
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
console.error(chalk.red(err.message));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Return a ready-to-use board provider, or exit with an error if not enabled.
|
|
23
|
+
*/
|
|
24
|
+
function getProvider(config, cwd) {
|
|
25
|
+
if (config.boardProvider?.enabled === false) {
|
|
26
|
+
console.error('Board provider is disabled. Remove boardProvider.enabled: false from night-watch.config.json to re-enable.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const bp = config.boardProvider ?? { enabled: true, provider: 'github' };
|
|
30
|
+
return createBoardProvider(bp, cwd);
|
|
31
|
+
}
|
|
32
|
+
function defaultBoardTitle(cwd) {
|
|
33
|
+
return `${path.basename(cwd)} Night Watch`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Ensure the project has a configured board number.
|
|
37
|
+
* If missing, auto-create a board and persist projectNumber to config.
|
|
38
|
+
*/
|
|
39
|
+
async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
40
|
+
if (config.boardProvider?.projectNumber) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const title = defaultBoardTitle(cwd);
|
|
44
|
+
if (!options?.quiet) {
|
|
45
|
+
info(`No board configured. Creating "${title}"…`);
|
|
46
|
+
}
|
|
47
|
+
const boardInfo = await provider.setupBoard(title);
|
|
48
|
+
const result = saveConfig(cwd, {
|
|
49
|
+
boardProvider: {
|
|
50
|
+
...config.boardProvider,
|
|
51
|
+
enabled: config.boardProvider?.enabled ?? true,
|
|
52
|
+
provider: config.boardProvider?.provider ?? 'github',
|
|
53
|
+
projectNumber: boardInfo.number,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
throw new Error(`Failed to save config: ${result.error}`);
|
|
58
|
+
}
|
|
59
|
+
if (!options?.quiet) {
|
|
60
|
+
success(`Board configured (#${boardInfo.number})`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Prompt the user for a yes/no confirmation via readline.
|
|
65
|
+
* Returns true when the user confirms.
|
|
66
|
+
*/
|
|
67
|
+
async function confirmPrompt(question) {
|
|
68
|
+
const rl = readline.createInterface({
|
|
69
|
+
input: process.stdin,
|
|
70
|
+
output: process.stdout,
|
|
71
|
+
});
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
rl.question(question, (answer) => {
|
|
74
|
+
rl.close();
|
|
75
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create GitHub labels via `gh label create` (idempotent).
|
|
81
|
+
*/
|
|
82
|
+
async function createGitHubLabel(label, cwd) {
|
|
83
|
+
try {
|
|
84
|
+
execFileSync('gh', ['label', 'create', label.name, '--description', label.description, '--color', label.color], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
85
|
+
return { created: true, skipped: false };
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const output = err instanceof Error ? err.message : String(err);
|
|
89
|
+
// Label already exists - treat as success
|
|
90
|
+
if (output.includes('already exists') || output.includes('Label already exists')) {
|
|
91
|
+
return { created: false, skipped: true };
|
|
92
|
+
}
|
|
93
|
+
return { created: false, skipped: false, error: output };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Group issues by priority for display.
|
|
98
|
+
*/
|
|
99
|
+
function groupByPriority(issues) {
|
|
100
|
+
const groups = new Map();
|
|
101
|
+
// Initialize with ordered priority groups
|
|
102
|
+
groups.set('P0 — Critical', []);
|
|
103
|
+
groups.set('P1 — High', []);
|
|
104
|
+
groups.set('P2 — Normal', []);
|
|
105
|
+
groups.set('No Priority', []);
|
|
106
|
+
for (const issue of issues) {
|
|
107
|
+
const priority = extractPriority(issue);
|
|
108
|
+
if (priority === 'P0') {
|
|
109
|
+
groups.get('P0 — Critical').push(issue);
|
|
110
|
+
}
|
|
111
|
+
else if (priority === 'P1') {
|
|
112
|
+
groups.get('P1 — High').push(issue);
|
|
113
|
+
}
|
|
114
|
+
else if (priority === 'P2') {
|
|
115
|
+
groups.get('P2 — Normal').push(issue);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
groups.get('No Priority').push(issue);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return groups;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Group issues by category for display.
|
|
125
|
+
*/
|
|
126
|
+
function groupByCategory(issues) {
|
|
127
|
+
const groups = new Map();
|
|
128
|
+
// Initialize with all categories
|
|
129
|
+
for (const cat of CATEGORY_LABELS) {
|
|
130
|
+
groups.set(cat, []);
|
|
131
|
+
}
|
|
132
|
+
groups.set('No Category', []);
|
|
133
|
+
for (const issue of issues) {
|
|
134
|
+
const category = extractCategory(issue);
|
|
135
|
+
if (category && groups.has(category)) {
|
|
136
|
+
groups.get(category).push(issue);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
groups.get('No Category').push(issue);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return groups;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Register the board command group with the program.
|
|
146
|
+
*/
|
|
147
|
+
export function boardCommand(program) {
|
|
148
|
+
const board = program.command('board').description('Manage the PRD tracking board');
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// board setup
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
board
|
|
153
|
+
.command('setup')
|
|
154
|
+
.description('Create the Night Watch project board and persist its number to config')
|
|
155
|
+
.option('--title <title>', 'Board title (default: <repo-folder> Night Watch)')
|
|
156
|
+
.action(async (options) => run(async () => {
|
|
157
|
+
const cwd = process.cwd();
|
|
158
|
+
const config = loadConfig(cwd);
|
|
159
|
+
const provider = getProvider(config, cwd);
|
|
160
|
+
// Warn if already configured
|
|
161
|
+
if (config.boardProvider?.projectNumber) {
|
|
162
|
+
warn(`Board already set up (project #${config.boardProvider.projectNumber}).`);
|
|
163
|
+
const confirmed = await confirmPrompt('Re-run setup? This will create a new board. [y/N] ');
|
|
164
|
+
if (!confirmed) {
|
|
165
|
+
dim('Aborted.');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const boardTitle = options.title?.trim() || defaultBoardTitle(cwd);
|
|
170
|
+
info(`Creating board "${boardTitle}"…`);
|
|
171
|
+
const boardInfo = await provider.setupBoard(boardTitle);
|
|
172
|
+
// Persist the project number
|
|
173
|
+
const result = saveConfig(cwd, {
|
|
174
|
+
boardProvider: {
|
|
175
|
+
...config.boardProvider,
|
|
176
|
+
projectNumber: boardInfo.number,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
if (!result.success) {
|
|
180
|
+
console.error(`Failed to save config: ${result.error}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
const columns = await provider.getColumns();
|
|
184
|
+
header('Board Created');
|
|
185
|
+
success(`URL: ${boardInfo.url}`);
|
|
186
|
+
info('Columns:');
|
|
187
|
+
for (const col of columns) {
|
|
188
|
+
dim(` • ${col.name}`);
|
|
189
|
+
}
|
|
190
|
+
}));
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// board setup-labels
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
board
|
|
195
|
+
.command('setup-labels')
|
|
196
|
+
.description('Create Night Watch priority, category, and horizon labels in the GitHub repo')
|
|
197
|
+
.option('--dry-run', 'Show what labels would be created without creating them')
|
|
198
|
+
.action(async (options) => run(async () => {
|
|
199
|
+
const cwd = process.cwd();
|
|
200
|
+
header('Night Watch Labels');
|
|
201
|
+
if (options.dryRun) {
|
|
202
|
+
info('Dry run — showing labels that would be created:');
|
|
203
|
+
for (const label of NIGHT_WATCH_LABELS) {
|
|
204
|
+
console.log(` ${chalk.cyan(label.name)} (${label.color})`);
|
|
205
|
+
dim(` ${label.description}`);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
let created = 0;
|
|
210
|
+
let skipped = 0;
|
|
211
|
+
let failed = 0;
|
|
212
|
+
for (const label of NIGHT_WATCH_LABELS) {
|
|
213
|
+
const result = await createGitHubLabel(label, cwd);
|
|
214
|
+
if (result.created) {
|
|
215
|
+
created++;
|
|
216
|
+
success(`Created label: ${label.name}`);
|
|
217
|
+
}
|
|
218
|
+
else if (result.skipped) {
|
|
219
|
+
skipped++;
|
|
220
|
+
dim(`Label already exists: ${label.name}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
failed++;
|
|
224
|
+
console.error(chalk.red(`Failed to create label ${label.name}: ${result.error}`));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
console.log();
|
|
228
|
+
info('Summary:');
|
|
229
|
+
dim(` Created: ${created}`);
|
|
230
|
+
dim(` Skipped (already existed): ${skipped}`);
|
|
231
|
+
if (failed > 0) {
|
|
232
|
+
console.error(chalk.red(` Failed: ${failed}`));
|
|
233
|
+
}
|
|
234
|
+
}));
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// board create-prd <title>
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
board
|
|
239
|
+
.command('create-prd')
|
|
240
|
+
.description('Create a new issue on the board and add it in the Draft column')
|
|
241
|
+
.argument('<title>', 'Issue title')
|
|
242
|
+
.option('--body <text>', 'Issue body text')
|
|
243
|
+
.option('--body-file <path>', 'Read issue body from a file')
|
|
244
|
+
.option('--column <name>', 'Target column (default: Draft)', 'Draft')
|
|
245
|
+
.option('--label <name>', 'Label to apply to the issue')
|
|
246
|
+
.option('--priority <value>', 'Priority label (P0, P1, P2)')
|
|
247
|
+
.option('--category <value>', 'Category label (reliability, quality, product, etc.)')
|
|
248
|
+
.option('--horizon <value>', 'Horizon label (short-term, medium-term, long-term)')
|
|
249
|
+
.action(async (title, options) => run(async () => {
|
|
250
|
+
const cwd = process.cwd();
|
|
251
|
+
const config = loadConfig(cwd);
|
|
252
|
+
const provider = getProvider(config, cwd);
|
|
253
|
+
await ensureBoardConfigured(config, cwd, provider);
|
|
254
|
+
// Validate column name
|
|
255
|
+
if (!BOARD_COLUMNS.includes(options.column)) {
|
|
256
|
+
console.error(`Invalid column "${options.column}". Valid columns: ${BOARD_COLUMNS.join(', ')}`);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
// Validate priority
|
|
260
|
+
if (options.priority && !isValidPriority(options.priority)) {
|
|
261
|
+
console.error(`Invalid priority "${options.priority}". Valid values: ${PRIORITY_LABELS.join(', ')}`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
// Validate category
|
|
265
|
+
if (options.category && !isValidCategory(options.category)) {
|
|
266
|
+
console.error(`Invalid category "${options.category}". Valid values: ${CATEGORY_LABELS.join(', ')}`);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
// Validate horizon
|
|
270
|
+
if (options.horizon && !isValidHorizon(options.horizon)) {
|
|
271
|
+
console.error(`Invalid horizon "${options.horizon}". Valid values: ${HORIZON_LABELS.join(', ')}`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
let body = options.body ?? '';
|
|
275
|
+
if (options.bodyFile) {
|
|
276
|
+
const filePath = options.bodyFile;
|
|
277
|
+
if (!fs.existsSync(filePath)) {
|
|
278
|
+
console.error(`File not found: ${filePath}`);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
body = fs.readFileSync(filePath, 'utf-8');
|
|
282
|
+
}
|
|
283
|
+
// Build labels array
|
|
284
|
+
const labels = [];
|
|
285
|
+
if (options.label) {
|
|
286
|
+
labels.push(options.label);
|
|
287
|
+
}
|
|
288
|
+
if (options.priority) {
|
|
289
|
+
labels.push(options.priority);
|
|
290
|
+
}
|
|
291
|
+
if (options.category) {
|
|
292
|
+
labels.push(options.category);
|
|
293
|
+
}
|
|
294
|
+
if (options.horizon) {
|
|
295
|
+
labels.push(options.horizon);
|
|
296
|
+
}
|
|
297
|
+
const issue = await provider.createIssue({
|
|
298
|
+
title,
|
|
299
|
+
body,
|
|
300
|
+
column: options.column,
|
|
301
|
+
labels: labels.length > 0 ? labels : undefined,
|
|
302
|
+
});
|
|
303
|
+
console.log(chalk.green(`Created issue #${issue.number}: ${issue.title}`));
|
|
304
|
+
console.log(chalk.green(`URL: ${issue.url}`));
|
|
305
|
+
// Show applied labels
|
|
306
|
+
if (labels.length > 0) {
|
|
307
|
+
dim(`Labels: ${labels.join(', ')}`);
|
|
308
|
+
}
|
|
309
|
+
}));
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// board status
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
board
|
|
314
|
+
.command('status')
|
|
315
|
+
.description('Show the current state of all issues grouped by column')
|
|
316
|
+
.option('--json', 'Output raw JSON')
|
|
317
|
+
.option('--group-by <field>', 'Group by: priority, category, or column (default: column)')
|
|
318
|
+
.action(async (options) => run(async () => {
|
|
319
|
+
const cwd = process.cwd();
|
|
320
|
+
const config = loadConfig(cwd);
|
|
321
|
+
const provider = getProvider(config, cwd);
|
|
322
|
+
await ensureBoardConfigured(config, cwd, provider, { quiet: options.json });
|
|
323
|
+
const issues = await provider.getAllIssues();
|
|
324
|
+
if (options.json) {
|
|
325
|
+
console.log(JSON.stringify(issues, null, 2));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
header('Board Status');
|
|
329
|
+
if (issues.length === 0) {
|
|
330
|
+
dim('No issues found on the board.');
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const groupBy = options.groupBy ?? 'column';
|
|
334
|
+
if (groupBy === 'priority') {
|
|
335
|
+
// Group by priority
|
|
336
|
+
const grouped = groupByPriority(issues);
|
|
337
|
+
const table = createTable({ head: ['Priority', 'Column', '#', 'Title', 'Category'] });
|
|
338
|
+
for (const [priority, priorityIssues] of grouped) {
|
|
339
|
+
if (priorityIssues.length === 0)
|
|
340
|
+
continue;
|
|
341
|
+
// Sort issues within priority by issue number
|
|
342
|
+
const sorted = [...priorityIssues].sort((a, b) => a.number - b.number);
|
|
343
|
+
for (const issue of sorted) {
|
|
344
|
+
const category = extractCategory(issue) ?? '';
|
|
345
|
+
table.push([
|
|
346
|
+
priority,
|
|
347
|
+
issue.column ?? '-',
|
|
348
|
+
String(issue.number),
|
|
349
|
+
issue.title,
|
|
350
|
+
category,
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
console.log(table.toString());
|
|
355
|
+
// Summary per priority
|
|
356
|
+
info('Summary:');
|
|
357
|
+
for (const [priority, priorityIssues] of grouped) {
|
|
358
|
+
if (priorityIssues.length > 0) {
|
|
359
|
+
dim(` ${priority}: ${priorityIssues.length} issue${priorityIssues.length === 1 ? '' : 's'}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
else if (groupBy === 'category') {
|
|
364
|
+
// Group by category
|
|
365
|
+
const grouped = groupByCategory(issues);
|
|
366
|
+
const table = createTable({ head: ['Category', 'Column', '#', 'Title', 'Priority'] });
|
|
367
|
+
for (const [category, categoryIssues] of grouped) {
|
|
368
|
+
if (categoryIssues.length === 0)
|
|
369
|
+
continue;
|
|
370
|
+
// Sort by priority within category
|
|
371
|
+
const sorted = sortByPriority(categoryIssues);
|
|
372
|
+
for (const issue of sorted) {
|
|
373
|
+
const priority = extractPriority(issue) ?? '';
|
|
374
|
+
table.push([
|
|
375
|
+
category,
|
|
376
|
+
issue.column ?? '-',
|
|
377
|
+
String(issue.number),
|
|
378
|
+
issue.title,
|
|
379
|
+
priority,
|
|
380
|
+
]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
console.log(table.toString());
|
|
384
|
+
// Summary per category
|
|
385
|
+
info('Summary:');
|
|
386
|
+
for (const [category, categoryIssues] of grouped) {
|
|
387
|
+
if (categoryIssues.length > 0) {
|
|
388
|
+
dim(` ${category}: ${categoryIssues.length} issue${categoryIssues.length === 1 ? '' : 's'}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// Default: group by column (with priority and category columns)
|
|
394
|
+
const grouped = {};
|
|
395
|
+
for (const issue of issues) {
|
|
396
|
+
const col = issue.column ?? 'Uncategorised';
|
|
397
|
+
if (!grouped[col])
|
|
398
|
+
grouped[col] = [];
|
|
399
|
+
grouped[col].push(issue);
|
|
400
|
+
}
|
|
401
|
+
const table = createTable({ head: ['Column', '#', 'Title', 'Priority', 'Category'] });
|
|
402
|
+
for (const [col, colIssues] of Object.entries(grouped)) {
|
|
403
|
+
// Sort by priority within column
|
|
404
|
+
const sorted = sortByPriority(colIssues);
|
|
405
|
+
for (const issue of sorted) {
|
|
406
|
+
const priority = extractPriority(issue) ?? '';
|
|
407
|
+
const category = extractCategory(issue) ?? '';
|
|
408
|
+
table.push([col, String(issue.number), issue.title, priority, category]);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
console.log(table.toString());
|
|
412
|
+
// Summary per column
|
|
413
|
+
info('Summary:');
|
|
414
|
+
for (const [col, colIssues] of Object.entries(grouped)) {
|
|
415
|
+
dim(` ${col}: ${colIssues.length} issue${colIssues.length === 1 ? '' : 's'}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
dim(` Total: ${issues.length}`);
|
|
419
|
+
}));
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
// board next-issue
|
|
422
|
+
// ---------------------------------------------------------------------------
|
|
423
|
+
board
|
|
424
|
+
.command('next-issue')
|
|
425
|
+
.description('Return the next issue from a column (default: Ready), sorted by priority')
|
|
426
|
+
.option('--column <name>', 'Column to fetch from', 'Ready')
|
|
427
|
+
.option('--json', 'Output full issue JSON (for agent consumption)')
|
|
428
|
+
.action(async (options) => run(async () => {
|
|
429
|
+
const cwd = process.cwd();
|
|
430
|
+
const config = loadConfig(cwd);
|
|
431
|
+
const provider = getProvider(config, cwd);
|
|
432
|
+
await ensureBoardConfigured(config, cwd, provider, { quiet: options.json });
|
|
433
|
+
const issues = await provider.getIssuesByColumn(options.column);
|
|
434
|
+
if (issues.length === 0) {
|
|
435
|
+
if (options.json) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
console.log(`No issues found in ${options.column}`);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Sort by priority (P0 > P1 > P2 > unlabeled), then by issue number
|
|
442
|
+
const sorted = sortByPriority(issues).sort((a, b) => {
|
|
443
|
+
const aPriority = extractPriority(a);
|
|
444
|
+
const bPriority = extractPriority(b);
|
|
445
|
+
const priorityOrder = { P0: 0, P1: 1, P2: 2 };
|
|
446
|
+
const aOrder = aPriority ? priorityOrder[aPriority] : 99;
|
|
447
|
+
const bOrder = bPriority ? priorityOrder[bPriority] : 99;
|
|
448
|
+
if (aOrder !== bOrder)
|
|
449
|
+
return aOrder - bOrder;
|
|
450
|
+
return a.number - b.number; // Tie-breaker: issue number ascending
|
|
451
|
+
});
|
|
452
|
+
const issue = sorted[0];
|
|
453
|
+
if (options.json) {
|
|
454
|
+
console.log(JSON.stringify(issue, null, 2));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
// Display with priority and category info
|
|
458
|
+
const priority = extractPriority(issue);
|
|
459
|
+
const category = extractCategory(issue);
|
|
460
|
+
const horizon = extractHorizon(issue);
|
|
461
|
+
console.log(`#${issue.number} ${issue.title}`);
|
|
462
|
+
if (priority || category || horizon) {
|
|
463
|
+
const labels = [priority, category, horizon].filter(Boolean);
|
|
464
|
+
dim(`Labels: ${labels.join(', ')}`);
|
|
465
|
+
}
|
|
466
|
+
if (issue.body) {
|
|
467
|
+
const preview = issue.body.slice(0, 200);
|
|
468
|
+
const suffix = issue.body.length > 200 ? '…' : '';
|
|
469
|
+
dim(preview + suffix);
|
|
470
|
+
}
|
|
471
|
+
}));
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
// board move-issue <number>
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
board
|
|
476
|
+
.command('move-issue')
|
|
477
|
+
.description('Move an issue to a different column')
|
|
478
|
+
.argument('<number>', 'Issue number')
|
|
479
|
+
.requiredOption('--column <name>', 'Target column name')
|
|
480
|
+
.action(async (number, options) => run(async () => {
|
|
481
|
+
const cwd = process.cwd();
|
|
482
|
+
const config = loadConfig(cwd);
|
|
483
|
+
const provider = getProvider(config, cwd);
|
|
484
|
+
await ensureBoardConfigured(config, cwd, provider);
|
|
485
|
+
await provider.moveIssue(parseInt(number, 10), options.column);
|
|
486
|
+
success(`Moved issue #${number} to ${options.column}`);
|
|
487
|
+
}));
|
|
488
|
+
// ---------------------------------------------------------------------------
|
|
489
|
+
// board comment <number>
|
|
490
|
+
// ---------------------------------------------------------------------------
|
|
491
|
+
board
|
|
492
|
+
.command('comment')
|
|
493
|
+
.description('Add a comment to an issue')
|
|
494
|
+
.argument('<number>', 'Issue number')
|
|
495
|
+
.requiredOption('--body <text>', 'Comment body text')
|
|
496
|
+
.action(async (number, options) => run(async () => {
|
|
497
|
+
const cwd = process.cwd();
|
|
498
|
+
const config = loadConfig(cwd);
|
|
499
|
+
const provider = getProvider(config, cwd);
|
|
500
|
+
await ensureBoardConfigured(config, cwd, provider);
|
|
501
|
+
await provider.commentOnIssue(parseInt(number, 10), options.body);
|
|
502
|
+
success(`Comment added to issue #${number}`);
|
|
503
|
+
}));
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
// board close-issue <number>
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
board
|
|
508
|
+
.command('close-issue')
|
|
509
|
+
.description('Close an issue and move it to Done')
|
|
510
|
+
.argument('<number>', 'Issue number')
|
|
511
|
+
.action(async (number) => run(async () => {
|
|
512
|
+
const cwd = process.cwd();
|
|
513
|
+
const config = loadConfig(cwd);
|
|
514
|
+
const provider = getProvider(config, cwd);
|
|
515
|
+
await ensureBoardConfigured(config, cwd, provider);
|
|
516
|
+
const issueNumber = parseInt(number, 10);
|
|
517
|
+
await provider.closeIssue(issueNumber);
|
|
518
|
+
await provider.moveIssue(issueNumber, 'Done');
|
|
519
|
+
success(`Closed issue #${number} and moved to Done`);
|
|
520
|
+
}));
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
// board sync-roadmap
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
board
|
|
525
|
+
.command('sync-roadmap')
|
|
526
|
+
.description('Sync unchecked items from ROADMAP.md to the board as Draft issues')
|
|
527
|
+
.option('--dry-run', 'Show what would be created without making API calls')
|
|
528
|
+
.option('--update-labels', 'Update labels on existing matching issues')
|
|
529
|
+
.option('--roadmap <path>', 'Path to ROADMAP.md file (default: ROADMAP.md in current directory)')
|
|
530
|
+
.action(async (options) => run(async () => {
|
|
531
|
+
const cwd = process.cwd();
|
|
532
|
+
const config = loadConfig(cwd);
|
|
533
|
+
const provider = getProvider(config, cwd);
|
|
534
|
+
await ensureBoardConfigured(config, cwd, provider);
|
|
535
|
+
// Find ROADMAP.md
|
|
536
|
+
const roadmapPath = options.roadmap ?? path.join(cwd, 'ROADMAP.md');
|
|
537
|
+
if (!fs.existsSync(roadmapPath)) {
|
|
538
|
+
console.error(`Roadmap file not found: ${roadmapPath}`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
542
|
+
const items = parseRoadmap(roadmapContent);
|
|
543
|
+
const uncheckedItems = getUncheckedItems(items);
|
|
544
|
+
if (uncheckedItems.length === 0) {
|
|
545
|
+
info('No unchecked items found in ROADMAP.md');
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
// Get existing issues for matching
|
|
549
|
+
const existingIssues = await provider.getAllIssues();
|
|
550
|
+
header('Roadmap Sync');
|
|
551
|
+
const toCreate = [];
|
|
552
|
+
const toUpdate = [];
|
|
553
|
+
const skipped = [];
|
|
554
|
+
for (const item of uncheckedItems) {
|
|
555
|
+
const labelMapping = getLabelsForSection(item.section);
|
|
556
|
+
if (!labelMapping) {
|
|
557
|
+
skipped.push({ item, reason: 'No section-to-label mapping found' });
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
const { category, horizon } = labelMapping;
|
|
561
|
+
const existingIssue = findMatchingIssue(item.title, existingIssues, 0.8);
|
|
562
|
+
if (existingIssue) {
|
|
563
|
+
if (options.updateLabels) {
|
|
564
|
+
// Check if labels need updating
|
|
565
|
+
const hasCategory = existingIssue.labels.includes(category);
|
|
566
|
+
const hasHorizon = existingIssue.labels.includes(horizon);
|
|
567
|
+
if (!hasCategory || !hasHorizon) {
|
|
568
|
+
toUpdate.push({ item, issue: existingIssue, category, horizon });
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
skipped.push({
|
|
572
|
+
item,
|
|
573
|
+
reason: `Already exists with correct labels: #${existingIssue.number}`,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
skipped.push({ item, reason: `Already exists: #${existingIssue.number}` });
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
toCreate.push({ item, category, horizon });
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (options.dryRun) {
|
|
586
|
+
info('Dry run — showing what would happen:');
|
|
587
|
+
console.log();
|
|
588
|
+
if (toCreate.length > 0) {
|
|
589
|
+
console.log(chalk.cyan('Would create:'));
|
|
590
|
+
for (const { item, category, horizon } of toCreate) {
|
|
591
|
+
dim(` • ${item.title}`);
|
|
592
|
+
dim(` Section: ${item.section}`);
|
|
593
|
+
dim(` Labels: ${category}, ${horizon}`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (toUpdate.length > 0) {
|
|
597
|
+
console.log();
|
|
598
|
+
console.log(chalk.yellow('Would update labels:'));
|
|
599
|
+
for (const { item, issue, category, horizon } of toUpdate) {
|
|
600
|
+
dim(` • #${issue.number}: ${item.title}`);
|
|
601
|
+
dim(` Labels to add: ${category}, ${horizon}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (skipped.length > 0) {
|
|
605
|
+
console.log();
|
|
606
|
+
console.log(chalk.gray('Skipped:'));
|
|
607
|
+
for (const { item, reason } of skipped) {
|
|
608
|
+
dim(` • ${item.title} — ${reason}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
console.log();
|
|
612
|
+
info('Summary:');
|
|
613
|
+
dim(` Would create: ${toCreate.length}`);
|
|
614
|
+
dim(` Would update: ${toUpdate.length}`);
|
|
615
|
+
dim(` Skipped: ${skipped.length}`);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
// Create issues
|
|
619
|
+
let created = 0;
|
|
620
|
+
let updated = 0;
|
|
621
|
+
let failed = 0;
|
|
622
|
+
for (const { item, category, horizon } of toCreate) {
|
|
623
|
+
try {
|
|
624
|
+
const issue = await provider.createIssue({
|
|
625
|
+
title: item.title,
|
|
626
|
+
body: item.description || `Imported from ROADMAP.md section: ${item.section}`,
|
|
627
|
+
column: 'Draft',
|
|
628
|
+
labels: [category, horizon],
|
|
629
|
+
});
|
|
630
|
+
created++;
|
|
631
|
+
success(`Created #${issue.number}: ${item.title}`);
|
|
632
|
+
}
|
|
633
|
+
catch (err) {
|
|
634
|
+
failed++;
|
|
635
|
+
console.error(chalk.red(`Failed to create "${item.title}": ${err.message}`));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// Update labels on existing issues
|
|
639
|
+
for (const { item, issue, category, horizon } of toUpdate) {
|
|
640
|
+
try {
|
|
641
|
+
// Add labels via gh CLI
|
|
642
|
+
const labelsToAdd = [category, horizon].filter((l) => !issue.labels.includes(l));
|
|
643
|
+
if (labelsToAdd.length > 0) {
|
|
644
|
+
execFileSync('gh', ['issue', 'edit', String(issue.number), '--add-label', labelsToAdd.join(',')], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
645
|
+
}
|
|
646
|
+
updated++;
|
|
647
|
+
success(`Updated labels on #${issue.number}: ${item.title}`);
|
|
648
|
+
}
|
|
649
|
+
catch (err) {
|
|
650
|
+
failed++;
|
|
651
|
+
console.error(chalk.red(`Failed to update #${issue.number}: ${err.message}`));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
console.log();
|
|
655
|
+
info('Summary:');
|
|
656
|
+
dim(` Created: ${created}`);
|
|
657
|
+
dim(` Updated: ${updated}`);
|
|
658
|
+
dim(` Skipped: ${skipped.length}`);
|
|
659
|
+
if (failed > 0) {
|
|
660
|
+
console.error(chalk.red(` Failed: ${failed}`));
|
|
661
|
+
}
|
|
662
|
+
}));
|
|
663
|
+
}
|
|
664
|
+
//# sourceMappingURL=board.js.map
|