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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Human-first rendering for the forge issue read surface (kernel issue a9bbd065,
|
|
4
|
+
// 0.1.0 critical path). `forge ready`, `forge list`, and `forge show` default to
|
|
5
|
+
// the compact text views below; the forge.issue.v1 JSON contract is unchanged and
|
|
6
|
+
// stays available behind --json (or FORGE_JSON=1). Dependency-free by design: no
|
|
7
|
+
// colors, no emoji, plain aligned text that survives any terminal or log file.
|
|
8
|
+
|
|
9
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
|
|
11
|
+
// Kernel UUIDs are unusable at a glance — display the 8-char prefix. Non-UUID ids
|
|
12
|
+
// (legacy `forge-2agy.2` style) are already short and MUST stay full: they are the
|
|
13
|
+
// only handle the CLI accepts. The full UUID remains accessible via `show` (which
|
|
14
|
+
// prints it verbatim) and via --json everywhere.
|
|
15
|
+
function shortId(id) {
|
|
16
|
+
if (typeof id !== 'string') return id == null ? '' : String(id);
|
|
17
|
+
return UUID_RE.test(id) ? id.slice(0, 8) : id;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Human-readable display handle: `<title-slug>-<short-id>` (kernel 1db53c60), e.g.
|
|
21
|
+
// "Add OAuth login" → "add-oauth-login-56a3a16d". The slug (first few title words) makes
|
|
22
|
+
// a queue scannable at a glance; the short-id keeps it unique and resolvable — `forge show
|
|
23
|
+
// <handle>` works because the resolver reads the trailing hex. The canonical id stays the
|
|
24
|
+
// full UUID; the handle is display + lookup sugar, recomputed from the current title.
|
|
25
|
+
function issueHandle(issue) {
|
|
26
|
+
const id = issue && issue.id != null ? String(issue.id) : '';
|
|
27
|
+
const short = shortId(id);
|
|
28
|
+
const title = issue && typeof issue.title === 'string' ? issue.title : '';
|
|
29
|
+
const slug = title
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
32
|
+
.replace(/^-+|-+$/g, '')
|
|
33
|
+
.split('-')
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.slice(0, 4)
|
|
36
|
+
.join('-');
|
|
37
|
+
return slug ? `${slug}-${short}` : short;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// gate.issue_verify (kernel 5f928cd0) attaches verified/mismatches to mutation
|
|
41
|
+
// envelopes. Human mode must surface — never hide — a failed or unconfirmed
|
|
42
|
+
// read-back, mirroring the JSON envelope's verified/mismatches keys.
|
|
43
|
+
function verificationLines(envelope) {
|
|
44
|
+
if (!envelope || envelope.verified === undefined || envelope.verified === true) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
if (envelope.verified === false) {
|
|
48
|
+
const lines = ['WARNING: read-back verification failed (gate.issue_verify):'];
|
|
49
|
+
for (const mismatch of Array.isArray(envelope.mismatches) ? envelope.mismatches : []) {
|
|
50
|
+
lines.push(` - ${mismatch}`);
|
|
51
|
+
}
|
|
52
|
+
return lines;
|
|
53
|
+
}
|
|
54
|
+
// verified === null — the verification read itself failed.
|
|
55
|
+
return ['WARNING: read-back verification could not confirm the result (gate.issue_verify).'];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Render rows as space-aligned columns. Every column except the last is padded to
|
|
59
|
+
// its widest cell; the last (title) runs free so long titles never distort the grid.
|
|
60
|
+
function renderTable(headers, rows) {
|
|
61
|
+
const all = [headers, ...rows];
|
|
62
|
+
const widths = headers.map((_, column) => (
|
|
63
|
+
column === headers.length - 1
|
|
64
|
+
? 0
|
|
65
|
+
: Math.max(...all.map(row => String(row[column] ?? '').length))
|
|
66
|
+
));
|
|
67
|
+
return all.map(row => row
|
|
68
|
+
.map((cell, column) => {
|
|
69
|
+
const text = String(cell ?? '');
|
|
70
|
+
return column === row.length - 1 ? text : text.padEnd(widths[column]);
|
|
71
|
+
})
|
|
72
|
+
.join(' ')
|
|
73
|
+
.replace(/\s+$/, ''));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// forge ready / forge list: one aligned row per issue. Columns mirror the triage
|
|
77
|
+
// decision: which issue (short id), what it is (type), where it stands (status),
|
|
78
|
+
// how urgent (priority), and what it says (title).
|
|
79
|
+
function renderIssueList(envelope, { emptyMessage = 'No issues found.' } = {}) {
|
|
80
|
+
const data = envelope && envelope.data && typeof envelope.data === 'object' ? envelope.data : {};
|
|
81
|
+
const issues = Array.isArray(data.issues) ? data.issues : [];
|
|
82
|
+
const lines = [];
|
|
83
|
+
|
|
84
|
+
if (issues.length === 0) {
|
|
85
|
+
lines.push(emptyMessage);
|
|
86
|
+
} else {
|
|
87
|
+
lines.push(...renderTable(
|
|
88
|
+
['ID', 'TYPE', 'STATUS', 'PRIORITY', 'TITLE'],
|
|
89
|
+
issues.map(issue => [
|
|
90
|
+
issueHandle(issue),
|
|
91
|
+
issue.type ?? '-',
|
|
92
|
+
issue.status ?? '-',
|
|
93
|
+
issue.priority ?? '-',
|
|
94
|
+
issue.title ?? '',
|
|
95
|
+
]),
|
|
96
|
+
));
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push(`${issues.length} issue${issues.length === 1 ? '' : 's'}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
lines.push(...verificationLines(envelope));
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function pushField(lines, label, value) {
|
|
106
|
+
if (value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
lines.push(`${label}: ${Array.isArray(value) ? value.join(', ') : value}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function pushSection(lines, label, text) {
|
|
113
|
+
if (typeof text !== 'string' || text.trim() === '') return;
|
|
114
|
+
lines.push('', `${label}:`, text);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// forge show: the full detail view. This is the surface that prints the FULL id
|
|
118
|
+
// (list/ready show only the 8-char prefix), so every field a script or human
|
|
119
|
+
// needs to act on the issue is reachable without --json.
|
|
120
|
+
function renderIssueShow(envelope) {
|
|
121
|
+
const issue = envelope && envelope.data && typeof envelope.data === 'object' ? envelope.data : {};
|
|
122
|
+
const lines = [];
|
|
123
|
+
|
|
124
|
+
lines.push(issue.title ?? '(untitled)');
|
|
125
|
+
lines.push(String(issue.id ?? ''));
|
|
126
|
+
lines.push('');
|
|
127
|
+
|
|
128
|
+
const status = issue.blocked === true ? `${issue.status ?? '-'} (blocked)` : (issue.status ?? '-');
|
|
129
|
+
const core = [`Type: ${issue.type ?? '-'}`, `Status: ${status}`, `Priority: ${issue.priority ?? '-'}`];
|
|
130
|
+
if (issue.rank !== null && issue.rank !== undefined) core.push(`Rank: ${issue.rank}`);
|
|
131
|
+
lines.push(core.join(' '));
|
|
132
|
+
|
|
133
|
+
pushField(lines, 'Labels', issue.labels);
|
|
134
|
+
pushField(lines, 'Assignee', issue.assignee);
|
|
135
|
+
pushField(lines, 'Claimed by', issue.claimed_by);
|
|
136
|
+
pushField(lines, 'Parent', shortId(issue.parent_id ?? null) || null);
|
|
137
|
+
pushField(lines, 'Blocked by', Array.isArray(issue.blocked_by) ? issue.blocked_by.map(shortId) : null);
|
|
138
|
+
pushField(lines, 'Dependencies', Array.isArray(issue.dependencies) ? issue.dependencies.map(shortId) : null);
|
|
139
|
+
pushField(lines, 'Dependents', Array.isArray(issue.dependents) ? issue.dependents.map(shortId) : null);
|
|
140
|
+
pushField(lines, 'Created', issue.created_at);
|
|
141
|
+
pushField(lines, 'Updated', issue.updated_at);
|
|
142
|
+
if (issue.closed_at) {
|
|
143
|
+
pushField(lines, 'Closed', issue.close_reason ? `${issue.closed_at} (${issue.close_reason})` : issue.closed_at);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (typeof issue.body === 'string' && issue.body.trim() !== '') {
|
|
147
|
+
lines.push('', issue.body);
|
|
148
|
+
}
|
|
149
|
+
pushSection(lines, 'Acceptance criteria', issue.acceptance_criteria);
|
|
150
|
+
pushSection(lines, 'Design', issue.design);
|
|
151
|
+
pushSection(lines, 'Notes', issue.notes);
|
|
152
|
+
|
|
153
|
+
const comments = Array.isArray(issue.comments) ? issue.comments : [];
|
|
154
|
+
if (comments.length > 0) {
|
|
155
|
+
lines.push('', `Comments (${comments.length}):`);
|
|
156
|
+
for (const comment of comments) {
|
|
157
|
+
const meta = [comment.created_at, comment.actor].filter(Boolean).join(' ');
|
|
158
|
+
lines.push(` [${meta}] ${comment.body ?? ''}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const verification = verificationLines(envelope);
|
|
163
|
+
if (verification.length > 0) {
|
|
164
|
+
lines.push('', ...verification);
|
|
165
|
+
}
|
|
166
|
+
return lines.join('\n');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Write subcommands → the past-tense verb used in their confirmation line.
|
|
170
|
+
const MUTATION_VERBS = {
|
|
171
|
+
create: 'Created',
|
|
172
|
+
update: 'Updated',
|
|
173
|
+
claim: 'Claimed',
|
|
174
|
+
release: 'Released',
|
|
175
|
+
comment: 'Commented on',
|
|
176
|
+
close: 'Closed',
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Concise, human-first confirmation for a write, used only at an interactive terminal
|
|
180
|
+
// (piped/non-interactive callers keep the machine-parseable JSON envelope — see the
|
|
181
|
+
// TTY gate in lib/commands/_issue.js).
|
|
182
|
+
function renderIssueMutation(subcommand, envelope) {
|
|
183
|
+
const issue = envelope && envelope.data && typeof envelope.data === 'object' ? envelope.data : {};
|
|
184
|
+
const verb = MUTATION_VERBS[subcommand] || 'Updated';
|
|
185
|
+
// Use the readable handle (slug-shortid) so a stream of confirmations stays scannable,
|
|
186
|
+
// consistent with `forge list`/`ready` (kernel 1db53c60).
|
|
187
|
+
const handle = issueHandle(issue) || '(issue)';
|
|
188
|
+
let headline = `✓ ${verb} ${handle}`;
|
|
189
|
+
if (issue.status) {
|
|
190
|
+
headline += ` [${issue.status}]`;
|
|
191
|
+
}
|
|
192
|
+
const lines = [headline];
|
|
193
|
+
const verification = verificationLines(envelope);
|
|
194
|
+
if (verification.length > 0) {
|
|
195
|
+
lines.push(...verification);
|
|
196
|
+
}
|
|
197
|
+
return lines.join('\n');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Dispatch a human rendering for one subcommand's success envelope.
|
|
201
|
+
function renderIssueEnvelope(subcommand, envelope) {
|
|
202
|
+
if (MUTATION_VERBS[subcommand]) {
|
|
203
|
+
return renderIssueMutation(subcommand, envelope);
|
|
204
|
+
}
|
|
205
|
+
if (subcommand === 'show') {
|
|
206
|
+
return renderIssueShow(envelope);
|
|
207
|
+
}
|
|
208
|
+
return renderIssueList(envelope, {
|
|
209
|
+
emptyMessage: subcommand === 'ready' ? 'No ready issues.' : 'No issues found.',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
shortId,
|
|
215
|
+
issueHandle,
|
|
216
|
+
renderIssueList,
|
|
217
|
+
renderIssueShow,
|
|
218
|
+
renderIssueMutation,
|
|
219
|
+
renderIssueEnvelope,
|
|
220
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const GITHUB_OWNED_FIELDS = new Set([
|
|
2
|
+
'github.number',
|
|
3
|
+
'github.nodeId',
|
|
4
|
+
'github.url',
|
|
5
|
+
'shared.title',
|
|
6
|
+
'shared.body',
|
|
7
|
+
'shared.state',
|
|
8
|
+
'shared.assignees',
|
|
9
|
+
'shared.labels',
|
|
10
|
+
'shared.milestone',
|
|
11
|
+
'sync.remoteUpdatedAt',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const FORGE_OWNED_FIELDS = new Set([
|
|
15
|
+
'forge.issueId',
|
|
16
|
+
'forge.dependencies',
|
|
17
|
+
'forge.parentId',
|
|
18
|
+
'forge.childIds',
|
|
19
|
+
'forge.workflowStage',
|
|
20
|
+
'forge.acceptanceCriteria',
|
|
21
|
+
'forge.progressNotes',
|
|
22
|
+
'forge.stageTransitions',
|
|
23
|
+
'forge.decisions',
|
|
24
|
+
'forge.memory',
|
|
25
|
+
'sync.lastPulledAt',
|
|
26
|
+
'sync.lastPushedAt',
|
|
27
|
+
'sync.pendingOutbound',
|
|
28
|
+
'sync.drift',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const CACHE_FIELDS = new Set([
|
|
32
|
+
'cache.githubSnapshot',
|
|
33
|
+
'cache.materializedIssue',
|
|
34
|
+
'cache.legacyLinkHints.mapping',
|
|
35
|
+
'cache.legacyLinkHints.githubIssue',
|
|
36
|
+
'cache.legacyLinkHints.syncComments',
|
|
37
|
+
'cache.legacyLinkHints.externalRef',
|
|
38
|
+
'cache.legacyLinkHints.descriptionUrl',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
function matchesFieldPath(fieldPath, knownFieldPath) {
|
|
42
|
+
return fieldPath === knownFieldPath || fieldPath.startsWith(`${knownFieldPath}.`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function hasFieldPath(fieldPath, fieldSet) {
|
|
46
|
+
if (typeof fieldPath !== 'string' || fieldPath.length === 0) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const knownFieldPath of fieldSet) {
|
|
51
|
+
if (matchesFieldPath(fieldPath, knownFieldPath)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getFieldAuthority(fieldPath) {
|
|
60
|
+
if (hasFieldPath(fieldPath, GITHUB_OWNED_FIELDS)) {
|
|
61
|
+
return 'github';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (hasFieldPath(fieldPath, FORGE_OWNED_FIELDS)) {
|
|
65
|
+
return 'forge';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (hasFieldPath(fieldPath, CACHE_FIELDS)) {
|
|
69
|
+
return 'cache';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isGitHubOwnedField(fieldPath) {
|
|
76
|
+
return getFieldAuthority(fieldPath) === 'github';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isForgeOwnedField(fieldPath) {
|
|
80
|
+
return getFieldAuthority(fieldPath) === 'forge';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isCacheField(fieldPath) {
|
|
84
|
+
return getFieldAuthority(fieldPath) === 'cache';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isSharedField(fieldPath) {
|
|
88
|
+
return typeof fieldPath === 'string' && fieldPath.startsWith('shared.') && isGitHubOwnedField(fieldPath);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
CACHE_FIELDS,
|
|
93
|
+
FORGE_OWNED_FIELDS,
|
|
94
|
+
GITHUB_OWNED_FIELDS,
|
|
95
|
+
getFieldAuthority,
|
|
96
|
+
isCacheField,
|
|
97
|
+
isForgeOwnedField,
|
|
98
|
+
isGitHubOwnedField,
|
|
99
|
+
isSharedField,
|
|
100
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeGitHubNumber } = require('./link-store.js');
|
|
4
|
+
|
|
5
|
+
const GITHUB_OWNED_FIELD_PATHS = [
|
|
6
|
+
'github.number',
|
|
7
|
+
'github.nodeId',
|
|
8
|
+
'github.url',
|
|
9
|
+
'shared.title',
|
|
10
|
+
'shared.body',
|
|
11
|
+
'shared.state',
|
|
12
|
+
'shared.assignees',
|
|
13
|
+
'shared.labels',
|
|
14
|
+
'shared.milestone',
|
|
15
|
+
'sync.remoteUpdatedAt',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function cloneValue(value) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.map(cloneValue);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (value && typeof value === 'object') {
|
|
24
|
+
const copy = {};
|
|
25
|
+
|
|
26
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
27
|
+
copy[key] = cloneValue(nestedValue);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return copy;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeText(value, fallback = '') {
|
|
37
|
+
return typeof value === 'string' ? value : fallback;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeState(value) {
|
|
41
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 'open';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeCollectionEntries(values) {
|
|
49
|
+
if (Array.isArray(values)) {
|
|
50
|
+
return values;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!values) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof values === 'object') {
|
|
58
|
+
if (Array.isArray(values.nodes)) {
|
|
59
|
+
return values.nodes;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Array.isArray(values.edges)) {
|
|
63
|
+
return values.edges
|
|
64
|
+
.map((edge) => edge?.node ?? edge)
|
|
65
|
+
.filter((entry) => entry != null);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return [values];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeNameList(values) {
|
|
73
|
+
const source = normalizeCollectionEntries(values);
|
|
74
|
+
const normalized = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
|
|
77
|
+
for (const entry of source) {
|
|
78
|
+
const name = typeof entry === 'string'
|
|
79
|
+
? entry
|
|
80
|
+
: entry?.login ?? entry?.name ?? entry?.title ?? null;
|
|
81
|
+
|
|
82
|
+
if (typeof name !== 'string' || name.length === 0 || seen.has(name)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
seen.add(name);
|
|
87
|
+
normalized.push(name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizeMilestone(milestone) {
|
|
94
|
+
if (!milestone) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof milestone === 'string') {
|
|
99
|
+
return milestone;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return milestone.title ?? milestone.name ?? null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normalizeNodeId(issue = {}) {
|
|
106
|
+
if (typeof issue.node_id === 'string' && issue.node_id.length > 0) {
|
|
107
|
+
return issue.node_id;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (typeof issue.nodeId === 'string' && issue.nodeId.length > 0) {
|
|
111
|
+
return issue.nodeId;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (typeof issue.id === 'string' && issue.id.length > 0) {
|
|
115
|
+
return issue.id;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeIssueUrl(issue = {}) {
|
|
122
|
+
const candidates = [issue.html_url, issue.htmlUrl, issue.url];
|
|
123
|
+
|
|
124
|
+
for (const candidate of candidates) {
|
|
125
|
+
if (typeof candidate !== 'string' || candidate.length === 0) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (/^https:\/\/api\.github\.com\//iu.test(candidate)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return candidate;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeAssignees(issue = {}) {
|
|
140
|
+
const assignees = normalizeNameList(issue.assignees);
|
|
141
|
+
|
|
142
|
+
if (assignees.length > 0) {
|
|
143
|
+
return assignees;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return normalizeNameList(issue.assignee);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeRemoteIssue(issue = {}) {
|
|
150
|
+
return {
|
|
151
|
+
github: {
|
|
152
|
+
number: normalizeGitHubNumber(issue.number ?? issue.issue_number ?? issue.issueNumber),
|
|
153
|
+
nodeId: normalizeNodeId(issue),
|
|
154
|
+
url: normalizeIssueUrl(issue),
|
|
155
|
+
},
|
|
156
|
+
shared: {
|
|
157
|
+
title: normalizeText(issue.title),
|
|
158
|
+
body: normalizeText(issue.body),
|
|
159
|
+
state: normalizeState(issue.state),
|
|
160
|
+
assignees: normalizeAssignees(issue),
|
|
161
|
+
labels: normalizeNameList(issue.labels),
|
|
162
|
+
milestone: normalizeMilestone(issue.milestone),
|
|
163
|
+
},
|
|
164
|
+
sync: {
|
|
165
|
+
remoteUpdatedAt: issue.updated_at ?? issue.updatedAt ?? null,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const normalizeGitHubIssuePayload = normalizeRemoteIssue;
|
|
171
|
+
|
|
172
|
+
module.exports = {
|
|
173
|
+
GITHUB_OWNED_FIELD_PATHS,
|
|
174
|
+
cloneValue,
|
|
175
|
+
normalizeGitHubIssuePayload,
|
|
176
|
+
normalizeAssignees,
|
|
177
|
+
normalizeCollectionEntries,
|
|
178
|
+
normalizeIssueUrl,
|
|
179
|
+
normalizeMilestone,
|
|
180
|
+
normalizeNameList,
|
|
181
|
+
normalizeNodeId,
|
|
182
|
+
normalizeRemoteIssue,
|
|
183
|
+
normalizeState,
|
|
184
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { cloneValue, normalizeRemoteIssue: normalizeSharedRemoteIssue } = require('./github-pull.js');
|
|
4
|
+
const { reconcileSharedIssueRecord } = require('./reconcile.js');
|
|
5
|
+
const { resolveCanonicalLink } = require('./link-store.js');
|
|
6
|
+
|
|
7
|
+
function toIssueArray(page = {}) {
|
|
8
|
+
if (Array.isArray(page)) {
|
|
9
|
+
return page;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (!page || typeof page !== 'object') {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (Array.isArray(page.nodes)) {
|
|
17
|
+
return page.nodes;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (Array.isArray(page.items)) {
|
|
21
|
+
return page.items;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(page.issues)) {
|
|
25
|
+
return page.issues;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Array.isArray(page.edges)) {
|
|
29
|
+
return page.edges.map((edge) => edge?.node).filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const nestedNodes = page.data?.repository?.issues?.nodes;
|
|
33
|
+
if (Array.isArray(nestedNodes)) {
|
|
34
|
+
return nestedNodes;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const nestedEdges = page.data?.repository?.issues?.edges;
|
|
38
|
+
if (Array.isArray(nestedEdges)) {
|
|
39
|
+
return nestedEdges.map((edge) => edge?.node).filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function listRemoteIssues(page = {}) {
|
|
46
|
+
return toIssueArray(page).map(cloneValue);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeRemoteIssue(issue = {}) {
|
|
50
|
+
return normalizeSharedRemoteIssue(issue);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveSharedLink(linkStore, remoteIssue = {}) {
|
|
54
|
+
if (!linkStore) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const lookup = {
|
|
59
|
+
githubNodeId: remoteIssue.github?.nodeId ?? remoteIssue.node_id ?? remoteIssue.nodeId ?? null,
|
|
60
|
+
githubNumber: remoteIssue.github?.number ?? remoteIssue.number ?? remoteIssue.issue_number ?? remoteIssue.issueNumber ?? null,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (typeof linkStore.resolveCanonicalLink === 'function') {
|
|
64
|
+
return linkStore.resolveCanonicalLink(lookup);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof linkStore === 'object' && typeof linkStore.byGitHubNumber?.get === 'function') {
|
|
68
|
+
return resolveCanonicalLink(linkStore, lookup);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function materializeLocalIssue(localRecord = {}, remoteIssue = {}, linkStore, options = {}) {
|
|
75
|
+
const normalizedRemoteIssue = remoteIssue?.github && remoteIssue?.shared && remoteIssue?.sync
|
|
76
|
+
? cloneValue(remoteIssue)
|
|
77
|
+
: normalizeRemoteIssue(remoteIssue);
|
|
78
|
+
|
|
79
|
+
const link = resolveSharedLink(linkStore, normalizedRemoteIssue);
|
|
80
|
+
const { record, diagnostics } = reconcileSharedIssueRecord(
|
|
81
|
+
localRecord,
|
|
82
|
+
normalizedRemoteIssue,
|
|
83
|
+
options.reconcileOptions ?? {},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
link,
|
|
88
|
+
diagnostics,
|
|
89
|
+
record,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
listRemoteIssues,
|
|
95
|
+
materializeLocalIssue,
|
|
96
|
+
normalizeRemoteIssue,
|
|
97
|
+
resolveSharedLink,
|
|
98
|
+
};
|