karajan-code 4.70.4 → 5.0.1
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/README.md +2 -2
- package/package.json +6 -14
- package/packages/hu-board/package.json +1 -1
- package/packages/hu-board/public/app.js +4 -18
- package/packages/hu-board/public/index.html +5 -2
- package/packages/hu-board/public/styles.css +26 -0
- package/packages/hu-board/public/utils/api.js +0 -74
- package/packages/hu-board/public/utils/board-tour.js +62 -0
- package/packages/hu-board/public/utils/board-view.js +49 -144
- package/packages/hu-board/public/utils/command-launcher.js +8 -70
- package/packages/hu-board/public/utils/formatters.js +10 -0
- package/packages/hu-board/public/utils/governance-view.js +13 -1
- package/packages/hu-board/public/utils/hu-actions.js +17 -68
- package/packages/hu-board/public/utils/init-listeners.js +7 -8
- package/packages/hu-board/public/utils/log-panel.js +3 -16
- package/packages/hu-board/public/utils/maggle-mode.js +2 -12
- package/packages/hu-board/public/utils/modals.js +7 -0
- package/packages/hu-board/public/utils/project-picker-view.js +1 -1
- package/packages/hu-board/public/utils/server-push.js +3 -22
- package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
- package/packages/hu-board/public/utils/story-detail-view.js +5 -19
- package/packages/hu-board/src/cleanup-zombies.js +0 -1
- package/packages/hu-board/src/command-runner.js +36 -25
- package/packages/hu-board/src/db-connection.js +282 -0
- package/packages/hu-board/src/db-paths.js +113 -0
- package/packages/hu-board/src/db-queries.js +188 -0
- package/packages/hu-board/src/db-tombstones.js +51 -0
- package/packages/hu-board/src/db.js +28 -619
- package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
- package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
- package/packages/hu-board/src/plan-mutations.js +3 -153
- package/packages/hu-board/src/plans-service.js +55 -0
- package/packages/hu-board/src/routes/api/commands.js +122 -0
- package/packages/hu-board/src/routes/api/plans.js +55 -0
- package/packages/hu-board/src/routes/api/projects.js +267 -0
- package/packages/hu-board/src/routes/api/prompts.js +135 -0
- package/packages/hu-board/src/routes/api/sessions.js +95 -0
- package/packages/hu-board/src/routes/api/stories.js +183 -0
- package/packages/hu-board/src/routes/api/system.js +241 -0
- package/packages/hu-board/src/routes/api/tombstones.js +38 -0
- package/packages/hu-board/src/routes/api.js +24 -1371
- package/packages/hu-board/src/routes/governance.js +23 -1
- package/packages/hu-board/src/routes/lanes.js +40 -0
- package/packages/hu-board/src/routes/rag.js +4 -3
- package/packages/hu-board/src/server.js +20 -34
- package/packages/hu-board/src/sync-files.js +318 -0
- package/packages/hu-board/src/sync-project.js +99 -0
- package/packages/hu-board/src/sync.js +22 -566
- package/packages/hu-board/src/zombie-reaper.js +1 -1
- package/scripts/dual-publish.mjs +19 -3
- package/scripts/postinstall.js +15 -2
- package/scripts/verify-pack-mcp.mjs +11 -7
- package/scripts/verify-pack.mjs +4 -4
- package/src/audit/audit-history.js +3 -2
- package/src/audit/dead-exports.js +68 -59
- package/src/audit/deterministic-summary.js +33 -13
- package/src/audit/npm-signatures.js +41 -0
- package/src/audit/sbom.js +35 -0
- package/src/checks/agent-token.js +112 -0
- package/src/checks/dir-setup.js +34 -1
- package/src/checks/native-build.js +21 -39
- package/src/checks/runner.js +128 -142
- package/src/cli/advanced-commands.js +6 -6
- package/src/cli/register-meta.js +42 -82
- package/src/cli/register-pipeline.js +8 -143
- package/src/cli/register-plan.js +9 -100
- package/src/cli/register-roles-skills.js +1 -1
- package/src/cli/retired-commands.js +49 -0
- package/src/cli.js +18 -5
- package/src/commands/agent-run.js +40 -26
- package/src/commands/audit.js +127 -124
- package/src/commands/board.js +5 -9
- package/src/commands/check.js +29 -16
- package/src/commands/doctor.js +2 -0
- package/src/commands/env.js +176 -150
- package/src/commands/go.js +71 -53
- package/src/commands/harden.js +90 -81
- package/src/commands/hu-start.js +52 -0
- package/src/commands/hu.js +288 -221
- package/src/commands/identity.js +111 -78
- package/src/commands/init-atril.js +47 -0
- package/src/commands/init.js +11 -40
- package/src/commands/install-tools.js +70 -85
- package/src/commands/main-guard.js +78 -0
- package/src/commands/policy.js +226 -184
- package/src/commands/qmd.js +27 -21
- package/src/commands/rag.js +121 -55
- package/src/commands/review-gate.js +421 -341
- package/src/commands/skills.js +4 -1
- package/src/commands/start.js +4 -5
- package/src/commands/steward.js +96 -47
- package/src/commands/tournament.js +46 -37
- package/src/commands/worktree.js +107 -26
- package/src/config/defaults.js +1 -0
- package/src/config/loader.js +34 -17
- package/src/config/role-resolver.js +1 -0
- package/src/config/schema.js +1 -0
- package/src/config/secrets.js +36 -0
- package/src/config.js +1 -0
- package/src/environment/adr.js +16 -2
- package/src/git/karajan-label.js +20 -8
- package/src/guards/main-guard.js +54 -0
- package/src/harden/hook-templates.js +16 -1
- package/src/harden/sentinel/posttooluse.mjs +18 -6
- package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
- package/src/harden/sentinel/sentinel-lib.mjs +129 -26
- package/src/harden/sentinel/sentinel-shell.mjs +5 -16
- package/src/harden/sentinel/sessionstart.mjs +3 -2
- package/src/harden/sentinel/stop.mjs +5 -4
- package/src/harden/workflow-templates.js +1 -1
- package/src/hu/board-lookup.js +29 -0
- package/src/hu/gh-identity.js +23 -0
- package/src/hu/github-board.js +4 -2
- package/src/hu/lane-prune.js +83 -13
- package/src/hu/lane-setup.js +65 -0
- package/src/hu/lanes.js +66 -0
- package/src/hu/team-board-mirror.js +3 -2
- package/src/infrastructure/command-runner.js +1 -1
- package/src/infrastructure/file-system-service.js +1 -1
- package/src/mcp/handlers/direct-handlers.js +2 -292
- package/src/mcp/handlers/hu-handlers.js +1 -35
- package/src/mcp/handlers/management-handlers.js +2 -42
- package/src/mcp/handlers/rag-handler.js +9 -1
- package/src/mcp/server-handlers.js +16 -22
- package/src/mcp/tools.js +1 -196
- package/src/plan/lane-plans.js +54 -0
- package/src/plan/plan-store.js +7 -4
- package/src/policy/add.js +26 -12
- package/src/policy/adr-number-check.js +31 -0
- package/src/prompts/audit.js +299 -339
- package/src/prompts/card-context.js +10 -5
- package/src/prompts/reviewer.js +5 -2
- package/src/rag/auto-update.js +71 -44
- package/src/rag/bootstrap-index.js +46 -54
- package/src/rag/indexer.js +9 -4
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +181 -0
- package/src/rag/project-store.js +2 -12
- package/src/review/card-first.js +57 -46
- package/src/review/gate-gitignore.js +23 -4
- package/src/review/gate-hook.js +12 -4
- package/src/review/one-shot-review.js +45 -35
- package/src/review/publish-pr.js +14 -2
- package/src/review/repo-docs.js +73 -0
- package/src/review/repo-stack.js +59 -0
- package/src/review/reviewer-fallback.js +19 -4
- package/src/review/seal-commit.js +29 -0
- package/src/review/sonar-pregate.js +59 -44
- package/src/review/third-party-review.js +90 -16
- package/src/review/third-party-size.js +43 -0
- package/src/review/third-party-skills.js +54 -0
- package/src/review/verify-pr.js +1 -24
- package/src/roles/audit-role.js +106 -125
- package/src/skills/atril-cli.js +19 -1
- package/src/skills/atril-skills.js +5 -4
- package/src/skills/skills-search.js +72 -0
- package/src/skills/translate.js +62 -0
- package/src/sonar/scanner.js +116 -101
- package/src/steward/invariants.js +5 -4
- package/src/steward/phantom-sweep.js +66 -0
- package/src/tournament/judge.js +60 -53
- package/src/utils/gh-account.js +37 -0
- package/src/utils/git.js +0 -13
- package/src/utils/home-migration.js +7 -1
- package/src/utils/main-checkout.js +53 -0
- package/src/utils/qmd-collection.js +3 -1
- package/src/utils/welcome.js +3 -3
- package/templates/{skills → atril/commands}/kj-architect.md +5 -0
- package/templates/{skills → atril/commands}/kj-audit.md +5 -0
- package/templates/{skills → atril/commands}/kj-board.md +5 -0
- package/templates/{skills → atril/commands}/kj-code.md +5 -0
- package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
- package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
- package/templates/{skills → atril/commands}/kj-discover.md +5 -0
- package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
- package/templates/{skills → atril/commands}/kj-review.md +5 -0
- package/templates/{skills → atril/commands}/kj-security.md +5 -0
- package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
- package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
- package/templates/{skills → atril/commands}/kj-test.md +5 -0
- package/packages/hu-board/public/utils/run-launcher.js +0 -169
- package/packages/hu-board/src/run-tracker.js +0 -121
- package/src/activity-log.js +0 -59
- package/src/agents/host-agent.js +0 -38
- package/src/agents/model-registry.js +0 -142
- package/src/audit/plan-adherence.js +0 -224
- package/src/autonomy/arbiter-role.js +0 -71
- package/src/autonomy/arbiter.js +0 -22
- package/src/autonomy/decider.js +0 -18
- package/src/autonomy/decision-resolver.js +0 -58
- package/src/autonomy/outcome-report.js +0 -55
- package/src/autonomy/stage-ask.js +0 -30
- package/src/brain/decision-tracker.js +0 -125
- package/src/brain/decisor.js +0 -243
- package/src/brain/solomon-consult.js +0 -171
- package/src/brain/standby-scheduler.js +0 -8
- package/src/budget/comparison.js +0 -96
- package/src/canvas/parse-md.js +0 -238
- package/src/canvas/schema.js +0 -101
- package/src/canvas/to-flat.js +0 -85
- package/src/canvas/validate.js +0 -163
- package/src/ci/dispatch.js +0 -99
- package/src/ci/index.js +0 -3
- package/src/ci/pr-diff.js +0 -26
- package/src/cli/register-standby.js +0 -26
- package/src/commands/architect.js +0 -111
- package/src/commands/autorun.js +0 -49
- package/src/commands/researcher.js +0 -54
- package/src/commands/resume.js +0 -72
- package/src/commands/review.js +0 -89
- package/src/commands/run.js +0 -185
- package/src/commands/standby.js +0 -117
- package/src/commands/status.js +0 -36
- package/src/commands/triage.js +0 -78
- package/src/commands/undo.js +0 -62
- package/src/domains/domain-loader.js +0 -245
- package/src/domains/domain-registry.js +0 -140
- package/src/domains/domain-synthesizer.js +0 -129
- package/src/git/automation.js +0 -433
- package/src/git/coder-commit-guard.js +0 -40
- package/src/git/hu-automation.js +0 -243
- package/src/git/hu-snapshot.js +0 -10
- package/src/git/symlink-targets.js +0 -57
- package/src/golden/asserter.js +0 -92
- package/src/golden/loader.js +0 -52
- package/src/golden/runner.js +0 -92
- package/src/golden/schema.js +0 -41
- package/src/guards/duplicate-members.js +0 -86
- package/src/guards/intent-guard.js +0 -161
- package/src/guards/perf-guard.js +0 -126
- package/src/guards/policy-guard.js +0 -90
- package/src/guards/policy-resolver.js +0 -85
- package/src/guards/secret-redactor.js +0 -71
- package/src/hu/acceptance-runner.js +0 -138
- package/src/hu/lazy-planner.js +0 -109
- package/src/hu/parallel-executor.js +0 -94
- package/src/hu/routing-metrics.js +0 -17
- package/src/hu/split-validator.js +0 -143
- package/src/hu/splitting-detector.js +0 -158
- package/src/hu/splitting-generator.js +0 -175
- package/src/hu/store.js +0 -394
- package/src/infrastructure/mocks.js +0 -182
- package/src/mcp/handlers/run-handler.js +0 -318
- package/src/mcp/sovereignty-guard.js +0 -113
- package/src/mcp/tool-arg-normalizers.js +0 -16
- package/src/mutate/reviewer-signal.js +0 -76
- package/src/orchestrator/agent-fallback.js +0 -81
- package/src/orchestrator/brain-coordinator.js +0 -186
- package/src/orchestrator/ci-integration.js +0 -139
- package/src/orchestrator/config-init.js +0 -426
- package/src/orchestrator/direct-actions.js +0 -217
- package/src/orchestrator/drivers/error-recovery.js +0 -287
- package/src/orchestrator/drivers/init-context.js +0 -238
- package/src/orchestrator/drivers/iteration-loop.js +0 -382
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
- package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
- package/src/orchestrator/drivers/post-loop.js +0 -404
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
- package/src/orchestrator/drivers/pre-loop.js +0 -524
- package/src/orchestrator/drivers/run-hu-batch.js +0 -468
- package/src/orchestrator/feedback-enrichment.js +0 -140
- package/src/orchestrator/feedback-queue.js +0 -151
- package/src/orchestrator/flow-control.js +0 -170
- package/src/orchestrator/flow-runner.js +0 -365
- package/src/orchestrator/fs-leak-detector.js +0 -228
- package/src/orchestrator/hu-scheduler.js +0 -114
- package/src/orchestrator/hu-sub-pipeline.js +0 -642
- package/src/orchestrator/iteration-gate.js +0 -73
- package/src/orchestrator/iteration-stages.js +0 -9
- package/src/orchestrator/parallel-limiter.js +0 -74
- package/src/orchestrator/pipeline-context.js +0 -72
- package/src/orchestrator/post-loop-stages.js +0 -468
- package/src/orchestrator/pre-loop-stages.js +0 -11
- package/src/orchestrator/preflight-checks.js +0 -487
- package/src/orchestrator/repair/repair-detector.js +0 -117
- package/src/orchestrator/repair/repair-runner.js +0 -142
- package/src/orchestrator/reviewer-fallback.js +0 -45
- package/src/orchestrator/role-output-compressor.js +0 -189
- package/src/orchestrator/session-journal.js +0 -308
- package/src/orchestrator/solomon-escalation.js +0 -331
- package/src/orchestrator/solomon-rules.js +0 -212
- package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
- package/src/orchestrator/stages/architect-stage.js +0 -172
- package/src/orchestrator/stages/coder-stage.js +0 -506
- package/src/orchestrator/stages/domain-curator-stage.js +0 -82
- package/src/orchestrator/stages/format-stage.js +0 -61
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
- package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
- package/src/orchestrator/stages/perf-stage.js +0 -140
- package/src/orchestrator/stages/planner-stage.js +0 -85
- package/src/orchestrator/stages/rag-context-stage.js +0 -80
- package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
- package/src/orchestrator/stages/research-stage.js +0 -69
- package/src/orchestrator/stages/reviewer-stage.js +0 -431
- package/src/orchestrator/stages/sonar-stage.js +0 -439
- package/src/orchestrator/stages/stage-classes.js +0 -84
- package/src/orchestrator/stages/stage-executor.js +0 -155
- package/src/orchestrator/stages/suite-green-stage.js +0 -62
- package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
- package/src/orchestrator/stages/tool-judge-stage.js +0 -53
- package/src/orchestrator/stages/triage-stage.js +0 -172
- package/src/orchestrator/standby.js +0 -71
- package/src/orchestrator/verification-gate.js +0 -183
- package/src/orchestrator.js +0 -22
- package/src/plan/adr-generator.js +0 -170
- package/src/plan/adr-loader.js +0 -60
- package/src/plan/plan-executor.js +0 -100
- package/src/plan/plan-validation.js +0 -4
- package/src/prompts/acceptance-coverage.js +0 -32
- package/src/prompts/arbiter.js +0 -38
- package/src/prompts/architect.js +0 -103
- package/src/prompts/hu-reviewer.js +0 -244
- package/src/prompts/tool-correctness-judge.js +0 -31
- package/src/prompts/triage.js +0 -57
- package/src/rag/embedder.js +0 -2
- package/src/rag/embedders/_cloud-base.js +0 -2
- package/src/rag/embedders/cohere.js +0 -2
- package/src/rag/embedders/mistral.js +0 -2
- package/src/rag/embedders/onnx.js +0 -2
- package/src/rag/embedders/openai.js +0 -2
- package/src/rag/embedders/voyage.js +0 -2
- package/src/rag/rerank.js +0 -4
- package/src/rag/where-parser.js +0 -4
- package/src/repeat-detector.js +0 -88
- package/src/review/diff-generator.js +0 -112
- package/src/review/git-stash-helper.js +0 -104
- package/src/review/schema.js +0 -31
- package/src/review/scope-filter.js +0 -152
- package/src/review/snapshot-diff.js +0 -122
- package/src/review/tdd-discipline.js +0 -90
- package/src/review/tdd-policy.js +0 -69
- package/src/roles/acceptance-coverage-role.js +0 -78
- package/src/roles/architect-role.js +0 -77
- package/src/roles/coder-role.js +0 -113
- package/src/roles/commiter-role.js +0 -113
- package/src/roles/domain-curator-role.js +0 -134
- package/src/roles/hu-reviewer-role.js +0 -64
- package/src/roles/impeccable-role.js +0 -85
- package/src/roles/index.js +0 -18
- package/src/roles/karajan-brain-role.js +0 -134
- package/src/roles/perf-role.js +0 -122
- package/src/roles/planner-role.js +0 -151
- package/src/roles/refactorer-role.js +0 -30
- package/src/roles/repairer-role.js +0 -181
- package/src/roles/researcher-role.js +0 -70
- package/src/roles/reviewer-role.js +0 -115
- package/src/roles/security-role.js +0 -73
- package/src/roles/solomon-role.js +0 -271
- package/src/roles/sonar-role.js +0 -72
- package/src/roles/tester-role.js +0 -235
- package/src/roles/tool-correctness-judge-role.js +0 -78
- package/src/roles/triage-role.js +0 -99
- package/src/session/journal/decisions-writer.js +0 -200
- package/src/session/journal/iteration-logger.js +0 -194
- package/src/session/journal/summary-writer.js +0 -320
- package/src/session/journal/tree-writer.js +0 -164
- package/src/session/journal/write-all.js +0 -89
- package/src/session-cleanup.js +0 -116
- package/src/skills/semantic-detector.js +0 -167
- package/src/skills/skill-detector.js +0 -385
- package/src/sonar/cloud-scanner.js +0 -78
- package/src/tracker-bootstrap.js +0 -44
- package/src/types/index.js +0 -38
- package/src/types/policy.js +0 -43
- package/src/types/solomon.js +0 -87
- package/src/utils/async-lock.js +0 -31
- package/src/utils/budget.js +0 -229
- package/src/utils/cwd-confirm.js +0 -39
- package/src/utils/display/event-handlers.js +0 -403
- package/src/utils/display/formatters.js +0 -132
- package/src/utils/display/header.js +0 -47
- package/src/utils/display/session-summary.js +0 -118
- package/src/utils/display/solomon.js +0 -42
- package/src/utils/pricing.js +0 -29
- package/src/utils/quota-simulator.js +0 -56
- package/src/utils/role-assigner.js +0 -137
- package/src/utils/rtk-wrapper.js +0 -87
- package/src/utils/run-registry.js +0 -9
- package/src/utils/status-dashboard.js +0 -254
- package/src/utils/tool-call-extractor.js +0 -56
- package/src/webperf/devtools-detect.js +0 -112
- package/src/webperf/webperf-role-integration.js +0 -66
- package/templates/skills/kj-run.md +0 -73
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Domain context synthesizer.
|
|
3
|
-
* Filters relevant sections from domain files based on task + hints,
|
|
4
|
-
* and compacts the output to stay within a token budget.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const DEFAULT_MAX_TOKENS = 4000;
|
|
8
|
-
const CHARS_PER_TOKEN = 4;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Synthesize a domain context string from selected domains,
|
|
12
|
-
* filtered by relevance to the task and domain hints.
|
|
13
|
-
*
|
|
14
|
-
* @param {Object} params
|
|
15
|
-
* @param {string} params.task — task description
|
|
16
|
-
* @param {string[]} params.domainHints — keywords from triage
|
|
17
|
-
* @param {import('./domain-loader.js').DomainFile[]|null} params.selectedDomains
|
|
18
|
-
* @param {number} [params.maxTokens=4000] — token budget (0 = unlimited)
|
|
19
|
-
* @returns {string} synthesized markdown context or empty string
|
|
20
|
-
*/
|
|
21
|
-
export function synthesizeDomainContext({ task, domainHints = [], selectedDomains, maxTokens = DEFAULT_MAX_TOKENS }) {
|
|
22
|
-
if (!selectedDomains?.length) return "";
|
|
23
|
-
|
|
24
|
-
const keywords = buildKeywords(task, domainHints);
|
|
25
|
-
const domainBlocks = [];
|
|
26
|
-
|
|
27
|
-
for (const domain of selectedDomains) {
|
|
28
|
-
const sections = domain.sections || [];
|
|
29
|
-
if (!sections.length) continue;
|
|
30
|
-
|
|
31
|
-
// Score all sections but include them all (domain was explicitly selected).
|
|
32
|
-
// Sort by relevance so highest-scoring sections survive token truncation.
|
|
33
|
-
const scored = sections
|
|
34
|
-
.map(section => ({
|
|
35
|
-
section,
|
|
36
|
-
score: scoreSection(section, keywords)
|
|
37
|
-
}))
|
|
38
|
-
.sort((a, b) => b.score - a.score);
|
|
39
|
-
|
|
40
|
-
const selected = scored.map(s => s.section);
|
|
41
|
-
|
|
42
|
-
const sectionMd = selected
|
|
43
|
-
.map(s => `#### ${s.heading}\n${s.content}`)
|
|
44
|
-
.join("\n\n");
|
|
45
|
-
|
|
46
|
-
domainBlocks.push(`### ${domain.name}\n\n${sectionMd}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!domainBlocks.length) return "";
|
|
50
|
-
|
|
51
|
-
let result = domainBlocks.join("\n\n---\n\n");
|
|
52
|
-
|
|
53
|
-
// Truncate by token budget if needed
|
|
54
|
-
if (maxTokens > 0) {
|
|
55
|
-
const maxChars = maxTokens * CHARS_PER_TOKEN;
|
|
56
|
-
if (result.length > maxChars) {
|
|
57
|
-
result = truncateBySection(result, maxChars);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Build a set of lowercase keywords from task text and domain hints.
|
|
66
|
-
* @param {string} task
|
|
67
|
-
* @param {string[]} hints
|
|
68
|
-
* @returns {string[]}
|
|
69
|
-
*/
|
|
70
|
-
function buildKeywords(task, hints) {
|
|
71
|
-
const words = new Set();
|
|
72
|
-
|
|
73
|
-
// Add domain hints
|
|
74
|
-
for (const h of hints) {
|
|
75
|
-
words.add(h.toLowerCase());
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Extract meaningful words from task (>3 chars, skip stop words)
|
|
79
|
-
const stopWords = new Set(["the", "this", "that", "with", "from", "have", "will", "should", "could", "would", "been", "being", "their", "there", "where", "when", "what", "which", "about", "into", "some", "than", "then", "them", "they", "also", "each", "more", "most", "other"]);
|
|
80
|
-
|
|
81
|
-
const taskWords = task
|
|
82
|
-
.toLowerCase()
|
|
83
|
-
.replaceAll(/[^a-z0-9\s-]/g, " ")
|
|
84
|
-
.split(/\s+/)
|
|
85
|
-
.filter(w => w.length > 3 && !stopWords.has(w));
|
|
86
|
-
|
|
87
|
-
for (const w of taskWords) {
|
|
88
|
-
words.add(w);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return Array.from(words);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Score a section by keyword overlap.
|
|
96
|
-
* @param {import('./domain-loader.js').DomainSection} section
|
|
97
|
-
* @param {string[]} keywords
|
|
98
|
-
* @returns {number}
|
|
99
|
-
*/
|
|
100
|
-
function scoreSection(section, keywords) {
|
|
101
|
-
const text = `${section.heading} ${section.content}`.toLowerCase();
|
|
102
|
-
let score = 0;
|
|
103
|
-
for (const kw of keywords) {
|
|
104
|
-
if (text.includes(kw)) score++;
|
|
105
|
-
}
|
|
106
|
-
return score;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Truncate result by dropping trailing sections to fit within maxChars.
|
|
111
|
-
* Preserves complete sections rather than cutting mid-sentence.
|
|
112
|
-
* @param {string} text
|
|
113
|
-
* @param {number} maxChars
|
|
114
|
-
* @returns {string}
|
|
115
|
-
*/
|
|
116
|
-
function truncateBySection(text, maxChars) {
|
|
117
|
-
// Split by section markers (#### or ###)
|
|
118
|
-
const parts = text.split(/(?=^#{3,4}\s)/m);
|
|
119
|
-
let result = "";
|
|
120
|
-
|
|
121
|
-
for (const part of parts) {
|
|
122
|
-
if (result.length + part.length > maxChars && result.length > 0) {
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
result += part;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return result.trim();
|
|
129
|
-
}
|
package/src/git/automation.js
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git automation helpers for the pipeline.
|
|
3
|
-
* Extracted from orchestrator.js for testability and reuse.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { addCheckpoint } from "../session/store.js";
|
|
7
|
-
import { setGitPreexisting } from "../session/mutators.js";
|
|
8
|
-
import { pipelineSonarBlock, stampStagedVerdict } from "../review/verdict-store.js";
|
|
9
|
-
import { branchSize } from "../commands/pr-size.js";
|
|
10
|
-
import { pushRemedy } from "./hu-automation.js";
|
|
11
|
-
import { resolveRunIdentity } from "./run-identity.js";
|
|
12
|
-
import { getRunContext } from "../utils/run-context.js";
|
|
13
|
-
import {
|
|
14
|
-
ensureGitRepo,
|
|
15
|
-
currentBranch,
|
|
16
|
-
hasCommits,
|
|
17
|
-
fetchBase,
|
|
18
|
-
syncBaseBranch,
|
|
19
|
-
ensureBranchUpToDateWithBase,
|
|
20
|
-
createBranch,
|
|
21
|
-
checkoutBranch,
|
|
22
|
-
buildBranchName,
|
|
23
|
-
commitAll,
|
|
24
|
-
pushBranch,
|
|
25
|
-
createPullRequest,
|
|
26
|
-
listPendingPaths,
|
|
27
|
-
hasRemote
|
|
28
|
-
} from "../utils/git.js";
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Karajan writes / updates several internal directories during a pipeline
|
|
32
|
-
* run (the workspace scaffold + the session journal). When they're the
|
|
33
|
-
* ONLY thing pending at finalization, the post-loop commit shouldn't
|
|
34
|
-
* inherit the user task's `feat:` message — it should be a clear
|
|
35
|
-
* `chore: scaffold karajan workspace` so `git log --oneline` reads:
|
|
36
|
-
*
|
|
37
|
-
* 2d770aa docs: add JSDoc comment to index.js
|
|
38
|
-
* abc1234 chore: scaffold karajan workspace
|
|
39
|
-
* 8a95ef0 initial
|
|
40
|
-
*
|
|
41
|
-
* A path counts as scaffolding when it lives at one of these prefixes
|
|
42
|
-
* OR is the project's root `.gitignore` (we extend it to include
|
|
43
|
-
* stack-specific entries during triage).
|
|
44
|
-
*/
|
|
45
|
-
const KARAJAN_SCAFFOLD_PREFIXES = [
|
|
46
|
-
".karajan/",
|
|
47
|
-
".reviews/",
|
|
48
|
-
".kj/",
|
|
49
|
-
".agent/",
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
function isKarajanScaffoldPath(p) {
|
|
53
|
-
if (p === ".gitignore") return true;
|
|
54
|
-
return KARAJAN_SCAFFOLD_PREFIXES.some((prefix) => p.startsWith(prefix));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @param {string[]} paths
|
|
59
|
-
* @returns {boolean} true when every pending path is a Karajan internal
|
|
60
|
-
*/
|
|
61
|
-
export function pendingPathsAreOnlyKarajanScaffold(paths) {
|
|
62
|
-
if (!Array.isArray(paths) || paths.length === 0) return false;
|
|
63
|
-
return paths.every(isKarajanScaffoldPath);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Map the taskType (as classified by triage) to the matching
|
|
68
|
-
* Conventional Commits prefix. When triage hasn't run or the type is
|
|
69
|
-
* unknown we default to `feat:` (the pre-fix behaviour).
|
|
70
|
-
*/
|
|
71
|
-
const TASK_TYPE_TO_PREFIX = {
|
|
72
|
-
doc: "docs",
|
|
73
|
-
"add-tests": "test",
|
|
74
|
-
refactor: "refactor",
|
|
75
|
-
infra: "chore",
|
|
76
|
-
sw: "feat",
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
function prefixForTaskType(taskType) {
|
|
80
|
-
return TASK_TYPE_TO_PREFIX[taskType] || "feat";
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function commitMessageFromTask(task, taskType = null) {
|
|
84
|
-
// KJC-BUG-0413 (#2565): the header comes from the first paragraph, without
|
|
85
|
-
// its leading Markdown marks; the whole task made "feat: # Roadmap ... Context: ...".
|
|
86
|
-
const firstParagraph = String(task || "").trim().split(/\n[ \t]*\r?\n/)[0];
|
|
87
|
-
const clean = firstParagraph
|
|
88
|
-
.replace(/^[#>*\-\s]+/, "")
|
|
89
|
-
.replaceAll(/\s+/g, " ")
|
|
90
|
-
.trim();
|
|
91
|
-
const prefix = prefixForTaskType(taskType);
|
|
92
|
-
// KJC-BUG-0294 (#1993): cut on a word, never mid-word (a title with no blank keeps the hard cut).
|
|
93
|
-
const cut = clean.length > 72 ? clean.slice(0, 72).replace(/\s+\S*$/, "") : clean;
|
|
94
|
-
return `${prefix}: ${cut || "karajan update"}`;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Build the message used by `finalizeGitAutomation` for the post-loop
|
|
99
|
-
* commit. Two paths:
|
|
100
|
-
* - When the only pending changes are Karajan scaffold files
|
|
101
|
-
* (.gitignore, .karajan/, .reviews/, ...), use a fixed
|
|
102
|
-
* `chore: scaffold karajan workspace` message. This avoids the
|
|
103
|
-
* "duplicate feat:" git log seen when triage extends .gitignore
|
|
104
|
-
* for a freshly detected stack and the coder already committed
|
|
105
|
-
* the user-visible change.
|
|
106
|
-
* - Otherwise, fall back to `commitMessageFromTask(task, taskType)`,
|
|
107
|
-
* respecting the type for the prefix.
|
|
108
|
-
*/
|
|
109
|
-
export function buildFinalizeCommitMessage({ task, taskType, pendingPaths }) {
|
|
110
|
-
if (pendingPathsAreOnlyKarajanScaffold(pendingPaths)) {
|
|
111
|
-
return "chore: scaffold karajan workspace";
|
|
112
|
-
}
|
|
113
|
-
return commitMessageFromTask(task, taskType);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// KJC-BUG-0249 (#1896): a branch named after the card is the same on a second
|
|
117
|
-
// run of that card, so one already there is taken, not recreated.
|
|
118
|
-
async function checkoutRunBranch(created) {
|
|
119
|
-
try {
|
|
120
|
-
await createBranch(created);
|
|
121
|
-
} catch (err) {
|
|
122
|
-
if (!/already exists/i.test(err.message)) throw err;
|
|
123
|
-
await checkoutBranch(created);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function prepareGitAutomation({ config, task, logger, session, preexisting: resumedBaseline = null }) {
|
|
128
|
-
const enabled = config.git.auto_commit || config.git.auto_push || config.git.auto_pr;
|
|
129
|
-
if (!enabled) {
|
|
130
|
-
// KJC-TSK-1237: the format stage needs the baseline also when kj does not
|
|
131
|
-
// commit; outside git there is none, and that stage says so.
|
|
132
|
-
let baseline = resumedBaseline;
|
|
133
|
-
if (!baseline) {
|
|
134
|
-
try {
|
|
135
|
-
baseline = await listPendingPaths();
|
|
136
|
-
} catch {
|
|
137
|
-
baseline = null;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (session && Array.isArray(baseline)) setGitPreexisting(session, baseline);
|
|
141
|
-
return { enabled: false };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (!(await ensureGitRepo())) {
|
|
145
|
-
throw new Error("Git automation requested but current directory is not a git repository");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// KJC-BUG-0246 (#1893): one identity for every commit of the run, resolved
|
|
149
|
-
// once: the clone's declaration, else the repository's config, else the
|
|
150
|
-
// global one. From the global config it is said, because another session
|
|
151
|
-
// may switch it mid-run. Nothing resolvable: git refuses the commit loudly.
|
|
152
|
-
const identity = resolveRunIdentity({ projectDir: config.projectDir || process.cwd() });
|
|
153
|
-
const runContext = getRunContext();
|
|
154
|
-
if (identity && runContext) runContext.gitIdentity = identity;
|
|
155
|
-
if (identity) {
|
|
156
|
-
logger.info(`Git identity for this run: ${identity.name} <${identity.email}> (${identity.source})`);
|
|
157
|
-
if (identity.source === "global") logger.warn("The identity comes from the global git config, which another session may switch mid-run: declare this clone's with kj identity set, or set user.email in this repository");
|
|
158
|
-
} else {
|
|
159
|
-
logger.warn("No git identity resolvable (kj identity set, or git config user.name and user.email): the first commit will fail");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const baseBranch = config.base_branch;
|
|
163
|
-
const autoRebase = config.git.auto_rebase !== false;
|
|
164
|
-
// KJC-BUG-0294 (#1993): what is dirty or untracked NOW is not the run's; the
|
|
165
|
-
// pipeline commits only what it changes. Both names of a pending rename are
|
|
166
|
-
// kept. Fails closed: with no answer from git the run stops here rather than
|
|
167
|
-
// commit the person's pending changes later.
|
|
168
|
-
// KJC-BUG-0412 (#2564): a resumed run keeps the baseline of the session it
|
|
169
|
-
// resumes; the tree now also holds that session's own work.
|
|
170
|
-
const preexisting = resumedBaseline ?? await listPendingPaths();
|
|
171
|
-
if (session) setGitPreexisting(session, preexisting);
|
|
172
|
-
const repoHasCommits = await hasCommits();
|
|
173
|
-
|
|
174
|
-
// New repo without commits: create branch directly (no fetch/sync possible)
|
|
175
|
-
if (!repoHasCommits) {
|
|
176
|
-
const created = buildBranchName(config.git.branch_prefix || "feat/", task, session?.pg_task_id || null);
|
|
177
|
-
// git checkout -b works even without commits (creates orphan-like branch on first commit)
|
|
178
|
-
await createBranch(created);
|
|
179
|
-
logger.info(`New repo — created working branch: ${created}`);
|
|
180
|
-
await addCheckpoint(session, { stage: "git-prep", branch: created, created: true, newRepo: true });
|
|
181
|
-
return { enabled: true, branch: created, baseBranch, autoRebase, preexisting };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
await fetchBase(baseBranch).catch(() => {
|
|
185
|
-
// No remote — skip fetch (new project without push)
|
|
186
|
-
logger.info("No remote configured — skipping fetch");
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
let branch;
|
|
190
|
-
try {
|
|
191
|
-
branch = await currentBranch();
|
|
192
|
-
} catch {
|
|
193
|
-
// HEAD exists but branch detection failed — unusual, treat as main
|
|
194
|
-
branch = baseBranch;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (branch === baseBranch) {
|
|
198
|
-
await syncBaseBranch({ baseBranch, autoRebase }).catch(() => {
|
|
199
|
-
// No remote tracking — skip sync for new projects
|
|
200
|
-
});
|
|
201
|
-
const created = buildBranchName(config.git.branch_prefix || "feat/", task, session?.pg_task_id || null);
|
|
202
|
-
await checkoutRunBranch(created);
|
|
203
|
-
branch = created;
|
|
204
|
-
logger.info(`Created working branch: ${branch}`);
|
|
205
|
-
await addCheckpoint(session, { stage: "git-prep", branch, created: true });
|
|
206
|
-
} else {
|
|
207
|
-
await ensureBranchUpToDateWithBase({ branch, baseBranch, autoRebase }).catch(() => {
|
|
208
|
-
// No remote tracking — skip rebase for new projects
|
|
209
|
-
logger.info("No remote tracking — skipping rebase check");
|
|
210
|
-
});
|
|
211
|
-
await addCheckpoint(session, { stage: "git-prep", branch, created: false });
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return { enabled: true, branch, baseBranch, autoRebase, preexisting };
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export function buildPrBody({ task: _task, stageResults }) {
|
|
218
|
-
const sections = ["Created by Karajan Code."];
|
|
219
|
-
|
|
220
|
-
const approach = stageResults?.planner?.approach;
|
|
221
|
-
if (approach) {
|
|
222
|
-
sections.push("", "## Approach", approach);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const steps = stageResults?.planner?.steps;
|
|
226
|
-
if (steps?.length) {
|
|
227
|
-
sections.push("", "## Steps");
|
|
228
|
-
for (let i = 0; i < steps.length; i++) {
|
|
229
|
-
sections.push(`${i + 1}. ${steps[i]}`);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const triageSubtasks = stageResults?.triage?.subtasks;
|
|
234
|
-
const shouldDecompose = stageResults?.triage?.shouldDecompose;
|
|
235
|
-
const pendingSubtasks = shouldDecompose && triageSubtasks?.length > 1 ? triageSubtasks.slice(1) : [];
|
|
236
|
-
if (pendingSubtasks.length > 0) {
|
|
237
|
-
sections.push("", "## Pending subtasks", "This PR addresses part of a larger task. The following subtasks were identified but not included:");
|
|
238
|
-
for (const subtask of pendingSubtasks) {
|
|
239
|
-
sections.push(`- [ ] ${subtask}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return sections.join("\n");
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Create an early PR after the first coder iteration (Karajan CI flow).
|
|
248
|
-
* Commits, pushes, and creates a PR before the reviewer runs.
|
|
249
|
-
* Returns { prNumber, prUrl, commits } or null if nothing to commit.
|
|
250
|
-
*/
|
|
251
|
-
export async function earlyPrCreation({ gitCtx, task, logger, session, stageResults = null }) {
|
|
252
|
-
if (!gitCtx?.enabled) return null;
|
|
253
|
-
|
|
254
|
-
const commitMsg = commitMessageFromTask(task);
|
|
255
|
-
const commitResult = await commitAll(commitMsg, null, { exclude: gitCtx.preexisting || [] });
|
|
256
|
-
if (!commitResult.committed) {
|
|
257
|
-
logger.info("earlyPrCreation: no changes to commit");
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const commits = commitResult.commit ? [commitResult.commit] : [];
|
|
262
|
-
await addCheckpoint(session, { stage: "ci-commit", committed: true });
|
|
263
|
-
|
|
264
|
-
await pushBranch(gitCtx.branch);
|
|
265
|
-
await addCheckpoint(session, { stage: "ci-push", branch: gitCtx.branch });
|
|
266
|
-
logger.info(`Pushed branch for early PR: ${gitCtx.branch}`);
|
|
267
|
-
|
|
268
|
-
const body = buildPrBody({ task, stageResults });
|
|
269
|
-
const prUrl = await createPullRequest({
|
|
270
|
-
baseBranch: gitCtx.baseBranch,
|
|
271
|
-
branch: gitCtx.branch,
|
|
272
|
-
title: commitMessageFromTask(task),
|
|
273
|
-
body
|
|
274
|
-
});
|
|
275
|
-
await addCheckpoint(session, { stage: "ci-pr", branch: gitCtx.branch, pr: prUrl });
|
|
276
|
-
logger.info(`Early PR created: ${prUrl}`);
|
|
277
|
-
|
|
278
|
-
// Extract PR number from URL (e.g. https://github.com/owner/repo/pull/42)
|
|
279
|
-
const prNumber = Number.parseInt(prUrl.split("/").pop(), 10) || null;
|
|
280
|
-
return { prNumber, prUrl, commits };
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Incremental push after each coder iteration (Karajan CI flow).
|
|
285
|
-
* Commits and pushes without creating a new PR.
|
|
286
|
-
*/
|
|
287
|
-
export async function incrementalPush({ gitCtx, task, logger, session }) {
|
|
288
|
-
if (!gitCtx?.enabled) return null;
|
|
289
|
-
|
|
290
|
-
const commitMsg = commitMessageFromTask(task);
|
|
291
|
-
const commitResult = await commitAll(commitMsg, null, { exclude: gitCtx.preexisting || [] });
|
|
292
|
-
if (!commitResult.committed) {
|
|
293
|
-
logger.info("incrementalPush: no changes to commit");
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const commits = commitResult.commit ? [commitResult.commit] : [];
|
|
298
|
-
await addCheckpoint(session, { stage: "ci-incremental-commit", committed: true });
|
|
299
|
-
|
|
300
|
-
await pushBranch(gitCtx.branch);
|
|
301
|
-
await addCheckpoint(session, { stage: "ci-incremental-push", branch: gitCtx.branch });
|
|
302
|
-
logger.info(`Incremental push: ${gitCtx.branch}`);
|
|
303
|
-
|
|
304
|
-
return { commits };
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const DEFAULT_PR_SIZE_LIMIT = 200;
|
|
308
|
-
|
|
309
|
-
export async function finalizeGitAutomation({ config, gitCtx, task, logger, session, stageResults = null, review = null, measureSize = branchSize }) {
|
|
310
|
-
if (!gitCtx?.enabled) return { git: "disabled", commits: [] };
|
|
311
|
-
// KJC-BUG-0297 (#1993): the pipeline commits only what a reviewer approved. An
|
|
312
|
-
// approval that reviewed nothing (reviewer disabled) stamps no verdict and
|
|
313
|
-
// commits nothing: the changes stay in the tree, and the log says why.
|
|
314
|
-
if (review && review.reviewed === false) {
|
|
315
|
-
logger.warn("No review, no commit: the reviewer was disabled, so the pipeline commits nothing; the changes stay in the tree for a review (kj review --staged)");
|
|
316
|
-
await addCheckpoint(session, { stage: "git-commit", committed: false, skipped: "unreviewed" });
|
|
317
|
-
return { committed: false, branch: gitCtx.branch, prUrl: null, pr: null, commits: [], unreviewed: true };
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Take a snapshot of pending paths BEFORE staging so we can decide
|
|
321
|
-
// whether what's about to be committed is just Karajan scaffolding
|
|
322
|
-
// (in which case we use `chore: scaffold karajan workspace`) or a
|
|
323
|
-
// real, user-visible change (in which case we honour the task's
|
|
324
|
-
// taskType for the Conventional Commits prefix).
|
|
325
|
-
const taskType = stageResults?.triage?.taskType ?? null;
|
|
326
|
-
let pendingPaths = [];
|
|
327
|
-
if (config.git.auto_commit) {
|
|
328
|
-
try {
|
|
329
|
-
pendingPaths = await listPendingPaths();
|
|
330
|
-
} catch { /* best-effort — fall back to default message */ }
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const commitMsg =
|
|
334
|
-
config.git.commit_message
|
|
335
|
-
|| buildFinalizeCommitMessage({ task, taskType, pendingPaths });
|
|
336
|
-
|
|
337
|
-
let committed = false;
|
|
338
|
-
const commits = [];
|
|
339
|
-
// KJC-BUG-0299 (#1993): the PR budget is applied BEFORE the commit, with the
|
|
340
|
-
// CI gate's own count (kj pr-size), over what this commit will contain. A
|
|
341
|
-
// step planned at 150 lines once committed 960. Where the branch cannot be
|
|
342
|
-
// measured (a fresh repo with no base yet) kj says so and goes on.
|
|
343
|
-
if (config.git.auto_commit) {
|
|
344
|
-
const limit = Number(config.git.pr_size_limit) > 0 ? Number(config.git.pr_size_limit) : DEFAULT_PR_SIZE_LIMIT;
|
|
345
|
-
let size = null;
|
|
346
|
-
try {
|
|
347
|
-
size = await measureSize({ projectDir: config.projectDir || process.cwd(), base: gitCtx.baseBranch, exclude: gitCtx.preexisting || [] });
|
|
348
|
-
} catch (err) {
|
|
349
|
-
logger.warn(`pr-size: the branch could not be measured (${err.message}); the budget is not checked for this commit`);
|
|
350
|
-
}
|
|
351
|
-
// KJC-BUG-0434: net, as the CI gate counts it.
|
|
352
|
-
if (size && size.net > limit) {
|
|
353
|
-
logger.warn(`pr-size: ${size.net} net line(s) against ${gitCtx.baseBranch}, over the budget of ${limit}: nothing committed. Split the step (several commits, several HUs); the changes stay in the tree`);
|
|
354
|
-
await addCheckpoint(session, { stage: "git-commit", committed: false, skipped: "over-budget", added: size.added, net: size.net, limit });
|
|
355
|
-
return { committed: false, branch: gitCtx.branch, prUrl: null, pr: null, commits: [], overBudget: { added: size.added, net: size.net, limit } };
|
|
356
|
-
}
|
|
357
|
-
// ENV-F1: this path only runs after the pipeline's reviewer approved,
|
|
358
|
-
// so stamp that verdict for the staged diff — the v4 pre-commit gate
|
|
359
|
-
// (when the repo opted in) accepts the pipeline's own commit.
|
|
360
|
-
const commitResult = await commitAll(commitMsg, null, {
|
|
361
|
-
exclude: gitCtx.preexisting || [],
|
|
362
|
-
beforeCommit: ({ paths } = {}) => stampStagedVerdict({
|
|
363
|
-
projectDir: config?.projectDir || process.cwd(),
|
|
364
|
-
paths,
|
|
365
|
-
reviewer: config?.reviewer || "pipeline-reviewer",
|
|
366
|
-
summary: `kj run session ${session?.id || ""}: reviewer approved`.trim(),
|
|
367
|
-
// KJC-TSK-0838: the sonar stage result travels with the stamp.
|
|
368
|
-
sonar: pipelineSonarBlock(stageResults?.sonar),
|
|
369
|
-
}),
|
|
370
|
-
});
|
|
371
|
-
committed = commitResult.committed;
|
|
372
|
-
if (commitResult.commit) {
|
|
373
|
-
commits.push(commitResult.commit);
|
|
374
|
-
}
|
|
375
|
-
// KJC-BUG-0294: what was pending before the run stays out of the commit and is named;
|
|
376
|
-
// if the run also touched one of those files, its edit is in the tree, not in the commit.
|
|
377
|
-
if (commitResult.leftOut?.length) {
|
|
378
|
-
logger.warn(`Left out of the commit, pending before the run (review and commit them yourself): ${commitResult.leftOut.join(", ")}`);
|
|
379
|
-
}
|
|
380
|
-
await addCheckpoint(session, { stage: "git-commit", committed, leftOut: commitResult.leftOut || [] });
|
|
381
|
-
logger.info(committed ? "Committed changes" : "No changes to commit");
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// KJC-BUG-0112: without an `origin` remote (the quickstart scenario:
|
|
385
|
-
// fresh folder + git init) push/PR automation has nothing to talk to —
|
|
386
|
-
// fetchBase used to throw here and escalate to Solomon on EVERY
|
|
387
|
-
// iteration, burning the whole budget on an expected condition.
|
|
388
|
-
const remoteAvailable = (config.git.auto_push || config.git.auto_pr)
|
|
389
|
-
? await hasRemote()
|
|
390
|
-
: false;
|
|
391
|
-
if ((config.git.auto_push || config.git.auto_pr) && !remoteAvailable) {
|
|
392
|
-
logger.info("No remote configured — skipping push/PR automation");
|
|
393
|
-
await addCheckpoint(session, { stage: "git-push", skipped: "no-remote" });
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (remoteAvailable && (config.git.auto_push || config.git.auto_pr)) {
|
|
397
|
-
await fetchBase(gitCtx.baseBranch);
|
|
398
|
-
await ensureBranchUpToDateWithBase({
|
|
399
|
-
branch: gitCtx.branch,
|
|
400
|
-
baseBranch: gitCtx.baseBranch,
|
|
401
|
-
autoRebase: gitCtx.autoRebase
|
|
402
|
-
});
|
|
403
|
-
await addCheckpoint(session, { stage: "git-rebase-check", branch: gitCtx.branch });
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (remoteAvailable && (config.git.auto_push || config.git.auto_pr)) {
|
|
407
|
-
// KJC-BUG-0248 (#1895): a refused push stops the run loudly, with its remedy.
|
|
408
|
-
try {
|
|
409
|
-
await pushBranch(gitCtx.branch);
|
|
410
|
-
} catch (err) {
|
|
411
|
-
throw new Error(`push of ${gitCtx.branch} failed: ${err.message}. ${pushRemedy(err.message, gitCtx.branch)}`, { cause: err });
|
|
412
|
-
}
|
|
413
|
-
await addCheckpoint(session, { stage: "git-push", branch: gitCtx.branch });
|
|
414
|
-
logger.info(`Pushed branch: ${gitCtx.branch}`);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
let prUrl = session.ci_pr_url || null;
|
|
418
|
-
if (remoteAvailable && config.git.auto_pr && !prUrl) {
|
|
419
|
-
const body = buildPrBody({ task, stageResults });
|
|
420
|
-
prUrl = await createPullRequest({
|
|
421
|
-
baseBranch: gitCtx.baseBranch,
|
|
422
|
-
branch: gitCtx.branch,
|
|
423
|
-
title: commitMessageFromTask(task),
|
|
424
|
-
body
|
|
425
|
-
});
|
|
426
|
-
await addCheckpoint(session, { stage: "git-pr", branch: gitCtx.branch, pr: prUrl });
|
|
427
|
-
logger.info("Pull request created");
|
|
428
|
-
} else if (prUrl) {
|
|
429
|
-
logger.info(`PR already exists (CI flow): ${prUrl}`);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return { committed, branch: gitCtx.branch, prUrl, pr: prUrl, commits };
|
|
433
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KJC-BUG-0285 (#1982): the coder commits nothing; the pipeline does, once the
|
|
3
|
-
* review approves. With --auto-commit the coder ran git commit inside its
|
|
4
|
-
* iteration, before the reviewer, and the rejected commit stayed on the branch.
|
|
5
|
-
* The stage reads HEAD before the coder runs and, when HEAD moved on top of it,
|
|
6
|
-
* undoes those commits SOFTLY: the changes stay in the tree and the index for
|
|
7
|
-
* the review. A rewritten branch (HEAD no longer descends from the start) is
|
|
8
|
-
* left alone. Plain git, no injected runner: it never throws, it says.
|
|
9
|
-
*/
|
|
10
|
-
import { spawnSync } from "node:child_process";
|
|
11
|
-
|
|
12
|
-
const git = (args, cwd) => spawnSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
13
|
-
|
|
14
|
-
/** @returns {string|null} the sha HEAD points at, or null outside a repo with commits */
|
|
15
|
-
export function headSha(cwd = process.cwd()) {
|
|
16
|
-
try {
|
|
17
|
-
const res = git(["rev-parse", "HEAD"], cwd);
|
|
18
|
-
return res.status === 0 ? res.stdout.trim() : null;
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** @returns {{undone: number, reason?: string}} */
|
|
25
|
-
export function undoCommitsSince(sha, cwd = process.cwd()) {
|
|
26
|
-
if (!sha) return { undone: 0, reason: "no starting commit" };
|
|
27
|
-
try {
|
|
28
|
-
const head = headSha(cwd);
|
|
29
|
-
if (!head || head === sha) return { undone: 0 };
|
|
30
|
-
if (git(["merge-base", "--is-ancestor", sha, "HEAD"], cwd).status !== 0) {
|
|
31
|
-
return { undone: 0, reason: "HEAD no longer descends from the iteration's start" };
|
|
32
|
-
}
|
|
33
|
-
const undone = Number(git(["rev-list", "--count", `${sha}..HEAD`], cwd).stdout.trim());
|
|
34
|
-
const reset = git(["reset", "--soft", sha], cwd);
|
|
35
|
-
if (reset.status !== 0) return { undone: 0, reason: `git reset --soft failed: ${reset.stderr.trim()}` };
|
|
36
|
-
return { undone };
|
|
37
|
-
} catch (err) {
|
|
38
|
-
return { undone: 0, reason: err.message };
|
|
39
|
-
}
|
|
40
|
-
}
|