forge-workflow 0.0.10 → 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 +150 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -118
- 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 +461 -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/forge/TOOLCHAIN.md +670 -0
- 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/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- 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 +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +741 -77
- 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 +17 -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 +0 -1
- 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 +838 -972
- 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 +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +307 -100
- 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 +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- 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 +382 -11
- 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/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 +3 -2
- 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 +99 -497
- 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 +241 -20
- 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/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- 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 +296 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- 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 +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- 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.sh +22 -3
- 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 +49 -84
- 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 +50 -83
- 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/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 +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- 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-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- 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} +44 -50
- 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} +132 -157
- 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/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- 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/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- 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/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- 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 -281
- 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-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- 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 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- 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,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
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Env overrides (for testing):
|
|
10
10
|
# GH_CMD — Path to gh binary (default: gh)
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# FORGE_CMD — Path to forge binary (default: forge)
|
|
12
|
+
# FORGE_ROOT — Path to .forge/ parent directory (default: .)
|
|
13
13
|
#
|
|
14
14
|
# This file does NOT set errexit/pipefail — callers manage their own shell options.
|
|
15
15
|
|
|
@@ -56,11 +56,11 @@ _hooks_warn() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
# ── _check_auto_sync_enabled ─────────────────────────────────────────────
|
|
59
|
-
# Reads .
|
|
59
|
+
# Reads .forge/config.yaml for the team.auto-sync setting.
|
|
60
60
|
# Returns 0 if enabled (default), 1 if disabled.
|
|
61
61
|
_check_auto_sync_enabled() {
|
|
62
|
-
local
|
|
63
|
-
local config_file="$
|
|
62
|
+
local forge_root="${FORGE_ROOT:-.}"
|
|
63
|
+
local config_file="$forge_root/.forge/config.yaml"
|
|
64
64
|
|
|
65
65
|
if [[ ! -f "$config_file" ]]; then
|
|
66
66
|
# Default: enabled
|
|
@@ -108,7 +108,7 @@ forge_team_sync() {
|
|
|
108
108
|
done
|
|
109
109
|
|
|
110
110
|
local gh_cmd="${GH_CMD:-gh}"
|
|
111
|
-
local
|
|
111
|
+
local forge_cmd="${FORGE_CMD:-forge}"
|
|
112
112
|
|
|
113
113
|
# 1. Check gh auth status
|
|
114
114
|
if ! "$gh_cmd" auth status >/dev/null 2>&1; then
|
|
@@ -122,44 +122,42 @@ forge_team_sync() {
|
|
|
122
122
|
return 0
|
|
123
123
|
fi
|
|
124
124
|
|
|
125
|
-
# 3. Get all in_progress issues
|
|
126
|
-
local
|
|
127
|
-
|
|
125
|
+
# 3. Get all in_progress issues as JSON
|
|
126
|
+
local list_json
|
|
127
|
+
list_json="$("$forge_cmd" issue list --status=in_progress --json 2>/dev/null)" || {
|
|
128
128
|
if [[ "$quiet" == "false" ]]; then
|
|
129
129
|
_hooks_warn "Failed to list issues, sync skipped"
|
|
130
130
|
fi
|
|
131
131
|
return 0
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
#
|
|
135
|
-
|
|
134
|
+
# Build "id<TAB>has_github" lines (has_github is true when a github_issue:<n>
|
|
135
|
+
# label is present on the issue).
|
|
136
|
+
local issue_lines
|
|
137
|
+
issue_lines="$(printf '%s' "$list_json" | jq -r '
|
|
138
|
+
(.data.issues // [])[]
|
|
139
|
+
| [ .id, (((.labels // []) | any(test("^github_issue:"))) | tostring) ]
|
|
140
|
+
| join("\t")' 2>/dev/null | tr -d '\r')"
|
|
141
|
+
|
|
142
|
+
if [[ -z "$issue_lines" ]] || [[ -z "$(printf '%s' "$issue_lines" | tr -d '[:space:]')" ]]; then
|
|
136
143
|
if [[ "$quiet" == "false" ]]; then
|
|
137
144
|
_hooks_info "Nothing to sync"
|
|
138
145
|
fi
|
|
139
146
|
return 0
|
|
140
147
|
fi
|
|
141
148
|
|
|
142
|
-
# 4. For each issue
|
|
149
|
+
# 4. For each issue with a github_issue label, sync its status to GitHub
|
|
143
150
|
local synced=0
|
|
144
151
|
local warnings=0
|
|
145
152
|
|
|
146
|
-
while IFS
|
|
147
|
-
[[ -z "$line" ]] && continue
|
|
148
|
-
|
|
149
|
-
# Extract issue id
|
|
150
|
-
local issue_id
|
|
151
|
-
issue_id="$(echo "$line" | grep -oE 'forge-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*' | head -1)"
|
|
153
|
+
while IFS=$'\t' read -r issue_id has_github; do
|
|
152
154
|
[[ -z "$issue_id" ]] && continue
|
|
153
155
|
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
local gh_issue_num
|
|
160
|
-
gh_issue_num="$(printf '%s' "$show_output" | grep -oP 'github_issue:\K[0-9]+' | head -1 || true)"
|
|
161
|
-
|
|
162
|
-
if [[ -z "$gh_issue_num" ]]; then
|
|
156
|
+
# Skip only when there is neither a github_issue:<n> label nor a legacy
|
|
157
|
+
# mapping-file association. _get_github_issue_number (from sync-github.sh)
|
|
158
|
+
# checks the label first, then the mapping-file fallback, so mapping-only
|
|
159
|
+
# linked issues are still synced instead of being skipped here.
|
|
160
|
+
if [[ "$has_github" != "true" ]] && ! _get_github_issue_number "$issue_id" >/dev/null 2>&1; then
|
|
163
161
|
# No GitHub issue linked, skip but warn
|
|
164
162
|
warnings=$((warnings + 1))
|
|
165
163
|
if [[ "$quiet" == "false" ]]; then
|
|
@@ -177,7 +175,7 @@ forge_team_sync() {
|
|
|
177
175
|
_hooks_warn "Failed to sync $issue_id to GitHub"
|
|
178
176
|
fi
|
|
179
177
|
fi
|
|
180
|
-
done <<< "$
|
|
178
|
+
done <<< "$issue_lines"
|
|
181
179
|
|
|
182
180
|
# 5. Report
|
|
183
181
|
if [[ "$quiet" == "false" ]]; then
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# scripts/forge-team/lib/identity.sh — GitHub-username-only identity mapping
|
|
3
3
|
#
|
|
4
4
|
# Auto-detects developer via `gh api user`. Stores identity in
|
|
5
|
-
# .
|
|
5
|
+
# .forge/team-map.jsonl (append-only, LWW per GitHub username).
|
|
6
6
|
#
|
|
7
7
|
# Functions:
|
|
8
8
|
# get_github_user — Current dev's GitHub username (cached)
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# Env overrides:
|
|
16
16
|
# GH_CMD — Path to gh binary (for testing)
|
|
17
|
-
# TEAM_MAP_ROOT — Root dir for .
|
|
17
|
+
# TEAM_MAP_ROOT — Root dir for .forge/team-map.jsonl (default: .)
|
|
18
18
|
#
|
|
19
19
|
# This file does NOT set errexit/pipefail — callers manage their own shell options.
|
|
20
20
|
|
|
@@ -69,7 +69,7 @@ _identity_prompt() {
|
|
|
69
69
|
# _team_map_file — Returns the path to team-map.jsonl
|
|
70
70
|
_team_map_file() {
|
|
71
71
|
local root="${TEAM_MAP_ROOT:-.}"
|
|
72
|
-
printf '%s' "$root/.
|
|
72
|
+
printf '%s' "$root/.forge/team-map.jsonl"
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
# _validate_github_username <username>
|
|
@@ -124,7 +124,7 @@ get_github_user() {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
# team_map_add <github-user> [display-name]
|
|
127
|
-
# Adds/updates entry in .
|
|
127
|
+
# Adds/updates entry in .forge/team-map.jsonl.
|
|
128
128
|
# Schema: {"github":"<user>","display_name":"<name>","updated_at":"<ts>","is_bot":false}
|
|
129
129
|
team_map_add() {
|
|
130
130
|
local github_user="${1:-}"
|