forge-workflow 0.0.9 → 0.1.0-beta.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/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -106
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +466 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/reference/TOOLCHAIN.md +658 -0
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +225 -28
- package/lib/beads-sync-scaffold.js +36 -107
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +744 -70
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +66 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +22 -2
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +851 -979
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +329 -11
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +15 -2
- package/lib/commands/test.js +58 -7
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +56 -25
- package/lib/commands/worktree.js +308 -128
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +42 -17
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +697 -0
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +8 -4
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +166 -0
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +332 -23
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +191 -0
- package/lib/status/beads-snapshot.js +145 -0
- package/lib/status/presenter.js +216 -0
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +298 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +159 -14
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +46 -36
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +284 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +64 -232
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +144 -47
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +183 -79
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +108 -0
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +117 -369
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +251 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +302 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/skills/ship/SKILL.md +251 -0
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/skills/status/SKILL.md +102 -0
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/ship.prompt.md +0 -215
- package/.github/prompts/status.prompt.md +0 -92
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -56
- package/.github/workflows/github-to-beads.yml +0 -97
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -255
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-health-check.js +0 -143
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -567
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -332
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -78
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
# dashboard.sh — Team health dashboard for forge-team.
|
|
3
3
|
#
|
|
4
4
|
# Provides cmd_dashboard [--format=json] which aggregates team issue data
|
|
5
|
-
# from
|
|
6
|
-
# blocked issues.
|
|
5
|
+
# from the Forge issue backend and displays per-developer stats, stale
|
|
6
|
+
# assignments, and blocked issues.
|
|
7
7
|
|
|
8
8
|
# ── Dependencies ─────────────────────────────────────────────────────────
|
|
9
|
-
#
|
|
10
|
-
|
|
9
|
+
# FORGE_CMD — path to forge binary (default: forge)
|
|
10
|
+
FORGE_CMD="${FORGE_CMD:-forge}"
|
|
11
11
|
|
|
12
12
|
# FORGE_NOW — override current epoch for testing (default: real time)
|
|
13
13
|
_now() {
|
|
@@ -45,73 +45,46 @@ _iso_to_epoch() {
|
|
|
45
45
|
|
|
46
46
|
# ── Data Collection ──────────────────────────────────────────────────────
|
|
47
47
|
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
echo "
|
|
59
|
-
done
|
|
48
|
+
# _forge_active_issues_json — Merged JSON array of open + in_progress issues.
|
|
49
|
+
# The Kernel list filter does not accept comma-joined statuses, so query each
|
|
50
|
+
# status separately and concatenate the issue arrays.
|
|
51
|
+
_forge_active_issues_json() {
|
|
52
|
+
local open_json inprog_json
|
|
53
|
+
open_json="$("$FORGE_CMD" issue list --status=open --json 2>/dev/null)" || open_json=""
|
|
54
|
+
inprog_json="$("$FORGE_CMD" issue list --status=in_progress --json 2>/dev/null)" || inprog_json=""
|
|
55
|
+
jq -n \
|
|
56
|
+
--argjson a "${open_json:-null}" \
|
|
57
|
+
--argjson b "${inprog_json:-null}" \
|
|
58
|
+
'(((($a.data.issues) // []) + (($b.data.issues) // [])))' 2>/dev/null || echo "[]"
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
# DEPENDS ON section
|
|
92
|
-
if [[ "$line" == "DEPENDS ON" ]]; then
|
|
93
|
-
in_depends=1
|
|
94
|
-
continue
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
if [[ $in_depends -eq 1 && "$line" =~ →.*([a-z]+-[a-z]+):.* ]]; then
|
|
98
|
-
# Extract dependency issue ID
|
|
99
|
-
local dep_id
|
|
100
|
-
dep_id="$(echo "$line" | sed -E 's/.*→ +[^ ]+ +([a-z]+-[a-z0-9]+):.*/\1/')"
|
|
101
|
-
if [[ -n "$blocked_by" ]]; then
|
|
102
|
-
blocked_by="$blocked_by,$dep_id"
|
|
103
|
-
else
|
|
104
|
-
blocked_by="$dep_id"
|
|
105
|
-
fi
|
|
106
|
-
fi
|
|
107
|
-
done <<< "$show_output"
|
|
108
|
-
|
|
109
|
-
local updated_epoch=0
|
|
110
|
-
if [[ -n "$updated" ]]; then
|
|
111
|
-
updated_epoch="$(_iso_to_epoch "$updated")"
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
echo "$issue_id|$title|$status|$owner|$updated_epoch|$blocked_by"
|
|
61
|
+
# _issue_detail_lines <issues-json>
|
|
62
|
+
# Emit one tab-delimited (jq @tsv) line per issue:
|
|
63
|
+
# id<TAB>title<TAB>STATUS<TAB>owner<TAB>updated_iso<TAB>blocked_by
|
|
64
|
+
# @tsv escapes any tab/newline/CR inside fields (issue titles are user-controlled
|
|
65
|
+
# and may contain '|', tabs, or newlines), so parsing stays field-aligned.
|
|
66
|
+
# STATUS is the upper-cased Kernel status; owner is assignee/claimed_by.
|
|
67
|
+
# A Kernel issue's `.dependencies` lists the issues it BLOCKS (outgoing edges),
|
|
68
|
+
# so blocked_by is computed by reverse scan: the issues (within this active set)
|
|
69
|
+
# whose `.dependencies` include this issue.
|
|
70
|
+
_issue_detail_lines() {
|
|
71
|
+
printf '%s' "$1" | jq -r '
|
|
72
|
+
. as $all
|
|
73
|
+
| $all[]
|
|
74
|
+
| . as $x
|
|
75
|
+
| [ $x.id,
|
|
76
|
+
($x.title // ""),
|
|
77
|
+
(($x.status // "open") | ascii_upcase),
|
|
78
|
+
($x.assignee // $x.claimed_by // ""),
|
|
79
|
+
(($x.updated_at // "") | sub("\\.[0-9]+Z$"; "Z")),
|
|
80
|
+
( [ $all[]
|
|
81
|
+
| select( (.dependencies // [])
|
|
82
|
+
| map(if type == "object" then (.id // .to // "") else . end)
|
|
83
|
+
| any(. == $x.id) )
|
|
84
|
+
| .id ]
|
|
85
|
+
| join(",") )
|
|
86
|
+
]
|
|
87
|
+
| @tsv' | tr -d '\r'
|
|
115
88
|
}
|
|
116
89
|
|
|
117
90
|
# ── Dashboard Command ────────────────────────────────────────────────────
|
|
@@ -128,12 +101,14 @@ cmd_dashboard() {
|
|
|
128
101
|
esac
|
|
129
102
|
done
|
|
130
103
|
|
|
131
|
-
# 1. Get all open/in_progress issues
|
|
132
|
-
local
|
|
133
|
-
|
|
104
|
+
# 1. Get all open/in_progress issues as a merged JSON array
|
|
105
|
+
local issues_json
|
|
106
|
+
issues_json="$(_forge_active_issues_json)"
|
|
134
107
|
|
|
135
108
|
# Check for empty
|
|
136
|
-
|
|
109
|
+
local active_count
|
|
110
|
+
active_count="$(printf '%s' "$issues_json" | jq 'length' 2>/dev/null || echo 0)"
|
|
111
|
+
if [[ "$active_count" -eq 0 ]]; then
|
|
137
112
|
if [[ "$format" == "json" ]]; then
|
|
138
113
|
echo '{"developers":{},"total":0,"stale":[],"blocked":[],"message":"No active issues — team is clear"}'
|
|
139
114
|
else
|
|
@@ -142,12 +117,12 @@ cmd_dashboard() {
|
|
|
142
117
|
return 0
|
|
143
118
|
fi
|
|
144
119
|
|
|
145
|
-
# 2.
|
|
120
|
+
# 2. Build per-issue detail lines: id|title|STATUS|owner|updated_iso|blocked_by
|
|
146
121
|
local issues=()
|
|
147
122
|
while IFS= read -r entry; do
|
|
148
123
|
[[ -z "$entry" ]] && continue
|
|
149
124
|
issues+=("$entry")
|
|
150
|
-
done <<< "$(
|
|
125
|
+
done <<< "$(_issue_detail_lines "$issues_json")"
|
|
151
126
|
|
|
152
127
|
# 3. Collect details for each issue
|
|
153
128
|
local now_epoch
|
|
@@ -163,12 +138,12 @@ cmd_dashboard() {
|
|
|
163
138
|
local total_open=0 total_inprogress=0 total_blocked=0 total_stale=0
|
|
164
139
|
|
|
165
140
|
for entry in "${issues[@]}"; do
|
|
166
|
-
|
|
141
|
+
IFS=$'\t' read -r _id _title _status _owner _updated_iso _blocked_by <<< "$entry"
|
|
167
142
|
|
|
168
|
-
local
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
143
|
+
local _updated_epoch=0
|
|
144
|
+
if [[ -n "$_updated_iso" ]]; then
|
|
145
|
+
_updated_epoch="$(_iso_to_epoch "$_updated_iso")"
|
|
146
|
+
fi
|
|
172
147
|
|
|
173
148
|
# Default owner if empty
|
|
174
149
|
[[ -z "$_owner" ]] && _owner="unassigned"
|
|
@@ -199,7 +174,7 @@ cmd_dashboard() {
|
|
|
199
174
|
if [[ -n "$_blocked_by" ]]; then
|
|
200
175
|
dev_blocked[$_owner]=$(( ${dev_blocked[$_owner]} + 1 ))
|
|
201
176
|
total_blocked=$((total_blocked + 1))
|
|
202
|
-
blocked_entries+=("$_id
|
|
177
|
+
blocked_entries+=("$_id"$'\t'"$_owner"$'\t'"$_title"$'\t'"$_blocked_by")
|
|
203
178
|
fi
|
|
204
179
|
|
|
205
180
|
# Check for stale (>48h since last update)
|
|
@@ -209,7 +184,7 @@ cmd_dashboard() {
|
|
|
209
184
|
local hours=$(( age / 3600 ))
|
|
210
185
|
dev_stale[$_owner]=$(( ${dev_stale[$_owner]} + 1 ))
|
|
211
186
|
total_stale=$((total_stale + 1))
|
|
212
|
-
stale_entries+=("$_id
|
|
187
|
+
stale_entries+=("$_id"$'\t'"$_owner"$'\t'"$_title"$'\t'"${hours}h since last update")
|
|
213
188
|
fi
|
|
214
189
|
fi
|
|
215
190
|
done
|
|
@@ -275,7 +250,7 @@ _dashboard_text() {
|
|
|
275
250
|
echo "Stale assignments (>48h):"
|
|
276
251
|
local entry
|
|
277
252
|
for entry in "${stale_entries[@]}"; do
|
|
278
|
-
IFS
|
|
253
|
+
IFS=$'\t' read -r _id _owner _title _age <<< "$entry"
|
|
279
254
|
echo " ⚠ $_id [$_owner] — $_title ($_age)"
|
|
280
255
|
done
|
|
281
256
|
fi
|
|
@@ -286,7 +261,7 @@ _dashboard_text() {
|
|
|
286
261
|
echo "Blocked issues:"
|
|
287
262
|
local entry
|
|
288
263
|
for entry in "${blocked_entries[@]}"; do
|
|
289
|
-
IFS
|
|
264
|
+
IFS=$'\t' read -r _id _owner _title _deps <<< "$entry"
|
|
290
265
|
echo " ⚠ $_id [$_owner] — $_title (blocked by $_deps)"
|
|
291
266
|
done
|
|
292
267
|
fi
|
|
@@ -321,7 +296,7 @@ _dashboard_json() {
|
|
|
321
296
|
for entry in "${stale_entries[@]}"; do
|
|
322
297
|
[[ $first -eq 0 ]] && stale_json+=","
|
|
323
298
|
first=0
|
|
324
|
-
IFS
|
|
299
|
+
IFS=$'\t' read -r _id _owner _title _age <<< "$entry"
|
|
325
300
|
stale_json+="$(jq -n -c --arg id "$_id" --arg owner "$_owner" --arg title "$_title" --arg age "$_age" '{id:$id,owner:$owner,title:$title,age:$age}')"
|
|
326
301
|
done
|
|
327
302
|
stale_json+="]"
|
|
@@ -332,7 +307,7 @@ _dashboard_json() {
|
|
|
332
307
|
for entry in "${blocked_entries[@]}"; do
|
|
333
308
|
[[ $first -eq 0 ]] && blocked_json+=","
|
|
334
309
|
first=0
|
|
335
|
-
IFS
|
|
310
|
+
IFS=$'\t' read -r _id _owner _title _deps <<< "$entry"
|
|
336
311
|
blocked_json+="$(jq -n -c --arg id "$_id" --arg owner "$_owner" --arg title "$_title" --arg deps "$_deps" '{id:$id,owner:$owner,title:$title,blocked_by:$deps}')"
|
|
337
312
|
done
|
|
338
313
|
blocked_json+="]"
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
# scripts/forge-team/lib/epic.sh — Epic rollup view
|
|
3
3
|
#
|
|
4
4
|
# Shows epic progress with per-developer breakdown and blocked issue tracking.
|
|
5
|
+
# All data comes from a SINGLE `forge issue children <id> --json` call: the kernel
|
|
6
|
+
# computes the rollup (done-only percentage, per-status counts) and emits each child
|
|
7
|
+
# with its assignee + blocked_by, so this layer only renders.
|
|
5
8
|
#
|
|
6
9
|
# Functions:
|
|
7
10
|
# cmd_epic — Top-level dispatcher entry point
|
|
8
11
|
#
|
|
9
12
|
# Env overrides (for testing):
|
|
10
|
-
#
|
|
13
|
+
# FORGE_CMD — Path to forge binary (default: forge)
|
|
11
14
|
#
|
|
12
15
|
# This file does NOT set errexit/pipefail — callers manage their own shell options.
|
|
13
16
|
|
|
@@ -19,85 +22,10 @@ _EPIC_LIB_LOADED=1
|
|
|
19
22
|
|
|
20
23
|
# ── Internal helpers ─────────────────────────────────────────────────────
|
|
21
24
|
|
|
22
|
-
#
|
|
23
|
-
_epic_get_bd() {
|
|
24
|
-
printf '%s' "${BD_CMD:-bd}"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
# _epic_parse_blocks <bd-show-output>
|
|
28
|
-
# Extracts child issue IDs from the BLOCKS section.
|
|
29
|
-
# Each line in BLOCKS looks like: ← ✓ forge-child1: Child 1 (closed)
|
|
30
|
-
# Returns one ID per line.
|
|
31
|
-
_epic_parse_blocks() {
|
|
32
|
-
local bd_output="$1"
|
|
33
|
-
local in_blocks=0
|
|
34
|
-
|
|
35
|
-
while IFS= read -r line; do
|
|
36
|
-
if [[ "$line" == "BLOCKS" ]]; then
|
|
37
|
-
in_blocks=1
|
|
38
|
-
continue
|
|
39
|
-
fi
|
|
40
|
-
# Stop at next section header (non-indented, non-empty line after BLOCKS)
|
|
41
|
-
if [[ $in_blocks -eq 1 ]]; then
|
|
42
|
-
if [[ -z "$line" ]]; then
|
|
43
|
-
continue
|
|
44
|
-
fi
|
|
45
|
-
if [[ "$line" =~ ^[A-Z] ]]; then
|
|
46
|
-
break
|
|
47
|
-
fi
|
|
48
|
-
# Extract issue ID: matches forge-XXXX pattern after the arrow+status marker
|
|
49
|
-
local child_id
|
|
50
|
-
child_id="$(printf '%s' "$line" | sed -n 's/.*← [^ ]* \([a-z0-9-]*\):.*/\1/p')"
|
|
51
|
-
if [[ -n "$child_id" ]]; then
|
|
52
|
-
printf '%s\n' "$child_id"
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
done <<< "$bd_output"
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# _epic_get_child_info <child-id>
|
|
59
|
-
# Returns: id|status|owner|title|blocked_by
|
|
60
|
-
_epic_get_child_info() {
|
|
61
|
-
local child_id="$1"
|
|
62
|
-
local bd_cmd
|
|
63
|
-
bd_cmd="$(_epic_get_bd)"
|
|
64
|
-
|
|
65
|
-
local child_output
|
|
66
|
-
child_output="$("$bd_cmd" show "$child_id" 2>/dev/null)" || return 1
|
|
67
|
-
|
|
68
|
-
local first_line
|
|
69
|
-
first_line="$(printf '%s' "$child_output" | head -1)"
|
|
70
|
-
|
|
71
|
-
# Parse status from the first character/marker
|
|
72
|
-
local status="open"
|
|
73
|
-
if [[ "$first_line" == *"CLOSED"* ]] || [[ "$first_line" == *"✓"* ]]; then
|
|
74
|
-
status="closed"
|
|
75
|
-
elif [[ "$first_line" == *"IN_PROGRESS"* ]] || [[ "$first_line" == *"◐"* ]]; then
|
|
76
|
-
status="in_progress"
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# Parse title — between · and [
|
|
80
|
-
local title
|
|
81
|
-
title="$(printf '%s' "$first_line" | sed -n 's/^[^ ]* [^ ]* · \(.*\) \[.*/\1/p')"
|
|
82
|
-
if [[ -z "$title" ]]; then
|
|
83
|
-
title="$child_id"
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# Parse owner from "Owner: xxx" line
|
|
87
|
-
local owner=""
|
|
88
|
-
owner="$(printf '%s' "$child_output" | sed -n 's/^Owner: *//p' | head -1)"
|
|
89
|
-
|
|
90
|
-
# Check for BLOCKED_BY section
|
|
91
|
-
local blocked_by=""
|
|
92
|
-
blocked_by="$(printf '%s' "$child_output" | sed -n '/^BLOCKED_BY$/,/^[A-Z]/{ /^ /{ s/.*→ \([a-z0-9-]*\):.*/\1/p; }; }')"
|
|
93
|
-
|
|
94
|
-
printf '%s|%s|%s|%s|%s' "$child_id" "$status" "$owner" "$title" "$blocked_by"
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# _epic_status_icon <status>
|
|
25
|
+
# _epic_status_icon <status> (kernel status vocabulary: open|in_progress|review|done|cancelled)
|
|
98
26
|
_epic_status_icon() {
|
|
99
27
|
case "$1" in
|
|
100
|
-
|
|
28
|
+
done) printf '%s' "✓" ;;
|
|
101
29
|
in_progress) printf '%s' "◐" ;;
|
|
102
30
|
*) printf '%s' "○" ;;
|
|
103
31
|
esac
|
|
@@ -115,35 +43,43 @@ cmd_epic() {
|
|
|
115
43
|
return 1
|
|
116
44
|
fi
|
|
117
45
|
|
|
118
|
-
local
|
|
119
|
-
|
|
46
|
+
local forge_cmd
|
|
47
|
+
forge_cmd="${FORGE_CMD:-forge}"
|
|
120
48
|
|
|
121
|
-
# 1.
|
|
122
|
-
|
|
123
|
-
|
|
49
|
+
# 1. Single query: epic header + direct children + kernel-computed rollup. A missing
|
|
50
|
+
# or invalid id makes `forge issue children` fail (empty stdout) — treat as not found.
|
|
51
|
+
local response
|
|
52
|
+
response="$("$forge_cmd" issue children "$issue_id" --json 2>/dev/null)" || {
|
|
124
53
|
echo "ERROR: Could not fetch epic $issue_id" >&2
|
|
125
54
|
return 1
|
|
126
55
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
epic_first_line="$(printf '%s' "$epic_output" | head -1)"
|
|
131
|
-
local epic_title
|
|
132
|
-
epic_title="$(printf '%s' "$epic_first_line" | sed -n 's/^[^ ]* [^ ]* · \(.*\) \[.*/\1/p')"
|
|
133
|
-
if [[ -z "$epic_title" ]]; then
|
|
134
|
-
epic_title="$issue_id"
|
|
56
|
+
if [[ -z "$response" ]]; then
|
|
57
|
+
echo "ERROR: Could not fetch epic $issue_id" >&2
|
|
58
|
+
return 1
|
|
135
59
|
fi
|
|
136
60
|
|
|
137
|
-
# 2.
|
|
138
|
-
local
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
61
|
+
# 2. Epic title (fall back to the id when the header carries no title).
|
|
62
|
+
local epic_title
|
|
63
|
+
epic_title="$(printf '%s' "$response" | jq -r '.data.epic.title // empty' | tr -d '\r')"
|
|
64
|
+
[[ -z "$epic_title" ]] && epic_title="$issue_id"
|
|
65
|
+
|
|
66
|
+
# 3. Rollup counts (kernel-computed). open_count keeps the historical invariant
|
|
67
|
+
# total = done + in_progress + open_count (the catch-all bucket also absorbs the
|
|
68
|
+
# kernel review/cancelled statuses), so downstream consumers see a stable shape.
|
|
69
|
+
local total done_count in_progress_count open_count percentage
|
|
70
|
+
total="$(printf '%s' "$response" | jq -r '.data.rollup.total // 0' | tr -d '\r')"
|
|
71
|
+
done_count="$(printf '%s' "$response" | jq -r '.data.rollup.done // 0' | tr -d '\r')"
|
|
72
|
+
in_progress_count="$(printf '%s' "$response" | jq -r '.data.rollup.in_progress // 0' | tr -d '\r')"
|
|
73
|
+
percentage="$(printf '%s' "$response" | jq -r '.data.rollup.percentage // 0' | tr -d '\r')"
|
|
74
|
+
open_count=$((total - done_count - in_progress_count))
|
|
75
|
+
|
|
76
|
+
# 4. Empty epic → no children.
|
|
77
|
+
if [[ "$total" -eq 0 ]]; then
|
|
143
78
|
if [[ "$format_flag" == "--format=json" ]]; then
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
79
|
+
# Build via jq --arg so a title containing quotes/backslashes stays valid
|
|
80
|
+
# JSON (parity with the non-empty path below).
|
|
81
|
+
jq -cn --arg epic_id "$issue_id" --arg title "$epic_title" \
|
|
82
|
+
'{epic_id:$epic_id,title:$title,total:0,done:0,in_progress:0,open_count:0,percentage:0,children:[],by_developer:{},blocked:[]}'
|
|
147
83
|
return 0
|
|
148
84
|
fi
|
|
149
85
|
echo "Epic: $issue_id — $epic_title"
|
|
@@ -151,181 +87,81 @@ ENDJSON
|
|
|
151
87
|
return 0
|
|
152
88
|
fi
|
|
153
89
|
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
local blocked_list=""
|
|
160
|
-
|
|
161
|
-
# Arrays for child data (using indexed approach for bash 3 compat)
|
|
162
|
-
local child_lines=""
|
|
163
|
-
# Developer tracking: accumulate "dev:status" pairs
|
|
164
|
-
local dev_entries=""
|
|
165
|
-
|
|
166
|
-
while IFS= read -r child_id; do
|
|
167
|
-
[[ -z "$child_id" ]] && continue
|
|
168
|
-
|
|
169
|
-
local info
|
|
170
|
-
info="$(_epic_get_child_info "$child_id")" || continue
|
|
171
|
-
|
|
172
|
-
local c_id c_status c_owner c_title c_blocked
|
|
173
|
-
IFS='|' read -r c_id c_status c_owner c_title c_blocked <<< "$info"
|
|
174
|
-
|
|
175
|
-
total=$((total + 1))
|
|
176
|
-
|
|
177
|
-
case "$c_status" in
|
|
178
|
-
closed) done_count=$((done_count + 1)) ;;
|
|
179
|
-
in_progress) in_progress_count=$((in_progress_count + 1)) ;;
|
|
180
|
-
*) open_count=$((open_count + 1)) ;;
|
|
181
|
-
esac
|
|
182
|
-
|
|
183
|
-
# Track blocked children
|
|
184
|
-
if [[ -n "$c_blocked" ]]; then
|
|
185
|
-
if [[ -n "$blocked_list" ]]; then
|
|
186
|
-
blocked_list="$blocked_list"$'\n'"$c_id blocked by $c_blocked"
|
|
187
|
-
else
|
|
188
|
-
blocked_list="$c_id blocked by $c_blocked"
|
|
189
|
-
fi
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
# Build child line for display
|
|
193
|
-
local icon
|
|
194
|
-
icon="$(_epic_status_icon "$c_status")"
|
|
195
|
-
local display_owner="${c_owner:-unassigned}"
|
|
196
|
-
child_lines="${child_lines} $icon $c_id [$display_owner] $c_title ($c_status)"$'\n'
|
|
197
|
-
|
|
198
|
-
# Track developer entries
|
|
199
|
-
if [[ -n "$c_owner" ]]; then
|
|
200
|
-
dev_entries="${dev_entries}${c_owner}:${c_status}"$'\n'
|
|
201
|
-
fi
|
|
202
|
-
done <<< "$children_ids"
|
|
203
|
-
|
|
204
|
-
# 4. Calculate completion percentage
|
|
205
|
-
local percentage=0
|
|
206
|
-
if [[ $total -gt 0 ]]; then
|
|
207
|
-
percentage=$(( (done_count * 100) / total ))
|
|
208
|
-
fi
|
|
209
|
-
|
|
210
|
-
# 5. Build per-developer breakdown
|
|
211
|
-
# Collect unique developers
|
|
212
|
-
local devs=""
|
|
213
|
-
while IFS= read -r entry; do
|
|
214
|
-
[[ -z "$entry" ]] && continue
|
|
215
|
-
local dev="${entry%%:*}"
|
|
216
|
-
if [[ -z "$devs" ]] || ! printf '%s' "$devs" | grep -qxF "$dev"; then
|
|
217
|
-
if [[ -n "$devs" ]]; then
|
|
218
|
-
devs="$devs"$'\n'"$dev"
|
|
219
|
-
else
|
|
220
|
-
devs="$dev"
|
|
221
|
-
fi
|
|
222
|
-
fi
|
|
223
|
-
done <<< "$dev_entries"
|
|
224
|
-
|
|
225
|
-
# JSON output
|
|
90
|
+
# 5. JSON output — one jq transform from the kernel response. Preserves the historical
|
|
91
|
+
# cmd_epic shape (epic_id,title,total,done,in_progress,open_count,percentage,
|
|
92
|
+
# children,by_developer,blocked). Unassigned children are excluded from by_developer
|
|
93
|
+
# (matches the prior behavior), and per-dev open is the same total-done-in_progress
|
|
94
|
+
# catch-all.
|
|
226
95
|
if [[ "$format_flag" == "--format=json" ]]; then
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
fi
|
|
261
|
-
done <<< "$dev_entries"
|
|
262
|
-
|
|
263
|
-
if [[ $first_dev -eq 0 ]]; then
|
|
264
|
-
dev_json="$dev_json,"
|
|
265
|
-
fi
|
|
266
|
-
first_dev=0
|
|
267
|
-
dev_json="$dev_json\"$dev\":{\"total\":$d_total,\"done\":$d_done,\"in_progress\":$d_in_progress,\"open\":$d_open}"
|
|
268
|
-
done <<< "$devs"
|
|
269
|
-
dev_json="$dev_json}"
|
|
270
|
-
|
|
271
|
-
local blocked_json="["
|
|
272
|
-
if [[ -n "$blocked_list" ]]; then
|
|
273
|
-
local first_blocked=1
|
|
274
|
-
while IFS= read -r bline; do
|
|
275
|
-
[[ -z "$bline" ]] && continue
|
|
276
|
-
if [[ $first_blocked -eq 0 ]]; then
|
|
277
|
-
blocked_json="$blocked_json,"
|
|
278
|
-
fi
|
|
279
|
-
first_blocked=0
|
|
280
|
-
blocked_json="$blocked_json\"$bline\""
|
|
281
|
-
done <<< "$blocked_list"
|
|
282
|
-
fi
|
|
283
|
-
blocked_json="$blocked_json]"
|
|
284
|
-
|
|
285
|
-
printf '{"epic_id":"%s","title":"%s","total":%d,"done":%d,"in_progress":%d,"open_count":%d,"percentage":%d,"children":%s,"by_developer":%s,"blocked":%s}\n' \
|
|
286
|
-
"$issue_id" "$epic_title" "$total" "$done_count" "$in_progress_count" "$open_count" "$percentage" \
|
|
287
|
-
"$children_json" "$dev_json" "$blocked_json"
|
|
96
|
+
printf '%s' "$response" | jq -c \
|
|
97
|
+
--arg epic_id "$issue_id" \
|
|
98
|
+
--arg title "$epic_title" '
|
|
99
|
+
(.data.children // []) as $children
|
|
100
|
+
| {
|
|
101
|
+
epic_id: $epic_id,
|
|
102
|
+
title: $title,
|
|
103
|
+
total: (.data.rollup.total // 0),
|
|
104
|
+
done: (.data.rollup.done // 0),
|
|
105
|
+
in_progress: (.data.rollup.in_progress // 0),
|
|
106
|
+
open_count: ((.data.rollup.total // 0) - (.data.rollup.done // 0) - (.data.rollup.in_progress // 0)),
|
|
107
|
+
percentage: (.data.rollup.percentage // 0),
|
|
108
|
+
children: [ $children[] | {
|
|
109
|
+
id: .id,
|
|
110
|
+
status: .status,
|
|
111
|
+
owner: (.assignee // ""),
|
|
112
|
+
title: (.title // .id),
|
|
113
|
+
blocked_by: ((.blocked_by // []) | join(","))
|
|
114
|
+
} ],
|
|
115
|
+
by_developer: (
|
|
116
|
+
[ $children[] | select((.assignee // "") != "") ]
|
|
117
|
+
| group_by(.assignee)
|
|
118
|
+
| map(
|
|
119
|
+
(.[0].assignee) as $dev
|
|
120
|
+
| length as $t
|
|
121
|
+
| ([.[] | select(.status == "done")] | length) as $d
|
|
122
|
+
| ([.[] | select(.status == "in_progress")] | length) as $ip
|
|
123
|
+
| { key: $dev, value: { total: $t, done: $d, in_progress: $ip, open: ($t - $d - $ip) } }
|
|
124
|
+
)
|
|
125
|
+
| from_entries
|
|
126
|
+
),
|
|
127
|
+
blocked: [ $children[] | select(.blocked == true) | "\(.id) blocked by \((.blocked_by // []) | join(","))" ]
|
|
128
|
+
}' | tr -d '\r'
|
|
288
129
|
return 0
|
|
289
130
|
fi
|
|
290
131
|
|
|
291
|
-
# Text output
|
|
132
|
+
# 6. Text output.
|
|
292
133
|
echo "Epic: $issue_id — $epic_title"
|
|
293
134
|
echo "Progress: $done_count/$total ($percentage%)"
|
|
294
|
-
|
|
135
|
+
while IFS=$'\t' read -r c_id c_status c_owner c_title; do
|
|
136
|
+
[[ -z "$c_id" ]] && continue
|
|
137
|
+
local icon
|
|
138
|
+
icon="$(_epic_status_icon "$c_status")"
|
|
139
|
+
echo " $icon $c_id [${c_owner:-unassigned}] $c_title ($c_status)"
|
|
140
|
+
done < <(printf '%s' "$response" | jq -r '.data.children[] | [.id, .status, (.assignee // ""), (.title // .id)] | @tsv' | tr -d '\r')
|
|
295
141
|
|
|
296
142
|
echo ""
|
|
297
143
|
echo "By developer:"
|
|
298
|
-
|
|
299
|
-
[[
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
fi
|
|
313
|
-
done <<< "$dev_entries"
|
|
314
|
-
|
|
315
|
-
local dev_parts=""
|
|
316
|
-
dev_parts="$d_done/$d_total done"
|
|
317
|
-
[[ $d_in_progress -gt 0 ]] && dev_parts="$dev_parts, $d_in_progress in progress"
|
|
318
|
-
[[ $d_open -gt 0 ]] && dev_parts="$dev_parts, $d_open open"
|
|
319
|
-
echo " $dev: $dev_parts"
|
|
320
|
-
done <<< "$devs"
|
|
144
|
+
printf '%s' "$response" | jq -r '
|
|
145
|
+
[ (.data.children // [])[] | select((.assignee // "") != "") ]
|
|
146
|
+
| group_by(.assignee)
|
|
147
|
+
| map(
|
|
148
|
+
(.[0].assignee) as $dev
|
|
149
|
+
| length as $t
|
|
150
|
+
| ([.[] | select(.status == "done")] | length) as $d
|
|
151
|
+
| ([.[] | select(.status == "in_progress")] | length) as $ip
|
|
152
|
+
| ($t - $d - $ip) as $op
|
|
153
|
+
| " \($dev): \($d)/\($t) done"
|
|
154
|
+
+ (if $ip > 0 then ", \($ip) in progress" else "" end)
|
|
155
|
+
+ (if $op > 0 then ", \($op) open" else "" end)
|
|
156
|
+
)
|
|
157
|
+
| .[]' | tr -d '\r'
|
|
321
158
|
|
|
322
159
|
echo ""
|
|
323
|
-
|
|
160
|
+
local blocked_lines
|
|
161
|
+
blocked_lines="$(printf '%s' "$response" | jq -r '.data.children[] | select(.blocked == true) | " \(.id) blocked by \((.blocked_by // []) | join(","))"' | tr -d '\r')"
|
|
162
|
+
if [[ -n "$blocked_lines" ]]; then
|
|
324
163
|
echo "Blocked:"
|
|
325
|
-
|
|
326
|
-
[[ -z "$bline" ]] && continue
|
|
327
|
-
echo " $bline"
|
|
328
|
-
done <<< "$blocked_list"
|
|
164
|
+
printf '%s\n' "$blocked_lines"
|
|
329
165
|
else
|
|
330
166
|
echo "Blocked: none"
|
|
331
167
|
fi
|