karajan-code 4.70.4 → 5.0.0
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/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 +75 -48
- package/src/commands/review-gate.js +421 -341
- 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 +94 -25
- package/src/config/loader.js +34 -17
- 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 +15 -7
- 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/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 +56 -43
- package/src/rag/bootstrap-index.js +28 -54
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +70 -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-skills.js +5 -4
- 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
package/src/commands/standby.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// KJC-TSK-0414 PR2: comandos kj standby list / kj resume.
|
|
2
|
-
//
|
|
3
|
-
// `kj resume <sessionId>` valida cooldownUntil y, si llegó la hora, re-spawn
|
|
4
|
-
// el comando original capturado en argv. Si cooldown todavía está en el
|
|
5
|
-
// futuro, muestra countdown y aborta. `kj standby list` solo enumera.
|
|
6
|
-
//
|
|
7
|
-
// Estrategia de resume: cada sessionState persiste su argv original (los
|
|
8
|
-
// argumentos con los que se lanzó el `kj` que hibernó). El resume re-spawn
|
|
9
|
-
// EXACTAMENTE ese mismo comando con env preservado. El run reanuda como si
|
|
10
|
-
// fuera un retry — el state interno (HU pendiente, snapshot_sha, etc.) está
|
|
11
|
-
// en el JSON persistido en disco por TSK-0408 + TSK-0414 PR1.
|
|
12
|
-
|
|
13
|
-
import { spawn } from "node:child_process";
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
import { loadStandby, listPendingStandby, markStandbyDone, acquireStandbyLock } from "../brain/standby-store.js";
|
|
17
|
-
|
|
18
|
-
const REPO_KJ_CLI = (() => {
|
|
19
|
-
try {
|
|
20
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
return path.join(here, "..", "cli.js");
|
|
22
|
-
} catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
})();
|
|
26
|
-
|
|
27
|
-
function formatCountdown(ms) {
|
|
28
|
-
if (ms <= 0) return "ahora";
|
|
29
|
-
const s = Math.round(ms / 1000);
|
|
30
|
-
const h = Math.floor(s / 3600);
|
|
31
|
-
const m = Math.floor((s % 3600) / 60);
|
|
32
|
-
const sec = s % 60;
|
|
33
|
-
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function standbyListCommand({ json } = {}) {
|
|
37
|
-
const sessions = listPendingStandby();
|
|
38
|
-
if (json) {
|
|
39
|
-
console.log(JSON.stringify(sessions, null, 2));
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (sessions.length === 0) {
|
|
43
|
-
console.log("No hay sesiones hibernadas.");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
console.log(`Sesiones hibernadas (${sessions.length}):`);
|
|
47
|
-
const now = Date.now();
|
|
48
|
-
for (const s of sessions) {
|
|
49
|
-
const ms = new Date(s.cooldownUntil).getTime() - now;
|
|
50
|
-
const status = ms <= 0 ? "✓ lista para resume" : `⏳ resume en ${formatCountdown(ms)}`;
|
|
51
|
-
console.log(` ${s.sessionId} · ${s.reason || "?"} · ${status}`);
|
|
52
|
-
if (s.planId) {
|
|
53
|
-
const huSuffix = s.huId ? `, HU: ${s.huId}` : "";
|
|
54
|
-
console.log(` plan: ${s.planId}${huSuffix}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Re-spawn el comando original capturado en sessionState.argv.
|
|
61
|
-
* Si cooldownUntil > now y force=false → aborta con mensaje.
|
|
62
|
-
*/
|
|
63
|
-
export async function resumeCommand({ sessionId, force } = {}) {
|
|
64
|
-
if (!sessionId) {
|
|
65
|
-
console.error("kj resume requiere <sessionId>. Usa `kj standby list` para ver pendientes.");
|
|
66
|
-
process.exitCode = 1;
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const state = loadStandby(sessionId);
|
|
70
|
-
if (!state) {
|
|
71
|
-
console.error(`Sesión no encontrada: ${sessionId}`);
|
|
72
|
-
process.exitCode = 1;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const cooldownMs = new Date(state.cooldownUntil).getTime() - Date.now();
|
|
76
|
-
if (cooldownMs > 0 && !force) {
|
|
77
|
-
console.log(`Sesión ${sessionId} todavía en cooldown ${formatCountdown(cooldownMs)} (hasta ${state.cooldownUntil}).`);
|
|
78
|
-
console.log("Espera o usa `kj resume <sessionId> --force` (no recomendado — la API seguramente seguirá rechazando).");
|
|
79
|
-
process.exitCode = 0;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const lock = acquireStandbyLock(sessionId);
|
|
84
|
-
if (!lock.acquired) {
|
|
85
|
-
console.log(`Sesión ${sessionId} ya está siendo reanudada por otro proceso.`);
|
|
86
|
-
process.exitCode = 0;
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
if (!Array.isArray(state.argv) || state.argv.length === 0) {
|
|
92
|
-
console.error(`Sesión ${sessionId} sin argv — no se puede re-spawn. Borra manual y relanza.`);
|
|
93
|
-
process.exitCode = 1;
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (!REPO_KJ_CLI) {
|
|
97
|
-
console.error("No se pudo localizar cli.js. ¿Karajan instalado correctamente?");
|
|
98
|
-
process.exitCode = 1;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
console.log(`Reanudando ${sessionId}: kj ${state.argv.join(" ")}`);
|
|
102
|
-
const child = spawn(process.execPath, [REPO_KJ_CLI, ...state.argv], {
|
|
103
|
-
cwd: state.cwd || process.cwd(),
|
|
104
|
-
env: { ...process.env, ...(state.env || {}), KJ_RESUME_FROM: sessionId },
|
|
105
|
-
stdio: "inherit",
|
|
106
|
-
});
|
|
107
|
-
await new Promise((resolve) => {
|
|
108
|
-
child.on("exit", (code) => {
|
|
109
|
-
if (code === 0) markStandbyDone(sessionId);
|
|
110
|
-
process.exitCode = code ?? 0;
|
|
111
|
-
resolve();
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
} finally {
|
|
115
|
-
lock.release?.();
|
|
116
|
-
}
|
|
117
|
-
}
|
package/src/commands/status.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI status command — shows a terminal dashboard of the current pipeline state.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readRunLog } from "../utils/run-log.js";
|
|
6
|
-
import { loadMostRecentSession } from "../session/store.js";
|
|
7
|
-
import { buildDashboard } from "../utils/status-dashboard.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Run the `kj status` command: print a human-readable dashboard to stdout.
|
|
12
|
-
* @param {object} options
|
|
13
|
-
* @param {number} [options.lines=50] - Max log lines to read.
|
|
14
|
-
* @param {string} [options.projectDir] - Project directory override.
|
|
15
|
-
*/
|
|
16
|
-
export async function statusCommand({ lines = 50, projectDir } = {}) {
|
|
17
|
-
const dir = projectDir || process.cwd();
|
|
18
|
-
const logResult = readRunLog(dir, lines);
|
|
19
|
-
|
|
20
|
-
let session = null;
|
|
21
|
-
try {
|
|
22
|
-
session = await loadMostRecentSession();
|
|
23
|
-
} catch { /* no session available */ }
|
|
24
|
-
|
|
25
|
-
let stories = [];
|
|
26
|
-
if (session) {
|
|
27
|
-
try {
|
|
28
|
-
const { loadHuBatch } = await import("../hu/store.js");
|
|
29
|
-
const batch = await loadHuBatch(session.id);
|
|
30
|
-
if (batch?.stories) stories = batch.stories;
|
|
31
|
-
} catch { /* no HU batch */ }
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const dashboard = buildDashboard(session, logResult.lines || [], { stories });
|
|
35
|
-
console.log(dashboard);
|
|
36
|
-
}
|
package/src/commands/triage.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { createAgent } from "../agents/index.js";
|
|
2
|
-
import { assertAgentsAvailable } from "../agents/availability.js";
|
|
3
|
-
import { resolveRole } from "../config.js";
|
|
4
|
-
import { createCliProgressReporter } from "../utils/cli-progress.js";
|
|
5
|
-
import { buildTriagePrompt } from "../prompts/triage.js";
|
|
6
|
-
import { parseMaybeJsonString } from "../review/parser.js";
|
|
7
|
-
import { withBrainRecovery } from "../brain/with-brain-recovery.js";
|
|
8
|
-
|
|
9
|
-
function formatTriage(result) {
|
|
10
|
-
const lines = [];
|
|
11
|
-
lines.push(`## Triage Result`);
|
|
12
|
-
lines.push(`- **Level:** ${result.level || "unknown"}`);
|
|
13
|
-
if (result.taskType) lines.push(`- **Task type:** ${result.taskType}`);
|
|
14
|
-
if (result.reasoning) lines.push(`- **Reasoning:** ${result.reasoning}`);
|
|
15
|
-
lines.push("");
|
|
16
|
-
|
|
17
|
-
if (result.roles?.length) {
|
|
18
|
-
lines.push("### Recommended Roles");
|
|
19
|
-
for (const r of result.roles) {
|
|
20
|
-
lines.push(`- ${r}`);
|
|
21
|
-
}
|
|
22
|
-
lines.push("");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (result.shouldDecompose) {
|
|
26
|
-
lines.push("### Decomposition Suggested");
|
|
27
|
-
if (result.subtasks?.length) {
|
|
28
|
-
for (const s of result.subtasks) {
|
|
29
|
-
lines.push(`- ${typeof s === "string" ? s : s.title || s}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
lines.push("");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return lines.join("\n");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function triageCommand({ task, config, logger, json }) {
|
|
39
|
-
const { withCliRunLog } = await import("../utils/cli-run-log.js");
|
|
40
|
-
return withCliRunLog("triage", { projectDir: config?.projectDir, logger }, async ({ runLog }) => {
|
|
41
|
-
const triageRole = resolveRole(config, "triage");
|
|
42
|
-
await assertAgentsAvailable([triageRole.provider]);
|
|
43
|
-
logger.info(`Triage (${triageRole.provider}) starting...`);
|
|
44
|
-
runLog.logText(`[triage] provider=${triageRole.provider}`);
|
|
45
|
-
|
|
46
|
-
const agent = createAgent(triageRole.provider, config, logger);
|
|
47
|
-
const prompt = buildTriagePrompt({ task });
|
|
48
|
-
const progress = createCliProgressReporter({ role: "triage" });
|
|
49
|
-
let result;
|
|
50
|
-
try {
|
|
51
|
-
result = await withBrainRecovery({
|
|
52
|
-
agent, taskArgs: { prompt, onOutput: progress.onOutput, role: "triage" },
|
|
53
|
-
role: "triage", provider: triageRole.provider, logger,
|
|
54
|
-
});
|
|
55
|
-
progress.finish(result.ok ? "done" : (result.action || "failed"));
|
|
56
|
-
} catch (err) { progress.finish("failed"); throw err; }
|
|
57
|
-
|
|
58
|
-
if (!result.ok) {
|
|
59
|
-
throw new Error(result.error || result.output || "Triage failed");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const parsed = parseMaybeJsonString(result.output);
|
|
63
|
-
if (parsed?.level) runLog.logText(`[triage] level=${parsed.level} roles=${(parsed.roles || []).join(",")}`);
|
|
64
|
-
|
|
65
|
-
if (json) {
|
|
66
|
-
console.log(JSON.stringify(parsed || result.output, null, 2));
|
|
67
|
-
return { ok: true };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (parsed?.level) {
|
|
71
|
-
console.log(formatTriage(parsed));
|
|
72
|
-
} else {
|
|
73
|
-
console.log(result.output);
|
|
74
|
-
}
|
|
75
|
-
logger.info("Triage completed.");
|
|
76
|
-
return { ok: true };
|
|
77
|
-
});
|
|
78
|
-
}
|
package/src/commands/undo.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { loadMostRecentSession } from "../session/store.js";
|
|
2
|
-
import { runCommand } from "../utils/process.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Revert the last pipeline run by resetting to the pre-pipeline commit.
|
|
6
|
-
* Default: soft reset (keeps changes staged). With --hard: discards all changes.
|
|
7
|
-
*/
|
|
8
|
-
export async function undoCommand({ hard = false, logger } = {}) {
|
|
9
|
-
const session = await loadMostRecentSession();
|
|
10
|
-
if (!session) {
|
|
11
|
-
const msg = "No session to undo. No sessions found in the sessions directory.";
|
|
12
|
-
if (logger) logger.error(msg);
|
|
13
|
-
return { ok: false, error: msg };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const sha = session.session_start_sha;
|
|
17
|
-
if (!sha) {
|
|
18
|
-
const msg = `Session ${session.id} has no session_start_sha. Cannot determine the pre-pipeline commit.`;
|
|
19
|
-
if (logger) logger.error(msg);
|
|
20
|
-
return { ok: false, error: msg };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Verify the SHA exists in git history
|
|
24
|
-
const verifyRes = await runCommand("git", ["cat-file", "-t", sha]);
|
|
25
|
-
if (verifyRes.exitCode !== 0 || verifyRes.stdout.trim() !== "commit") {
|
|
26
|
-
const msg = `Commit ${sha} not found in git history. It may have been garbage-collected or belong to a different repository.`;
|
|
27
|
-
if (logger) logger.error(msg);
|
|
28
|
-
return { ok: false, error: msg };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Check there are commits after the SHA (HEAD is ahead)
|
|
32
|
-
const logRes = await runCommand("git", ["log", "--oneline", `${sha}..HEAD`]);
|
|
33
|
-
if (logRes.exitCode !== 0 || !logRes.stdout.trim()) {
|
|
34
|
-
const msg = `No commits to undo. HEAD is already at or before ${sha.slice(0, 8)}.`;
|
|
35
|
-
if (logger) logger.warn(msg);
|
|
36
|
-
return { ok: false, error: msg };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const mode = hard ? "--hard" : "--soft";
|
|
40
|
-
const resetRes = await runCommand("git", ["reset", mode, sha]);
|
|
41
|
-
if (resetRes.exitCode !== 0) {
|
|
42
|
-
const msg = `git reset ${mode} ${sha} failed: ${resetRes.stderr || resetRes.stdout}`;
|
|
43
|
-
if (logger) logger.error(msg);
|
|
44
|
-
return { ok: false, error: msg };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const shortSha = sha.slice(0, 8);
|
|
48
|
-
const message = hard
|
|
49
|
-
? `Reverted to pre-pipeline state (commit ${shortSha}). All changes discarded.`
|
|
50
|
-
: `Reverted to pre-pipeline state (commit ${shortSha}). Changes are staged. Use \`git diff --cached\` to review.`;
|
|
51
|
-
|
|
52
|
-
if (logger) logger.info(message);
|
|
53
|
-
console.log(message);
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
ok: true,
|
|
57
|
-
message,
|
|
58
|
-
sessionId: session.id,
|
|
59
|
-
sha,
|
|
60
|
-
mode: hard ? "hard" : "soft"
|
|
61
|
-
};
|
|
62
|
-
}
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Domain Knowledge loader.
|
|
3
|
-
* Scans .karajan/domains/ (project-local) and ~/.karajan/domains/ (user-global)
|
|
4
|
-
* for DOMAIN.md files with YAML frontmatter.
|
|
5
|
-
* Project-local domains override user-global domains by directory name.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readdir, readFile, mkdir, writeFile, stat } from "node:fs/promises";
|
|
9
|
-
import { join, isAbsolute } from "node:path";
|
|
10
|
-
import yaml from "js-yaml";
|
|
11
|
-
import { getKarajanHome } from "../utils/paths.js";
|
|
12
|
-
|
|
13
|
-
const DOMAIN_FILE = "DOMAIN.md";
|
|
14
|
-
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Parse a DOMAIN.md file into a structured DomainFile object.
|
|
18
|
-
* @param {string} filePath — absolute path to the DOMAIN.md file
|
|
19
|
-
* @returns {Promise<DomainFile|null>} parsed domain or null on error
|
|
20
|
-
*
|
|
21
|
-
* @typedef {Object} DomainSection
|
|
22
|
-
* @property {string} heading — section heading (e.g. "Core Concepts")
|
|
23
|
-
* @property {string} content — section body (markdown)
|
|
24
|
-
*
|
|
25
|
-
* @typedef {Object} DomainFile
|
|
26
|
-
* @property {string} name
|
|
27
|
-
* @property {string} description
|
|
28
|
-
* @property {string[]} tags
|
|
29
|
-
* @property {string} version
|
|
30
|
-
* @property {string} author
|
|
31
|
-
* @property {string} visibility — "private" | "public"
|
|
32
|
-
* @property {Array<{type: string, note?: string}>} sources
|
|
33
|
-
* @property {string} content — full markdown body (after frontmatter)
|
|
34
|
-
* @property {DomainSection[]} sections — parsed markdown sections
|
|
35
|
-
* @property {string} filePath — original file path
|
|
36
|
-
* @property {string} [origin] — "project" | "user" (set by loadDomains)
|
|
37
|
-
*/
|
|
38
|
-
export async function parseDomainFile(filePath) {
|
|
39
|
-
let raw;
|
|
40
|
-
try {
|
|
41
|
-
raw = await readFile(filePath, "utf-8");
|
|
42
|
-
} catch {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const match = raw.match(FRONTMATTER_RE);
|
|
47
|
-
if (!match) return null;
|
|
48
|
-
|
|
49
|
-
let meta;
|
|
50
|
-
try {
|
|
51
|
-
meta = yaml.load(match[1]);
|
|
52
|
-
} catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!meta || typeof meta !== "object" || !meta.name) return null;
|
|
57
|
-
|
|
58
|
-
const content = match[2].trim();
|
|
59
|
-
const sections = parseSections(content);
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
name: meta.name,
|
|
63
|
-
description: meta.description || "",
|
|
64
|
-
tags: Array.isArray(meta.tags) ? meta.tags : [],
|
|
65
|
-
version: meta.version || "0.0.0",
|
|
66
|
-
author: meta.author || "",
|
|
67
|
-
visibility: meta.visibility || "private",
|
|
68
|
-
sources: Array.isArray(meta.sources) ? meta.sources : [],
|
|
69
|
-
content,
|
|
70
|
-
sections,
|
|
71
|
-
filePath
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Split markdown content into sections by ## headings.
|
|
77
|
-
* @param {string} content
|
|
78
|
-
* @returns {DomainSection[]}
|
|
79
|
-
*/
|
|
80
|
-
function parseSections(content) {
|
|
81
|
-
const sections = [];
|
|
82
|
-
const lines = content.split("\n");
|
|
83
|
-
let current = null;
|
|
84
|
-
|
|
85
|
-
for (const line of lines) {
|
|
86
|
-
const headingMatch = line.match(/^##\s+(.+)/);
|
|
87
|
-
if (headingMatch) {
|
|
88
|
-
if (current) {
|
|
89
|
-
current.content = current.content.trim();
|
|
90
|
-
sections.push(current);
|
|
91
|
-
}
|
|
92
|
-
current = { heading: headingMatch[1].trim(), content: "" };
|
|
93
|
-
} else if (current) {
|
|
94
|
-
current.content += line + "\n";
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (current) {
|
|
99
|
-
current.content = current.content.trim();
|
|
100
|
-
sections.push(current);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return sections;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Auto-detect project context files (README.md, CLAUDE.md) as lightweight domains.
|
|
108
|
-
* Used as fallback when no explicit .karajan/domains/ are configured.
|
|
109
|
-
*
|
|
110
|
-
* @param {string} projectDir — absolute path to project root
|
|
111
|
-
* @returns {Promise<DomainFile[]>}
|
|
112
|
-
*/
|
|
113
|
-
export async function autoDetectDomains(projectDir) {
|
|
114
|
-
if (!projectDir) return [];
|
|
115
|
-
|
|
116
|
-
const candidates = ["README.md", "CLAUDE.md", "docs/README.md"];
|
|
117
|
-
const detected = [];
|
|
118
|
-
|
|
119
|
-
for (const rel of candidates) {
|
|
120
|
-
const filePath = join(projectDir, rel);
|
|
121
|
-
let content;
|
|
122
|
-
try {
|
|
123
|
-
content = await readFile(filePath, "utf-8");
|
|
124
|
-
} catch { continue; }
|
|
125
|
-
|
|
126
|
-
if (!content || content.trim().length < 20) continue;
|
|
127
|
-
|
|
128
|
-
// Truncate to first 4000 chars to avoid bloating context
|
|
129
|
-
const trimmed = content.length > 4000 ? content.slice(0, 4000) + "\n\n[...truncated]" : content;
|
|
130
|
-
|
|
131
|
-
detected.push({
|
|
132
|
-
name: `auto:${rel}`,
|
|
133
|
-
description: `Auto-detected from ${rel}`,
|
|
134
|
-
tags: [],
|
|
135
|
-
version: "0.0.0",
|
|
136
|
-
author: "",
|
|
137
|
-
visibility: "private",
|
|
138
|
-
sources: [{ type: "auto-detect", note: rel }],
|
|
139
|
-
content: trimmed,
|
|
140
|
-
sections: parseSections(trimmed),
|
|
141
|
-
filePath,
|
|
142
|
-
origin: "auto-detect"
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return detected;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Load all domains from project-local and user-global directories.
|
|
151
|
-
* Falls back to auto-detecting README.md/CLAUDE.md when no explicit domains found.
|
|
152
|
-
* Project-local domains override user-global domains when directory names match.
|
|
153
|
-
*
|
|
154
|
-
* @param {string|null} projectDir — absolute path to project root (null = user-global only)
|
|
155
|
-
* @returns {Promise<DomainFile[]>}
|
|
156
|
-
*/
|
|
157
|
-
export async function loadDomains(projectDir) {
|
|
158
|
-
const userDir = join(getKarajanHome(), "domains");
|
|
159
|
-
const projectDomainDir = projectDir ? join(projectDir, ".karajan", "domains") : null;
|
|
160
|
-
|
|
161
|
-
// Load user-global first, then project-local overrides
|
|
162
|
-
const userDomains = await scanDomainDir(userDir, "user");
|
|
163
|
-
const projectDomains = projectDomainDir ? await scanDomainDir(projectDomainDir, "project") : [];
|
|
164
|
-
|
|
165
|
-
// Build a map keyed by directory name for merge.
|
|
166
|
-
const merged = new Map();
|
|
167
|
-
|
|
168
|
-
for (const { dirName, domain } of userDomains) {
|
|
169
|
-
merged.set(dirName, domain);
|
|
170
|
-
}
|
|
171
|
-
for (const { dirName, domain } of projectDomains) {
|
|
172
|
-
merged.set(dirName, domain); // project overrides user
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const explicit = Array.from(merged.values());
|
|
176
|
-
|
|
177
|
-
// Fallback: auto-detect README.md, CLAUDE.md when no explicit domains found
|
|
178
|
-
if (explicit.length === 0 && projectDir) {
|
|
179
|
-
return autoDetectDomains(projectDir);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return explicit;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Scan a single domains directory for DOMAIN.md files.
|
|
187
|
-
* @param {string} dir — absolute path to a domains/ directory
|
|
188
|
-
* @param {string} origin — "project" | "user"
|
|
189
|
-
* @returns {Promise<Array<{dirName: string, domain: DomainFile}>>}
|
|
190
|
-
*/
|
|
191
|
-
async function scanDomainDir(dir, origin) {
|
|
192
|
-
let entries;
|
|
193
|
-
try {
|
|
194
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
195
|
-
} catch {
|
|
196
|
-
return [];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const results = [];
|
|
200
|
-
|
|
201
|
-
for (const entry of entries) {
|
|
202
|
-
if (!entry.isDirectory()) continue;
|
|
203
|
-
|
|
204
|
-
const domainPath = join(dir, entry.name, DOMAIN_FILE);
|
|
205
|
-
const domain = await parseDomainFile(domainPath);
|
|
206
|
-
|
|
207
|
-
if (domain) {
|
|
208
|
-
domain.origin = origin;
|
|
209
|
-
results.push({ dirName: entry.name, domain });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return results;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Persist an inline domain (text or file path) to .karajan/domains/inline/DOMAIN.md.
|
|
218
|
-
* If domainInput is a path to an existing .md file, reads its content.
|
|
219
|
-
* Otherwise treats it as inline text.
|
|
220
|
-
*
|
|
221
|
-
* @param {string} domainInput — inline text or absolute path to a .md file
|
|
222
|
-
* @param {string} projectDir — project root
|
|
223
|
-
*/
|
|
224
|
-
export async function persistInlineDomain(domainInput, projectDir) {
|
|
225
|
-
if (!domainInput || !projectDir) return;
|
|
226
|
-
|
|
227
|
-
let content = domainInput;
|
|
228
|
-
|
|
229
|
-
// If it looks like a file path, try to read it
|
|
230
|
-
if (isAbsolute(domainInput) && domainInput.endsWith(".md")) {
|
|
231
|
-
try {
|
|
232
|
-
const s = await stat(domainInput);
|
|
233
|
-
if (s.isFile()) {
|
|
234
|
-
content = await readFile(domainInput, "utf-8");
|
|
235
|
-
}
|
|
236
|
-
} catch { /* treat as inline text */ }
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const domainDir = join(projectDir, ".karajan", "domains", "inline");
|
|
240
|
-
await mkdir(domainDir, { recursive: true });
|
|
241
|
-
|
|
242
|
-
const domainFile = join(domainDir, DOMAIN_FILE);
|
|
243
|
-
const wrapped = `---\nname: inline-domain\ndescription: Domain knowledge provided via --domain parameter\ntags: []\nversion: "0.0.0"\n---\n\n${content}\n`;
|
|
244
|
-
await writeFile(domainFile, wrapped, "utf-8");
|
|
245
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local domain registry.
|
|
3
|
-
* Tracks installed domains in ~/.karajan/domain-registry.json.
|
|
4
|
-
* Provides search and filtering by tags/hints.
|
|
5
|
-
* Interface prepared for future remote registry extension.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { getKarajanHome } from "../utils/paths.js";
|
|
11
|
-
|
|
12
|
-
const REGISTRY_FILE = "domain-registry.json";
|
|
13
|
-
const SCHEMA_VERSION = 1;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {Object} RegistryEntry
|
|
17
|
-
* @property {string} name
|
|
18
|
-
* @property {string} [description]
|
|
19
|
-
* @property {string} version
|
|
20
|
-
* @property {string} source — "local" (future: "remote")
|
|
21
|
-
* @property {string} installedAt — ISO timestamp
|
|
22
|
-
* @property {string} filePath — absolute path to DOMAIN.md
|
|
23
|
-
* @property {string[]} tags
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
export class DomainRegistry {
|
|
27
|
-
/**
|
|
28
|
-
* @param {Record<string, RegistryEntry>} domains
|
|
29
|
-
*/
|
|
30
|
-
constructor(domains = {}) {
|
|
31
|
-
this._domains = domains;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Load registry from disk. Creates empty registry if file is missing or corrupt.
|
|
36
|
-
* @returns {Promise<DomainRegistry>}
|
|
37
|
-
*/
|
|
38
|
-
static async load() {
|
|
39
|
-
const filePath = join(getKarajanHome(), REGISTRY_FILE);
|
|
40
|
-
try {
|
|
41
|
-
const raw = await readFile(filePath, "utf-8");
|
|
42
|
-
const data = JSON.parse(raw);
|
|
43
|
-
if (data?.schemaVersion === SCHEMA_VERSION && data.domains) {
|
|
44
|
-
return new DomainRegistry(data.domains);
|
|
45
|
-
}
|
|
46
|
-
} catch { /* file missing, corrupt, or wrong schema */ }
|
|
47
|
-
return new DomainRegistry();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Persist registry to disk.
|
|
52
|
-
*/
|
|
53
|
-
async save() {
|
|
54
|
-
const home = getKarajanHome();
|
|
55
|
-
await mkdir(home, { recursive: true });
|
|
56
|
-
const filePath = join(home, REGISTRY_FILE);
|
|
57
|
-
const data = {
|
|
58
|
-
schemaVersion: SCHEMA_VERSION,
|
|
59
|
-
domains: this._domains
|
|
60
|
-
};
|
|
61
|
-
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Register or update a domain in the registry.
|
|
66
|
-
* @param {{name: string, version?: string, tags?: string[], filePath?: string, description?: string}} domainFile
|
|
67
|
-
*/
|
|
68
|
-
register(domainFile) {
|
|
69
|
-
this._domains[domainFile.name] = {
|
|
70
|
-
name: domainFile.name,
|
|
71
|
-
description: domainFile.description || "",
|
|
72
|
-
version: domainFile.version || "0.0.0",
|
|
73
|
-
source: "local",
|
|
74
|
-
installedAt: new Date().toISOString(),
|
|
75
|
-
filePath: domainFile.filePath || "",
|
|
76
|
-
tags: domainFile.tags || []
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* List domains, optionally filtered by tags and/or free-text query.
|
|
82
|
-
* @param {{tags?: string[], query?: string}} [filters]
|
|
83
|
-
* @returns {RegistryEntry[]}
|
|
84
|
-
*/
|
|
85
|
-
list(filters = {}) {
|
|
86
|
-
let entries = Object.values(this._domains);
|
|
87
|
-
|
|
88
|
-
if (filters.tags?.length) {
|
|
89
|
-
const tagSet = new Set(filters.tags.map(t => t.toLowerCase()));
|
|
90
|
-
entries = entries.filter(e =>
|
|
91
|
-
e.tags?.some(t => tagSet.has(t.toLowerCase()))
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (filters.query) {
|
|
96
|
-
const q = filters.query.toLowerCase();
|
|
97
|
-
entries = entries.filter(e =>
|
|
98
|
-
(e.name || "").toLowerCase().includes(q) ||
|
|
99
|
-
(e.description || "").toLowerCase().includes(q)
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return entries;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Search domains by triage hints.
|
|
108
|
-
* Matches hints against tags, name and description.
|
|
109
|
-
* Returns results ranked by number of matching hints.
|
|
110
|
-
* @param {string[]} hints — keywords from triage domainHints
|
|
111
|
-
* @returns {RegistryEntry[]}
|
|
112
|
-
*/
|
|
113
|
-
search(hints) {
|
|
114
|
-
if (!hints?.length) return [];
|
|
115
|
-
|
|
116
|
-
const lowerHints = hints.map(h => h.toLowerCase());
|
|
117
|
-
const entries = Object.values(this._domains);
|
|
118
|
-
const scored = [];
|
|
119
|
-
|
|
120
|
-
for (const entry of entries) {
|
|
121
|
-
const searchable = [
|
|
122
|
-
entry.name || "",
|
|
123
|
-
entry.description || "",
|
|
124
|
-
...(entry.tags || [])
|
|
125
|
-
].join(" ").toLowerCase();
|
|
126
|
-
|
|
127
|
-
let score = 0;
|
|
128
|
-
for (const hint of lowerHints) {
|
|
129
|
-
if (searchable.includes(hint)) score++;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (score > 0) {
|
|
133
|
-
scored.push({ entry, score });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
scored.sort((a, b) => b.score - a.score);
|
|
138
|
-
return scored.map(s => s.entry);
|
|
139
|
-
}
|
|
140
|
-
}
|