@sentry/warden 0.21.0 → 0.23.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/.oxlintrc.json +169 -0
- package/CHANGELOG.md +390 -0
- package/README.md +4 -4
- package/dist/cli/args.d.ts +6 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +175 -207
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/add.js +16 -5
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/commands/build.d.ts +13 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +384 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +10 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/runs.d.ts.map +1 -1
- package/dist/cli/commands/runs.js +79 -15
- package/dist/cli/commands/runs.js.map +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +4 -1
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/context.d.ts.map +1 -1
- package/dist/cli/context.js +8 -1
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/files.d.ts.map +1 -1
- package/dist/cli/files.js +27 -9
- package/dist/cli/files.js.map +1 -1
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +448 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/index.js +2 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/input.d.ts +12 -0
- package/dist/cli/input.d.ts.map +1 -1
- package/dist/cli/input.js +60 -0
- package/dist/cli/input.js.map +1 -1
- package/dist/cli/log-cleanup.d.ts.map +1 -1
- package/dist/cli/log-cleanup.js +4 -0
- package/dist/cli/log-cleanup.js.map +1 -1
- package/dist/cli/main.d.ts +39 -2
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +482 -100
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output/box.js +1 -1
- package/dist/cli/output/formatters.d.ts +10 -2
- package/dist/cli/output/formatters.d.ts.map +1 -1
- package/dist/cli/output/formatters.js +32 -9
- package/dist/cli/output/formatters.js.map +1 -1
- package/dist/cli/output/index.d.ts +2 -2
- package/dist/cli/output/index.d.ts.map +1 -1
- package/dist/cli/output/index.js +2 -2
- package/dist/cli/output/index.js.map +1 -1
- package/dist/cli/output/ink-runner.d.ts +3 -1
- package/dist/cli/output/ink-runner.d.ts.map +1 -1
- package/dist/cli/output/ink-runner.js +79 -25
- package/dist/cli/output/ink-runner.js.map +1 -1
- package/dist/cli/output/jsonl-schema-gen.d.ts.map +1 -1
- package/dist/cli/output/jsonl-schema-gen.js +4 -2
- package/dist/cli/output/jsonl-schema-gen.js.map +1 -1
- package/dist/cli/output/jsonl.d.ts +147 -4
- package/dist/cli/output/jsonl.d.ts.map +1 -1
- package/dist/cli/output/jsonl.js +183 -7
- package/dist/cli/output/jsonl.js.map +1 -1
- package/dist/cli/output/live-status.d.ts +48 -0
- package/dist/cli/output/live-status.d.ts.map +1 -0
- package/dist/cli/output/live-status.js +173 -0
- package/dist/cli/output/live-status.js.map +1 -0
- package/dist/cli/output/reporter.d.ts +4 -0
- package/dist/cli/output/reporter.d.ts.map +1 -1
- package/dist/cli/output/reporter.js +20 -3
- package/dist/cli/output/reporter.js.map +1 -1
- package/dist/cli/output/tasks.d.ts +11 -5
- package/dist/cli/output/tasks.d.ts.map +1 -1
- package/dist/cli/output/tasks.js +126 -68
- package/dist/cli/output/tasks.js.map +1 -1
- package/dist/cli/signals.d.ts +15 -0
- package/dist/cli/signals.d.ts.map +1 -0
- package/dist/cli/signals.js +26 -0
- package/dist/cli/signals.js.map +1 -0
- package/dist/config/loader.d.ts +48 -7
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +228 -32
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +55 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +34 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/diff/context.d.ts +9 -2
- package/dist/diff/context.d.ts.map +1 -1
- package/dist/diff/context.js +65 -17
- package/dist/diff/context.js.map +1 -1
- package/dist/evals/runner.d.ts.map +1 -1
- package/dist/evals/runner.js +16 -0
- package/dist/evals/runner.js.map +1 -1
- package/dist/event/context.d.ts.map +1 -1
- package/dist/event/context.js +1 -0
- package/dist/event/context.js.map +1 -1
- package/dist/event/schedule-context.d.ts.map +1 -1
- package/dist/event/schedule-context.js +1 -0
- package/dist/event/schedule-context.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/output/dedup.d.ts +8 -11
- package/dist/output/dedup.d.ts.map +1 -1
- package/dist/output/dedup.js +39 -37
- package/dist/output/dedup.js.map +1 -1
- package/dist/output/github-checks.d.ts +1 -0
- package/dist/output/github-checks.d.ts.map +1 -1
- package/dist/output/github-checks.js +9 -7
- package/dist/output/github-checks.js.map +1 -1
- package/dist/sdk/analyze.d.ts.map +1 -1
- package/dist/sdk/analyze.js +166 -276
- package/dist/sdk/analyze.js.map +1 -1
- package/dist/sdk/circuit-breaker.d.ts +26 -0
- package/dist/sdk/circuit-breaker.d.ts.map +1 -0
- package/dist/sdk/circuit-breaker.js +53 -0
- package/dist/sdk/circuit-breaker.js.map +1 -0
- package/dist/sdk/errors.d.ts +5 -0
- package/dist/sdk/errors.d.ts.map +1 -1
- package/dist/sdk/errors.js +27 -0
- package/dist/sdk/errors.js.map +1 -1
- package/dist/sdk/extract.d.ts +18 -9
- package/dist/sdk/extract.d.ts.map +1 -1
- package/dist/sdk/extract.js +95 -87
- package/dist/sdk/extract.js.map +1 -1
- package/dist/sdk/fix-quality.d.ts +5 -0
- package/dist/sdk/fix-quality.d.ts.map +1 -1
- package/dist/sdk/fix-quality.js +42 -23
- package/dist/sdk/fix-quality.js.map +1 -1
- package/dist/sdk/haiku.d.ts +6 -0
- package/dist/sdk/haiku.d.ts.map +1 -1
- package/dist/sdk/haiku.js +75 -49
- package/dist/sdk/haiku.js.map +1 -1
- package/dist/sdk/json-output.d.ts +31 -0
- package/dist/sdk/json-output.d.ts.map +1 -0
- package/dist/sdk/json-output.js +98 -0
- package/dist/sdk/json-output.js.map +1 -0
- package/dist/sdk/model-pricing.json +56 -16
- package/dist/sdk/post-process.d.ts +29 -0
- package/dist/sdk/post-process.d.ts.map +1 -0
- package/dist/sdk/post-process.js +65 -0
- package/dist/sdk/post-process.js.map +1 -0
- package/dist/sdk/prepare.d.ts.map +1 -1
- package/dist/sdk/prepare.js +4 -1
- package/dist/sdk/prepare.js.map +1 -1
- package/dist/sdk/pricing.d.ts +18 -0
- package/dist/sdk/pricing.d.ts.map +1 -1
- package/dist/sdk/pricing.js +108 -3
- package/dist/sdk/pricing.js.map +1 -1
- package/dist/sdk/prompt-sections.d.ts +56 -0
- package/dist/sdk/prompt-sections.d.ts.map +1 -0
- package/dist/sdk/prompt-sections.js +118 -0
- package/dist/sdk/prompt-sections.js.map +1 -0
- package/dist/sdk/prompt.d.ts +2 -17
- package/dist/sdk/prompt.d.ts.map +1 -1
- package/dist/sdk/prompt.js +15 -37
- package/dist/sdk/prompt.js.map +1 -1
- package/dist/sdk/report-files.d.ts +15 -0
- package/dist/sdk/report-files.d.ts.map +1 -0
- package/dist/sdk/report-files.js +20 -0
- package/dist/sdk/report-files.js.map +1 -0
- package/dist/sdk/runner.d.ts +9 -1
- package/dist/sdk/runner.d.ts.map +1 -1
- package/dist/sdk/runner.js +6 -0
- package/dist/sdk/runner.js.map +1 -1
- package/dist/sdk/runtimes/claude.d.ts +3 -0
- package/dist/sdk/runtimes/claude.d.ts.map +1 -0
- package/dist/sdk/runtimes/claude.js +376 -0
- package/dist/sdk/runtimes/claude.js.map +1 -0
- package/dist/sdk/runtimes/index.d.ts +12 -0
- package/dist/sdk/runtimes/index.d.ts.map +1 -0
- package/dist/sdk/runtimes/index.js +22 -0
- package/dist/sdk/runtimes/index.js.map +1 -0
- package/dist/sdk/runtimes/types.d.ts +116 -0
- package/dist/sdk/runtimes/types.d.ts.map +1 -0
- package/dist/sdk/runtimes/types.js +17 -0
- package/dist/sdk/runtimes/types.js.map +1 -0
- package/dist/sdk/types.d.ts +42 -2
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk/types.js.map +1 -1
- package/dist/sdk/usage.d.ts +21 -6
- package/dist/sdk/usage.d.ts.map +1 -1
- package/dist/sdk/usage.js +36 -23
- package/dist/sdk/usage.js.map +1 -1
- package/dist/sdk/verify.d.ts +25 -0
- package/dist/sdk/verify.d.ts.map +1 -0
- package/dist/sdk/verify.js +226 -0
- package/dist/sdk/verify.js.map +1 -0
- package/dist/skill-builder/agentic.d.ts +33 -0
- package/dist/skill-builder/agentic.d.ts.map +1 -0
- package/dist/skill-builder/agentic.js +240 -0
- package/dist/skill-builder/agentic.js.map +1 -0
- package/dist/skill-builder/authoring-provider.d.ts +5 -0
- package/dist/skill-builder/authoring-provider.d.ts.map +1 -0
- package/dist/skill-builder/authoring-provider.js +70 -0
- package/dist/skill-builder/authoring-provider.js.map +1 -0
- package/dist/skill-builder/definition.d.ts +46 -0
- package/dist/skill-builder/definition.d.ts.map +1 -0
- package/dist/skill-builder/definition.js +171 -0
- package/dist/skill-builder/definition.js.map +1 -0
- package/dist/skill-builder/outline-contract.d.ts +113 -0
- package/dist/skill-builder/outline-contract.d.ts.map +1 -0
- package/dist/skill-builder/outline-contract.js +73 -0
- package/dist/skill-builder/outline-contract.js.map +1 -0
- package/dist/skill-builder/outline-state.d.ts +158 -0
- package/dist/skill-builder/outline-state.d.ts.map +1 -0
- package/dist/skill-builder/outline-state.js +87 -0
- package/dist/skill-builder/outline-state.js.map +1 -0
- package/dist/skill-builder/outline.d.ts +36 -0
- package/dist/skill-builder/outline.d.ts.map +1 -0
- package/dist/skill-builder/outline.js +345 -0
- package/dist/skill-builder/outline.js.map +1 -0
- package/dist/skill-builder/skill-contract.d.ts +97 -0
- package/dist/skill-builder/skill-contract.d.ts.map +1 -0
- package/dist/skill-builder/skill-contract.js +58 -0
- package/dist/skill-builder/skill-contract.js.map +1 -0
- package/dist/skill-builder/skill-prompts.d.ts +61 -0
- package/dist/skill-builder/skill-prompts.d.ts.map +1 -0
- package/dist/skill-builder/skill-prompts.js +285 -0
- package/dist/skill-builder/skill-prompts.js.map +1 -0
- package/dist/skill-builder/skill.d.ts +23 -0
- package/dist/skill-builder/skill.d.ts.map +1 -0
- package/dist/skill-builder/skill.js +563 -0
- package/dist/skill-builder/skill.js.map +1 -0
- package/dist/skills/index.d.ts +2 -2
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/loader.d.ts +20 -6
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +88 -141
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/remote.d.ts +11 -7
- package/dist/skills/remote.d.ts.map +1 -1
- package/dist/skills/remote.js +62 -162
- package/dist/skills/remote.js.map +1 -1
- package/dist/triggers/matcher.d.ts.map +1 -1
- package/dist/triggers/matcher.js +28 -15
- package/dist/triggers/matcher.js.map +1 -1
- package/dist/types/index.d.ts +41 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +24 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/path.d.ts +17 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +36 -0
- package/dist/utils/path.js.map +1 -0
- package/package.json +8 -8
- package/policies/README.md +22 -0
- package/policies/code-comments.md +20 -0
- package/policies/policy-template.md +14 -0
- package/skills/warden/SKILL.md +5 -5
- package/skills/warden/SOURCES.md +39 -0
- package/skills/warden/SPEC.md +112 -0
- package/skills/warden/references/cli-reference.md +11 -4
- package/skills/warden/references/config-schema.md +38 -13
- package/skills/warden/references/configuration.md +29 -8
- package/skills/warden-sweep/SKILL.md +43 -365
- package/skills/warden-sweep/SOURCES.md +43 -0
- package/skills/warden-sweep/SPEC.md +126 -0
- package/skills/warden-sweep/references/issue-phase.md +24 -0
- package/skills/warden-sweep/references/organize-phase.md +31 -0
- package/skills/warden-sweep/references/patch-phase.md +142 -0
- package/skills/warden-sweep/references/resume-and-artifacts.md +51 -0
- package/skills/warden-sweep/references/scan-phase.md +46 -0
- package/skills/warden-sweep/references/script-interfaces.md +127 -0
- package/skills/warden-sweep/references/verify-phase.md +69 -0
- package/src/builtin-skills/security-review/SKILL.md +81 -0
- package/src/builtin-skills/security-review/SPEC.md +77 -0
- package/src/builtin-skills/security-review/references/github-workflows.md +186 -0
- package/src/builtin-skills/security-review/references/javascript-typescript.md +69 -0
- package/src/builtin-skills/security-review/references/python.md +70 -0
- package/src/internal-skills/skill-writer/EVAL.md +124 -0
- package/src/internal-skills/skill-writer/SKILL.md +159 -0
- package/src/internal-skills/skill-writer/SOURCES.md +94 -0
- package/src/internal-skills/skill-writer/SPEC.md +153 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/argument-driven-skill-layout.md +32 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/asset-template-skill-layout.md +30 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/inline-skill-layout.md +28 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/reference-backed-skill-layout.md +35 -0
- package/src/internal-skills/skill-writer/references/artifact-layouts/script-backed-skill-layout.md +31 -0
- package/src/internal-skills/skill-writer/references/authoring-path.md +91 -0
- package/src/internal-skills/skill-writer/references/claude-code/argument-substitutions.md +20 -0
- package/src/internal-skills/skill-writer/references/claude-code/dynamic-context.md +16 -0
- package/src/internal-skills/skill-writer/references/claude-code/frontmatter-and-invocation.md +33 -0
- package/src/internal-skills/skill-writer/references/claude-code/hook-backed-skills.md +28 -0
- package/src/internal-skills/skill-writer/references/claude-code/subagent-fork-skills.md +22 -0
- package/src/internal-skills/skill-writer/references/description-optimization.md +28 -0
- package/src/internal-skills/skill-writer/references/design-principles.md +74 -0
- package/src/internal-skills/skill-writer/references/evaluation-path.md +73 -0
- package/src/internal-skills/skill-writer/references/examples/documentation-skill.md +70 -0
- package/src/internal-skills/skill-writer/references/examples/evaluator-loop-skill.md +38 -0
- package/src/internal-skills/skill-writer/references/examples/hook-backed-skill.md +39 -0
- package/src/internal-skills/skill-writer/references/examples/router-skill.md +41 -0
- package/src/internal-skills/skill-writer/references/examples/security-review-skill.md +69 -0
- package/src/internal-skills/skill-writer/references/examples/subagent-fork-skill.md +39 -0
- package/src/internal-skills/skill-writer/references/examples/workflow-process-skill.md +65 -0
- package/src/internal-skills/skill-writer/references/execution-shapes.md +68 -0
- package/src/internal-skills/skill-writer/references/iteration-evidence.md +82 -0
- package/src/internal-skills/skill-writer/references/iteration-path.md +40 -0
- package/src/internal-skills/skill-writer/references/mode-selection.md +67 -0
- package/src/internal-skills/skill-writer/references/output-contracts.md +68 -0
- package/src/internal-skills/skill-writer/references/reference-architecture.md +71 -0
- package/src/internal-skills/skill-writer/references/registration-validation.md +67 -0
- package/src/internal-skills/skill-writer/references/source-discovery.md +60 -0
- package/src/internal-skills/skill-writer/references/spec-template.md +118 -0
- package/src/internal-skills/skill-writer/references/structure-troubleshooting.md +111 -0
- package/src/internal-skills/skill-writer/references/synthesis-path.md +119 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/evaluator-loops.md +25 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/orchestrator-workers.md +25 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/parallel-workflows.md +21 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/plan-validate-execute.md +26 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/prompt-chaining.md +24 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/routing-workflows.md +28 -0
- package/src/internal-skills/skill-writer/references/workflow-mechanics/validation-loops.md +26 -0
- package/src/internal-skills/skill-writer/scripts/quick_validate.py +158 -0
- package/src/internal-skills/skill-writer/scripts/quick_validate_test.py +79 -0
package/dist/cli/main.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { config as dotenvConfig } from 'dotenv';
|
|
4
4
|
import { Sentry, flushSentry, setGlobalAttributes, emitRunMetric, getTraceId } from '../sentry.js';
|
|
5
|
-
import { loadWardenConfig, resolveSkillConfigs } from '../config/loader.js';
|
|
5
|
+
import { emptyToUndefined, loadWardenConfig, resolveSkillConfigs } from '../config/loader.js';
|
|
6
6
|
import { verifyAuth } from '../sdk/runner.js';
|
|
7
|
-
import {
|
|
7
|
+
import { mapExtractionErrorCode } from '../sdk/errors.js';
|
|
8
|
+
import { mergeAuxiliaryUsage } from '../sdk/usage.js';
|
|
9
|
+
import { resolveSkillAsync, SkillLoaderError } from '../skills/loader.js';
|
|
8
10
|
import { matchTrigger, filterContextByPaths, shouldFail, countFindingsAtOrAbove } from '../triggers/matcher.js';
|
|
9
11
|
import { filterFindings } from '../types/index.js';
|
|
10
12
|
import { DEFAULT_CONCURRENCY, getAnthropicApiKey } from '../utils/index.js';
|
|
11
|
-
import { parseCliArgs,
|
|
13
|
+
import { parseCliArgs, showVersion, classifyTargets } from './args.js';
|
|
14
|
+
import { showHelp } from './help.js';
|
|
12
15
|
import { buildLocalEventContext, buildFileEventContext } from './context.js';
|
|
13
|
-
import { getRepoRoot, getHeadSha, refExists,
|
|
16
|
+
import { getRepoRoot, getHeadSha, refExists, getDefaultBranch } from './git.js';
|
|
14
17
|
import { renderTerminalReport, filterReports } from './terminal.js';
|
|
15
|
-
import { Reporter, detectOutputMode, parseVerbosity, Verbosity, runSkillTasks, runSkillTasksWithInk, pluralize, MODEL_DEFAULT_SENTINEL, writeJsonlContent, renderJsonlString,
|
|
18
|
+
import { Reporter, detectOutputMode, parseVerbosity, Verbosity, runSkillTasks, runSkillTasksWithInk, pluralize, MODEL_DEFAULT_SENTINEL, writeJsonlContent, renderJsonlString, renderJsonlChunkLine, renderJsonlChunkRecords, initJsonlFile, appendJsonlLine, getRepoLogPath, generateRunId, } from './output/index.js';
|
|
16
19
|
import { cleanupArtifacts } from './log-cleanup.js';
|
|
17
20
|
import { collectFixableFindings, applyAllFixes, runInteractiveFixFlow, renderFixSummary, } from './fix.js';
|
|
18
21
|
import { UserAbortError } from './input.js';
|
|
@@ -21,6 +24,8 @@ import { runAdd } from './commands/add.js';
|
|
|
21
24
|
import { runSetupApp } from './commands/setup-app.js';
|
|
22
25
|
import { runSync } from './commands/sync.js';
|
|
23
26
|
import { runRuns } from './commands/runs.js';
|
|
27
|
+
import { runBuild, runImprove } from './commands/build.js';
|
|
28
|
+
import { generatedSkillDefinitionRootExists, resolveGeneratedSkillTarget, } from '../skill-builder/definition.js';
|
|
24
29
|
/**
|
|
25
30
|
* Global abort controller for graceful shutdown on SIGINT.
|
|
26
31
|
* Used to cancel in-progress SDK queries.
|
|
@@ -52,22 +57,31 @@ function loadEnvFiles(dir) {
|
|
|
52
57
|
dotenvConfig({ path: envLocalPath, override: true, quiet: true });
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Create a Reporter instance from CLI options.
|
|
57
|
-
*/
|
|
58
60
|
function createReporter(options) {
|
|
59
61
|
const detected = detectOutputMode(options.color);
|
|
60
62
|
const outputMode = options.log ? { ...detected, isTTY: false } : detected;
|
|
61
63
|
const verbosity = parseVerbosity(options.quiet, options.verbose, options.debug);
|
|
62
64
|
return new Reporter(outputMode, verbosity);
|
|
63
65
|
}
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
/** Resolve the directory Warden should treat as the invocation root. */
|
|
67
|
+
export function resolveInvocationCwd(baseCwd, cliCwd) {
|
|
68
|
+
return cliCwd ? resolve(baseCwd, cliCwd) : baseCwd;
|
|
69
|
+
}
|
|
67
70
|
function resolveConfigPath(options, repoPath) {
|
|
68
71
|
const cwd = process.cwd();
|
|
69
72
|
return options.config ? resolve(cwd, options.config) : resolve(repoPath, 'warden.toml');
|
|
70
73
|
}
|
|
74
|
+
function resolveLocalReviewBase(configDefaultBranch, repoPath) {
|
|
75
|
+
const defaultBranch = configDefaultBranch ?? getDefaultBranch(repoPath);
|
|
76
|
+
if (defaultBranch.includes('/')) {
|
|
77
|
+
return { defaultBranch, base: defaultBranch };
|
|
78
|
+
}
|
|
79
|
+
const remoteTrackingRef = `origin/${defaultBranch}`;
|
|
80
|
+
return {
|
|
81
|
+
defaultBranch,
|
|
82
|
+
base: refExists(remoteTrackingRef, repoPath) ? remoteTrackingRef : defaultBranch,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
71
85
|
/**
|
|
72
86
|
* Emit a minimal JSONL log (summary-only, 0 findings) for early-exit paths.
|
|
73
87
|
*
|
|
@@ -146,17 +160,139 @@ function initializeRunLog(args) {
|
|
|
146
160
|
}
|
|
147
161
|
}
|
|
148
162
|
const paths = [];
|
|
163
|
+
const seenPaths = new Set();
|
|
164
|
+
const addPath = (path) => {
|
|
165
|
+
const key = resolve(process.cwd(), path);
|
|
166
|
+
if (seenPaths.has(key))
|
|
167
|
+
return;
|
|
168
|
+
seenPaths.add(key);
|
|
169
|
+
paths.push(path);
|
|
170
|
+
};
|
|
149
171
|
if (primaryLogWritten)
|
|
150
|
-
|
|
172
|
+
addPath(primaryLogPath);
|
|
151
173
|
if (resolvedOutputPath)
|
|
152
|
-
|
|
153
|
-
return { paths, primaryLogPath, primaryLogWritten, outputPath: resolvedOutputPath, startTime, baseRun };
|
|
174
|
+
addPath(resolvedOutputPath);
|
|
175
|
+
return { paths, primaryLogPath, primaryLogWritten, outputPath: resolvedOutputPath, startTime, baseRun, chunks: [] };
|
|
154
176
|
}
|
|
155
|
-
function
|
|
177
|
+
function appendChunkToRunLog(log, skillName, chunk) {
|
|
156
178
|
if (log.paths.length === 0)
|
|
157
179
|
return;
|
|
158
|
-
const
|
|
159
|
-
const
|
|
180
|
+
const auxiliaryUsage = chunk.auxiliaryUsage?.reduce((acc, entry) => mergeAuxiliaryUsage(acc, { [entry.agent]: entry.usage }), undefined);
|
|
181
|
+
const error = chunk.failed
|
|
182
|
+
? {
|
|
183
|
+
code: chunk.failureCode ?? 'unknown',
|
|
184
|
+
message: chunk.failureMessage ?? 'unknown error',
|
|
185
|
+
timestamp: new Date().toISOString(),
|
|
186
|
+
}
|
|
187
|
+
: chunk.extractionFailed
|
|
188
|
+
? {
|
|
189
|
+
code: mapExtractionErrorCode(chunk.extractionError),
|
|
190
|
+
message: chunk.extractionError ?? 'unknown extraction error',
|
|
191
|
+
timestamp: new Date().toISOString(),
|
|
192
|
+
}
|
|
193
|
+
: undefined;
|
|
194
|
+
const record = {
|
|
195
|
+
schemaVersion: 1,
|
|
196
|
+
run: { ...log.baseRun, durationMs: Date.now() - log.startTime },
|
|
197
|
+
skill: skillName,
|
|
198
|
+
model: chunk.model,
|
|
199
|
+
chunk: {
|
|
200
|
+
file: chunk.filename,
|
|
201
|
+
index: chunk.index,
|
|
202
|
+
total: chunk.total,
|
|
203
|
+
lineRange: chunk.lineRange,
|
|
204
|
+
},
|
|
205
|
+
status: error ? 'error' : 'ok',
|
|
206
|
+
findings: chunk.findings,
|
|
207
|
+
usage: chunk.usage,
|
|
208
|
+
durationMs: chunk.durationMs,
|
|
209
|
+
auxiliaryUsage,
|
|
210
|
+
error,
|
|
211
|
+
};
|
|
212
|
+
let line;
|
|
213
|
+
try {
|
|
214
|
+
line = renderJsonlChunkLine(record);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
log.chunks.push(record);
|
|
220
|
+
for (const p of log.paths) {
|
|
221
|
+
try {
|
|
222
|
+
appendJsonlLine(p, line);
|
|
223
|
+
}
|
|
224
|
+
catch { /* best-effort */ }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function buildReportChunkRecord(log, report, runDurationMs, index = 1, total = 1, error) {
|
|
228
|
+
const reportError = report.error ?? error;
|
|
229
|
+
return {
|
|
230
|
+
schemaVersion: 1,
|
|
231
|
+
run: { ...log.baseRun, durationMs: runDurationMs },
|
|
232
|
+
skill: report.skill,
|
|
233
|
+
model: report.model,
|
|
234
|
+
chunk: {
|
|
235
|
+
file: report.skippedFiles?.[0]?.filename ?? '',
|
|
236
|
+
index,
|
|
237
|
+
total,
|
|
238
|
+
lineRange: '',
|
|
239
|
+
},
|
|
240
|
+
status: reportError ? 'error' : report.skippedFiles?.length ? 'skipped' : 'ok',
|
|
241
|
+
findings: report.findings,
|
|
242
|
+
usage: report.usage,
|
|
243
|
+
durationMs: report.durationMs ?? runDurationMs,
|
|
244
|
+
auxiliaryUsage: report.auxiliaryUsage,
|
|
245
|
+
error: reportError,
|
|
246
|
+
skippedFiles: report.skippedFiles,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function buildRunErrorChunkRecord(log, runDurationMs, error) {
|
|
250
|
+
return {
|
|
251
|
+
schemaVersion: 1,
|
|
252
|
+
run: { ...log.baseRun, durationMs: runDurationMs },
|
|
253
|
+
skill: 'run',
|
|
254
|
+
chunk: {
|
|
255
|
+
file: '',
|
|
256
|
+
index: 1,
|
|
257
|
+
total: 1,
|
|
258
|
+
lineRange: '',
|
|
259
|
+
},
|
|
260
|
+
status: 'error',
|
|
261
|
+
findings: [],
|
|
262
|
+
durationMs: runDurationMs,
|
|
263
|
+
error,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function hasReportRecord(log, report) {
|
|
267
|
+
return log.chunks.some((chunk) => {
|
|
268
|
+
if (chunk.skill !== report.skill)
|
|
269
|
+
return false;
|
|
270
|
+
if (report.error) {
|
|
271
|
+
return chunk.error?.code === report.error.code && chunk.error.message === report.error.message;
|
|
272
|
+
}
|
|
273
|
+
if (report.skippedFiles?.length) {
|
|
274
|
+
return (chunk.skippedFiles?.length ?? 0) > 0;
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function shouldStreamReportRecord(log, report) {
|
|
280
|
+
if (hasReportRecord(log, report))
|
|
281
|
+
return false;
|
|
282
|
+
return Boolean(report.error || report.skippedFiles?.length || !log.chunks.some((chunk) => chunk.skill === report.skill));
|
|
283
|
+
}
|
|
284
|
+
function appendReportToRunLog(log, report) {
|
|
285
|
+
if (!shouldStreamReportRecord(log, report))
|
|
286
|
+
return;
|
|
287
|
+
const record = buildReportChunkRecord(log, report, Date.now() - log.startTime);
|
|
288
|
+
let line;
|
|
289
|
+
try {
|
|
290
|
+
line = renderJsonlChunkLine(record);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
log.chunks.push(record);
|
|
160
296
|
for (const p of log.paths) {
|
|
161
297
|
try {
|
|
162
298
|
appendJsonlLine(p, line);
|
|
@@ -164,26 +300,198 @@ function appendSkillToRunLog(log, report) {
|
|
|
164
300
|
catch { /* best-effort */ }
|
|
165
301
|
}
|
|
166
302
|
}
|
|
303
|
+
function lineRangeIncludes(lineRange, line) {
|
|
304
|
+
if (!lineRange)
|
|
305
|
+
return false;
|
|
306
|
+
const [startText, endText] = lineRange.split('-');
|
|
307
|
+
const start = Number(startText);
|
|
308
|
+
const end = endText ? Number(endText) : start;
|
|
309
|
+
return Number.isFinite(start) && Number.isFinite(end) && line >= start && line <= end;
|
|
310
|
+
}
|
|
311
|
+
function findChunkForFinding(chunks, skill, finding) {
|
|
312
|
+
const sameSkill = chunks.filter((chunk) => chunk.skill === skill && chunk.chunk.file);
|
|
313
|
+
const location = finding.location;
|
|
314
|
+
if (!location)
|
|
315
|
+
return sameSkill[0];
|
|
316
|
+
return sameSkill.find((chunk) => chunk.chunk.file === location.path && lineRangeIncludes(chunk.chunk.lineRange, location.startLine)) ?? sameSkill.find((chunk) => chunk.chunk.file === location.path) ?? sameSkill[0];
|
|
317
|
+
}
|
|
318
|
+
function buildFinalChunkRecords(log, reports, totalDurationMs, error) {
|
|
319
|
+
const finalRun = { ...log.baseRun, durationMs: totalDurationMs };
|
|
320
|
+
if (log.chunks.length === 0) {
|
|
321
|
+
if (reports.length === 0 && error) {
|
|
322
|
+
return [buildRunErrorChunkRecord(log, totalDurationMs, error)];
|
|
323
|
+
}
|
|
324
|
+
return reports.map((report) => buildReportChunkRecord(log, report, totalDurationMs, undefined, undefined, error));
|
|
325
|
+
}
|
|
326
|
+
const findingsByChunk = new Map();
|
|
327
|
+
for (const report of reports) {
|
|
328
|
+
for (const finding of report.findings) {
|
|
329
|
+
const chunk = findChunkForFinding(log.chunks, report.skill, finding);
|
|
330
|
+
if (!chunk)
|
|
331
|
+
continue;
|
|
332
|
+
const findings = findingsByChunk.get(chunk) ?? [];
|
|
333
|
+
findings.push(finding);
|
|
334
|
+
findingsByChunk.set(chunk, findings);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const chunkRecords = log.chunks.map((chunk) => ({
|
|
338
|
+
...chunk,
|
|
339
|
+
run: { ...finalRun },
|
|
340
|
+
findings: findingsByChunk.get(chunk) ?? [],
|
|
341
|
+
}));
|
|
342
|
+
const finalLog = { ...log, chunks: chunkRecords };
|
|
343
|
+
const missingReports = reports.filter((report) => shouldStreamReportRecord(finalLog, report));
|
|
344
|
+
return [
|
|
345
|
+
...chunkRecords,
|
|
346
|
+
...missingReports.map((report) => buildReportChunkRecord(log, report, totalDurationMs, undefined, undefined, error)),
|
|
347
|
+
];
|
|
348
|
+
}
|
|
167
349
|
/**
|
|
168
|
-
*
|
|
350
|
+
* Rewrite the run log with final chunk records. Returns the set of paths that
|
|
169
351
|
* accepted the write, so the caller can decide whether to claim
|
|
170
|
-
* "wrote JSONL output to X" (only true when the
|
|
352
|
+
* "wrote JSONL output to X" (only true when the final log actually landed).
|
|
171
353
|
*/
|
|
172
354
|
function finalizeRunLog(log, reports, totalDurationMs, error) {
|
|
173
355
|
const wrote = new Set();
|
|
174
356
|
if (log.paths.length === 0)
|
|
175
357
|
return wrote;
|
|
176
|
-
|
|
177
|
-
|
|
358
|
+
let content;
|
|
359
|
+
try {
|
|
360
|
+
const records = buildFinalChunkRecords(log, reports, totalDurationMs, error);
|
|
361
|
+
content = renderJsonlChunkRecords(records);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return wrote;
|
|
365
|
+
}
|
|
178
366
|
for (const p of log.paths) {
|
|
367
|
+
const targetPath = resolve(process.cwd(), p);
|
|
368
|
+
const tempPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
|
|
369
|
+
const tempDonePath = `${tempPath}.done`;
|
|
179
370
|
try {
|
|
180
|
-
|
|
371
|
+
writeJsonlContent(tempPath, content);
|
|
372
|
+
writeFileSync(tempDonePath, '');
|
|
373
|
+
renameSync(tempPath, targetPath);
|
|
374
|
+
renameSync(tempDonePath, `${targetPath}.done`);
|
|
181
375
|
wrote.add(p);
|
|
182
376
|
}
|
|
183
|
-
catch {
|
|
377
|
+
catch {
|
|
378
|
+
try {
|
|
379
|
+
unlinkSync(tempPath);
|
|
380
|
+
}
|
|
381
|
+
catch { /* ignore */ }
|
|
382
|
+
try {
|
|
383
|
+
unlinkSync(tempDonePath);
|
|
384
|
+
}
|
|
385
|
+
catch { /* ignore */ }
|
|
386
|
+
// best-effort
|
|
387
|
+
}
|
|
184
388
|
}
|
|
185
389
|
return wrote;
|
|
186
390
|
}
|
|
391
|
+
/** Apply per-skill runner overrides on top of the shared execution defaults. */
|
|
392
|
+
export function mergeSkillRunnerOptions(base, overrides) {
|
|
393
|
+
const merged = { ...base };
|
|
394
|
+
if (overrides.model !== undefined)
|
|
395
|
+
merged.model = overrides.model;
|
|
396
|
+
if (overrides.maxTurns !== undefined)
|
|
397
|
+
merged.maxTurns = overrides.maxTurns;
|
|
398
|
+
if (overrides.runtime !== undefined)
|
|
399
|
+
merged.runtime = overrides.runtime;
|
|
400
|
+
if (overrides.auxiliaryModel !== undefined)
|
|
401
|
+
merged.auxiliaryModel = overrides.auxiliaryModel;
|
|
402
|
+
if (overrides.synthesisModel !== undefined)
|
|
403
|
+
merged.synthesisModel = overrides.synthesisModel;
|
|
404
|
+
if (overrides.auxiliaryMaxRetries !== undefined) {
|
|
405
|
+
merged.auxiliaryMaxRetries = overrides.auxiliaryMaxRetries;
|
|
406
|
+
}
|
|
407
|
+
if (overrides.verifyFindings !== undefined)
|
|
408
|
+
merged.verifyFindings = overrides.verifyFindings;
|
|
409
|
+
return merged;
|
|
410
|
+
}
|
|
411
|
+
function renderSkillRunHeader(args) {
|
|
412
|
+
const { reporter, skill, repoPath, runtimeName, model } = args;
|
|
413
|
+
const source = skill.rootDir && repoPath && skill.rootDir.startsWith(repoPath)
|
|
414
|
+
? skill.rootDir.slice(repoPath.length + 1)
|
|
415
|
+
: skill.rootDir;
|
|
416
|
+
reporter.blank();
|
|
417
|
+
reporter.text(` Skill ${skill.name}`);
|
|
418
|
+
if (source) {
|
|
419
|
+
reporter.text(` Source ${source}`);
|
|
420
|
+
}
|
|
421
|
+
reporter.text(` Model ${model ?? 'default'} [${runtimeName}]`);
|
|
422
|
+
reporter.blank();
|
|
423
|
+
}
|
|
424
|
+
async function createDirectSkillTask(args) {
|
|
425
|
+
const { spec, repoPath, options, reporter, renderHeader } = args;
|
|
426
|
+
let skill;
|
|
427
|
+
try {
|
|
428
|
+
skill = await resolveSkillAsync(spec.skill, repoPath, {
|
|
429
|
+
remote: spec.remote,
|
|
430
|
+
offline: options.offline,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
if (error instanceof SkillLoaderError &&
|
|
435
|
+
repoPath &&
|
|
436
|
+
generatedSkillDefinitionRootExists(resolveGeneratedSkillTarget(repoPath, spec.skill).rootDir)) {
|
|
437
|
+
throw new Error(`Generated skill ${spec.skill} is missing generated artifacts. Run "warden build ${spec.skill}" first.`);
|
|
438
|
+
}
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
441
|
+
if (renderHeader && !options.json) {
|
|
442
|
+
renderSkillRunHeader({
|
|
443
|
+
reporter,
|
|
444
|
+
skill,
|
|
445
|
+
repoPath,
|
|
446
|
+
runtimeName: spec.runnerOptions.runtime ?? 'claude',
|
|
447
|
+
model: spec.runnerOptions.model,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
name: spec.name,
|
|
452
|
+
displayName: spec.displayName,
|
|
453
|
+
failOn: spec.failOn,
|
|
454
|
+
minConfidence: spec.minConfidence,
|
|
455
|
+
resolveSkill: async () => skill,
|
|
456
|
+
context: spec.context,
|
|
457
|
+
runnerOptions: spec.runnerOptions,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
/** Expand configured skills into runnable direct tasks. */
|
|
461
|
+
export async function createSkillTasks(args) {
|
|
462
|
+
const tasks = [];
|
|
463
|
+
const singleDirectSkill = args.specs.length === 1;
|
|
464
|
+
for (const spec of args.specs) {
|
|
465
|
+
tasks.push(await createDirectSkillTask({
|
|
466
|
+
spec,
|
|
467
|
+
repoPath: args.repoPath,
|
|
468
|
+
options: args.options,
|
|
469
|
+
reporter: args.reporter,
|
|
470
|
+
renderHeader: singleDirectSkill,
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
return tasks;
|
|
474
|
+
}
|
|
475
|
+
/** Resolve the default analysis model from config, CLI overrides, or environment. */
|
|
476
|
+
export function resolveCliDefaultModel(config, cliModel) {
|
|
477
|
+
return (emptyToUndefined(config?.defaults?.agent?.model) ??
|
|
478
|
+
emptyToUndefined(config?.defaults?.model) ??
|
|
479
|
+
emptyToUndefined(cliModel) ??
|
|
480
|
+
emptyToUndefined(process.env['WARDEN_MODEL']));
|
|
481
|
+
}
|
|
482
|
+
/** Resolve the default auxiliary model used for helper and repair passes. */
|
|
483
|
+
export function resolveCliDefaultAuxiliaryModel(config) {
|
|
484
|
+
return emptyToUndefined(config?.defaults?.auxiliary?.model);
|
|
485
|
+
}
|
|
486
|
+
/** Resolve the default synthesis model, falling back to the auxiliary lane when unset. */
|
|
487
|
+
export function resolveCliDefaultSynthesisModel(config) {
|
|
488
|
+
return (emptyToUndefined(config?.defaults?.synthesis?.model) ??
|
|
489
|
+
resolveCliDefaultAuxiliaryModel(config));
|
|
490
|
+
}
|
|
491
|
+
/** Resolve the model label recorded in JSONL output, including the default sentinel. */
|
|
492
|
+
export function resolveCliLogModel(config, cliModel) {
|
|
493
|
+
return resolveCliDefaultModel(config, cliModel) ?? MODEL_DEFAULT_SENTINEL;
|
|
494
|
+
}
|
|
187
495
|
/**
|
|
188
496
|
* Process skill task results into reports and check for failures.
|
|
189
497
|
* Exported for testing; callers inside main.ts use it directly.
|
|
@@ -216,9 +524,6 @@ export function processTaskResults(results, reportOn, minConfidence) {
|
|
|
216
524
|
const filteredReports = filterReports(reports, reportOn, minConfidence);
|
|
217
525
|
return { reports, filteredReports, hasFailure, failureReasons };
|
|
218
526
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Output results and handle fixes. Returns exit code.
|
|
221
|
-
*/
|
|
222
527
|
async function outputResultsAndHandleFixes(processed, options, reporter, runLog, totalDuration, failFastAborted) {
|
|
223
528
|
const { reports, filteredReports, hasFailure, failureReasons } = processed;
|
|
224
529
|
const traceId = runLog.baseRun.traceId;
|
|
@@ -247,8 +552,7 @@ async function outputResultsAndHandleFixes(processed, options, reporter, runLog,
|
|
|
247
552
|
// Prefer reading the on-disk log (per-skill durationMs is a snapshot).
|
|
248
553
|
// Only read it back if finalize actually landed the summary there;
|
|
249
554
|
// a half-written file should fall through to the in-memory render.
|
|
250
|
-
// The fallback
|
|
251
|
-
// valid but not byte-identical to the file.
|
|
555
|
+
// The fallback renders the same chunk-record shape in memory.
|
|
252
556
|
let jsonlContent;
|
|
253
557
|
if (finalizedPaths.has(runLog.primaryLogPath)) {
|
|
254
558
|
try {
|
|
@@ -257,14 +561,13 @@ async function outputResultsAndHandleFixes(processed, options, reporter, runLog,
|
|
|
257
561
|
catch { /* fall through */ }
|
|
258
562
|
}
|
|
259
563
|
if (!jsonlContent) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
});
|
|
564
|
+
try {
|
|
565
|
+
jsonlContent = renderJsonlChunkRecords(buildFinalChunkRecords(runLog, reports, totalDuration));
|
|
566
|
+
}
|
|
567
|
+
catch (err) {
|
|
568
|
+
reporter.error(`Failed to render JSONL output: ${err instanceof Error ? err.message : String(err)}`);
|
|
569
|
+
return 1;
|
|
570
|
+
}
|
|
268
571
|
}
|
|
269
572
|
process.stdout.write(jsonlContent);
|
|
270
573
|
}
|
|
@@ -310,12 +613,8 @@ async function outputResultsAndHandleFixes(processed, options, reporter, runLog,
|
|
|
310
613
|
}
|
|
311
614
|
return 0;
|
|
312
615
|
}
|
|
313
|
-
/**
|
|
314
|
-
|
|
315
|
-
* If skillName is provided, runs only that skill.
|
|
316
|
-
* Otherwise, runs skills from matched triggers in warden.toml.
|
|
317
|
-
*/
|
|
318
|
-
async function runSkills(context, options, reporter) {
|
|
616
|
+
/** Run one or more skills against an already constructed review context. */
|
|
617
|
+
export async function runSkills(context, options, reporter) {
|
|
319
618
|
const cwd = process.cwd();
|
|
320
619
|
const startTime = Date.now();
|
|
321
620
|
// Get API key (optional - SDK can use Claude Code subscription auth)
|
|
@@ -357,6 +656,9 @@ async function runSkills(context, options, reporter) {
|
|
|
357
656
|
const config = configPath && existsSync(configPath)
|
|
358
657
|
? loadWardenConfig(dirname(configPath))
|
|
359
658
|
: null;
|
|
659
|
+
const defaultModel = resolveCliDefaultModel(config, options.model);
|
|
660
|
+
const defaultAuxiliaryModel = resolveCliDefaultAuxiliaryModel(config);
|
|
661
|
+
const defaultSynthesisModel = resolveCliDefaultSynthesisModel(config);
|
|
360
662
|
// Determine which triggers/skills to run
|
|
361
663
|
let skillsToRun;
|
|
362
664
|
if (options.skill) {
|
|
@@ -369,7 +671,20 @@ async function runSkills(context, options, reporter) {
|
|
|
369
671
|
const fallbackFilters = defaultIgnorePaths?.length
|
|
370
672
|
? { ignorePaths: defaultIgnorePaths }
|
|
371
673
|
: {};
|
|
372
|
-
skillsToRun = [{
|
|
674
|
+
skillsToRun = [{
|
|
675
|
+
skill: options.skill,
|
|
676
|
+
remote: match?.remote,
|
|
677
|
+
filters: match?.filters ?? fallbackFilters,
|
|
678
|
+
model: match?.model ?? defaultModel,
|
|
679
|
+
maxTurns: match?.maxTurns ?? config?.defaults?.agent?.maxTurns ?? config?.defaults?.maxTurns,
|
|
680
|
+
runtime: match?.runtime ?? config?.defaults?.runtime ?? 'claude',
|
|
681
|
+
auxiliaryModel: match?.auxiliaryModel ?? defaultAuxiliaryModel,
|
|
682
|
+
synthesisModel: match?.synthesisModel ?? defaultSynthesisModel,
|
|
683
|
+
auxiliaryMaxRetries: match?.auxiliaryMaxRetries ??
|
|
684
|
+
config?.defaults?.auxiliary?.maxRetries ??
|
|
685
|
+
config?.defaults?.auxiliaryMaxRetries,
|
|
686
|
+
verifyFindings: match?.verifyFindings ?? (config?.defaults?.verification?.enabled !== false),
|
|
687
|
+
}];
|
|
373
688
|
}
|
|
374
689
|
else if (config) {
|
|
375
690
|
// Get skills from matched triggers, preserving remote property and filters
|
|
@@ -384,7 +699,18 @@ async function runSkills(context, options, reporter) {
|
|
|
384
699
|
seen.add(t.skill);
|
|
385
700
|
return true;
|
|
386
701
|
})
|
|
387
|
-
.map((t) => ({
|
|
702
|
+
.map((t) => ({
|
|
703
|
+
skill: t.skill,
|
|
704
|
+
remote: t.remote,
|
|
705
|
+
filters: t.filters,
|
|
706
|
+
model: t.model,
|
|
707
|
+
maxTurns: t.maxTurns,
|
|
708
|
+
runtime: t.runtime,
|
|
709
|
+
auxiliaryModel: t.auxiliaryModel,
|
|
710
|
+
synthesisModel: t.synthesisModel,
|
|
711
|
+
auxiliaryMaxRetries: t.auxiliaryMaxRetries,
|
|
712
|
+
verifyFindings: t.verifyFindings,
|
|
713
|
+
}));
|
|
388
714
|
}
|
|
389
715
|
else {
|
|
390
716
|
skillsToRun = [];
|
|
@@ -402,30 +728,54 @@ async function runSkills(context, options, reporter) {
|
|
|
402
728
|
return 0;
|
|
403
729
|
}
|
|
404
730
|
// Build skill tasks
|
|
405
|
-
// Model precedence: defaults.model > CLI flag > WARDEN_MODEL env var > SDK default
|
|
731
|
+
// Model precedence: defaults.agent.model > defaults.model > CLI flag > WARDEN_MODEL env var > SDK default
|
|
406
732
|
// sdkModel is undefined when no model is explicitly configured (lets SDK use its default).
|
|
407
733
|
// logModel records what was used for JSONL logs (sentinel when no explicit model).
|
|
408
|
-
const sdkModel =
|
|
409
|
-
const logModel =
|
|
734
|
+
const sdkModel = defaultModel;
|
|
735
|
+
const logModel = resolveCliLogModel(config, options.model);
|
|
410
736
|
const runnerOptions = {
|
|
411
737
|
apiKey,
|
|
412
738
|
model: sdkModel,
|
|
739
|
+
runtime: config?.defaults?.runtime ?? 'claude',
|
|
740
|
+
auxiliaryModel: defaultAuxiliaryModel,
|
|
741
|
+
synthesisModel: defaultSynthesisModel,
|
|
413
742
|
abortController,
|
|
414
|
-
maxTurns: config?.defaults?.maxTurns,
|
|
743
|
+
maxTurns: config?.defaults?.agent?.maxTurns ?? config?.defaults?.maxTurns,
|
|
415
744
|
batchDelayMs: config?.defaults?.batchDelayMs,
|
|
416
745
|
maxContextFiles: config?.defaults?.chunking?.maxContextFiles,
|
|
417
|
-
auxiliaryMaxRetries: config?.defaults?.
|
|
746
|
+
auxiliaryMaxRetries: config?.defaults?.auxiliary?.maxRetries ??
|
|
747
|
+
config?.defaults?.auxiliaryMaxRetries,
|
|
748
|
+
verifyFindings: config?.defaults?.verification?.enabled !== false,
|
|
418
749
|
};
|
|
419
|
-
const
|
|
750
|
+
const specs = skillsToRun.map(({ skill, remote, filters, ...skillOptions }) => ({
|
|
420
751
|
name: skill,
|
|
752
|
+
skill,
|
|
753
|
+
remote,
|
|
421
754
|
failOn: options.failOn,
|
|
422
|
-
resolveSkill: () => resolveSkillAsync(skill, repoPath, {
|
|
423
|
-
remote,
|
|
424
|
-
offline: options.offline,
|
|
425
|
-
}),
|
|
426
755
|
context: filterContextByPaths(context, filters),
|
|
427
|
-
runnerOptions,
|
|
756
|
+
runnerOptions: mergeSkillRunnerOptions(runnerOptions, skillOptions),
|
|
428
757
|
}));
|
|
758
|
+
let tasks;
|
|
759
|
+
const concurrency = options.parallel ?? DEFAULT_CONCURRENCY;
|
|
760
|
+
try {
|
|
761
|
+
tasks = await createSkillTasks({
|
|
762
|
+
specs,
|
|
763
|
+
repoPath,
|
|
764
|
+
options,
|
|
765
|
+
parallel: concurrency,
|
|
766
|
+
reporter,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
771
|
+
reporter.error(message);
|
|
772
|
+
emitEmptyRunLog(repoPath ?? cwd, options, {
|
|
773
|
+
code: 'unknown',
|
|
774
|
+
message,
|
|
775
|
+
timestamp: new Date().toISOString(),
|
|
776
|
+
});
|
|
777
|
+
return 1;
|
|
778
|
+
}
|
|
429
779
|
// Open the run's JSONL log before launching skills so `warden runs
|
|
430
780
|
// follow <runId>` works from a second terminal while the run is live.
|
|
431
781
|
const runId = generateRunId();
|
|
@@ -450,14 +800,14 @@ async function runSkills(context, options, reporter) {
|
|
|
450
800
|
startTime,
|
|
451
801
|
});
|
|
452
802
|
// Run skills with Ink UI (TTY) or simple console output (non-TTY)
|
|
453
|
-
const concurrency = options.parallel ?? DEFAULT_CONCURRENCY;
|
|
454
803
|
failFastController = options.failFast ? new AbortController() : undefined;
|
|
455
804
|
const taskOptions = {
|
|
456
805
|
mode: reporter.mode,
|
|
457
806
|
verbosity: reporter.verbosity,
|
|
458
807
|
concurrency,
|
|
459
808
|
failFastController,
|
|
460
|
-
|
|
809
|
+
onChunkComplete: (skillName, chunk) => appendChunkToRunLog(runLog, skillName, chunk),
|
|
810
|
+
onSkillComplete: (report) => appendReportToRunLog(runLog, report),
|
|
461
811
|
};
|
|
462
812
|
const results = reporter.mode.isTTY
|
|
463
813
|
? await runSkillTasksWithInk(tasks, taskOptions)
|
|
@@ -468,9 +818,6 @@ async function runSkills(context, options, reporter) {
|
|
|
468
818
|
const processed = processTaskResults(results, options.reportOn, effectiveMinConfidence);
|
|
469
819
|
return outputResultsAndHandleFixes(processed, options, reporter, runLog, totalDuration, failFastController?.signal.aborted);
|
|
470
820
|
}
|
|
471
|
-
/**
|
|
472
|
-
* Run in file mode: analyze specific files.
|
|
473
|
-
*/
|
|
474
821
|
async function runFileMode(filePatterns, options, reporter) {
|
|
475
822
|
const cwd = process.cwd();
|
|
476
823
|
// Build context from files
|
|
@@ -513,9 +860,6 @@ function parseGitRef(ref) {
|
|
|
513
860
|
}
|
|
514
861
|
return { base: ref, head: 'HEAD' };
|
|
515
862
|
}
|
|
516
|
-
/**
|
|
517
|
-
* Run in git ref mode: analyze changes from a git ref.
|
|
518
|
-
*/
|
|
519
863
|
async function runGitRefMode(gitRef, options, reporter) {
|
|
520
864
|
const cwd = process.cwd();
|
|
521
865
|
let repoPath;
|
|
@@ -565,9 +909,6 @@ async function runGitRefMode(gitRef, options, reporter) {
|
|
|
565
909
|
reporter.contextFiles(pullRequest.files);
|
|
566
910
|
return runSkills(context, options, reporter);
|
|
567
911
|
}
|
|
568
|
-
/**
|
|
569
|
-
* Run in config mode: use warden.toml triggers.
|
|
570
|
-
*/
|
|
571
912
|
async function runConfigMode(options, reporter) {
|
|
572
913
|
const cwd = process.cwd();
|
|
573
914
|
let repoPath;
|
|
@@ -589,13 +930,18 @@ async function runConfigMode(options, reporter) {
|
|
|
589
930
|
}
|
|
590
931
|
// Load config
|
|
591
932
|
const config = loadWardenConfig(dirname(configPath));
|
|
592
|
-
// Build context from local git
|
|
593
|
-
|
|
933
|
+
// Build context from local git. By default, mirror PR-style analysis:
|
|
934
|
+
// compare the configured/default branch merge base to HEAD.
|
|
935
|
+
const localReviewBase = resolveLocalReviewBase(config.defaults?.defaultBranch, repoPath);
|
|
936
|
+
const statusMessage = options.staged
|
|
937
|
+
? 'Analyzing staged changes...'
|
|
938
|
+
: `Analyzing changes from ${localReviewBase.base} to HEAD...`;
|
|
594
939
|
reporter.startContext(statusMessage);
|
|
595
940
|
const context = buildLocalEventContext({
|
|
596
|
-
base: 'HEAD',
|
|
941
|
+
base: options.staged ? 'HEAD' : localReviewBase.base,
|
|
942
|
+
head: options.staged ? undefined : 'HEAD',
|
|
597
943
|
cwd: repoPath,
|
|
598
|
-
defaultBranch:
|
|
944
|
+
defaultBranch: localReviewBase.defaultBranch,
|
|
599
945
|
staged: options.staged,
|
|
600
946
|
});
|
|
601
947
|
const pullRequest = context.pullRequest;
|
|
@@ -610,10 +956,7 @@ async function runConfigMode(options, reporter) {
|
|
|
610
956
|
reporter.renderEmptyState('No staged changes found');
|
|
611
957
|
}
|
|
612
958
|
else {
|
|
613
|
-
|
|
614
|
-
? 'Specify a git ref: warden HEAD~3 --skill <name>'
|
|
615
|
-
: undefined;
|
|
616
|
-
reporter.renderEmptyState('No uncommitted changes found', tip);
|
|
959
|
+
reporter.renderEmptyState(`No changes found from ${pullRequest.baseBranch} to HEAD`, 'Use --staged to analyze staged changes');
|
|
617
960
|
}
|
|
618
961
|
reporter.blank();
|
|
619
962
|
}
|
|
@@ -659,7 +1002,6 @@ async function runConfigMode(options, reporter) {
|
|
|
659
1002
|
if (!apiKey) {
|
|
660
1003
|
reporter.debug('No API key found. Using Claude Code subscription auth.');
|
|
661
1004
|
}
|
|
662
|
-
// Pre-flight: verify auth will work before starting analysis
|
|
663
1005
|
try {
|
|
664
1006
|
verifyAuth({ apiKey });
|
|
665
1007
|
}
|
|
@@ -675,31 +1017,54 @@ async function runConfigMode(options, reporter) {
|
|
|
675
1017
|
}
|
|
676
1018
|
// Build trigger tasks
|
|
677
1019
|
const effectiveMinConfidence = options.minConfidence ?? config.defaults?.minConfidence ?? 'medium';
|
|
678
|
-
const
|
|
1020
|
+
const specs = triggersToRun.map((trigger) => ({
|
|
679
1021
|
name: trigger.name,
|
|
680
1022
|
displayName: trigger.skill,
|
|
1023
|
+
skill: trigger.skill,
|
|
1024
|
+
remote: trigger.remote,
|
|
681
1025
|
failOn: trigger.failOn ?? options.failOn,
|
|
682
1026
|
minConfidence: trigger.minConfidence ?? effectiveMinConfidence,
|
|
683
|
-
resolveSkill: () => resolveSkillAsync(trigger.skill, repoPath, {
|
|
684
|
-
remote: trigger.remote,
|
|
685
|
-
offline: options.offline,
|
|
686
|
-
}),
|
|
687
1027
|
context: filterContextByPaths(context, trigger.filters),
|
|
688
1028
|
runnerOptions: {
|
|
689
1029
|
apiKey,
|
|
690
1030
|
model: trigger.model,
|
|
1031
|
+
runtime: trigger.runtime,
|
|
1032
|
+
auxiliaryModel: trigger.auxiliaryModel,
|
|
1033
|
+
synthesisModel: trigger.synthesisModel,
|
|
691
1034
|
abortController,
|
|
692
1035
|
maxTurns: trigger.maxTurns,
|
|
693
1036
|
maxContextFiles: config.defaults?.chunking?.maxContextFiles,
|
|
694
|
-
auxiliaryMaxRetries:
|
|
1037
|
+
auxiliaryMaxRetries: trigger.auxiliaryMaxRetries,
|
|
1038
|
+
verifyFindings: trigger.verifyFindings,
|
|
695
1039
|
},
|
|
696
1040
|
}));
|
|
1041
|
+
let tasks;
|
|
1042
|
+
const concurrency = options.parallel ?? config.runner?.concurrency ?? DEFAULT_CONCURRENCY;
|
|
1043
|
+
try {
|
|
1044
|
+
tasks = await createSkillTasks({
|
|
1045
|
+
specs,
|
|
1046
|
+
repoPath,
|
|
1047
|
+
options,
|
|
1048
|
+
parallel: concurrency,
|
|
1049
|
+
reporter,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1054
|
+
reporter.error(message);
|
|
1055
|
+
emitEmptyRunLog(repoPath, options, {
|
|
1056
|
+
code: 'unknown',
|
|
1057
|
+
message,
|
|
1058
|
+
timestamp: new Date().toISOString(),
|
|
1059
|
+
});
|
|
1060
|
+
return 1;
|
|
1061
|
+
}
|
|
697
1062
|
// Initialize the run's JSONL log up front so a second terminal can
|
|
698
1063
|
// `warden runs follow <runId>` while skills are still running.
|
|
699
1064
|
// Skill records are appended on each completion; the trailing summary
|
|
700
1065
|
// is appended in `outputResultsAndHandleFixes`.
|
|
701
1066
|
// Run-level model is the default (ignoring per-trigger overrides); per-skill models are on each report.
|
|
702
|
-
const defaultModel = config
|
|
1067
|
+
const defaultModel = resolveCliLogModel(config, options.model);
|
|
703
1068
|
const runId = generateRunId();
|
|
704
1069
|
const timestamp = new Date();
|
|
705
1070
|
const traceId = getTraceId();
|
|
@@ -722,14 +1087,14 @@ async function runConfigMode(options, reporter) {
|
|
|
722
1087
|
startTime,
|
|
723
1088
|
});
|
|
724
1089
|
// Run triggers with Ink UI (TTY) or simple console output (non-TTY)
|
|
725
|
-
const concurrency = options.parallel ?? config.runner?.concurrency ?? DEFAULT_CONCURRENCY;
|
|
726
1090
|
failFastController = options.failFast ? new AbortController() : undefined;
|
|
727
1091
|
const taskOptions = {
|
|
728
1092
|
mode: reporter.mode,
|
|
729
1093
|
verbosity: reporter.verbosity,
|
|
730
1094
|
concurrency,
|
|
731
1095
|
failFastController,
|
|
732
|
-
|
|
1096
|
+
onChunkComplete: (skillName, chunk) => appendChunkToRunLog(runLog, skillName, chunk),
|
|
1097
|
+
onSkillComplete: (report) => appendReportToRunLog(runLog, report),
|
|
733
1098
|
};
|
|
734
1099
|
const results = reporter.mode.isTTY
|
|
735
1100
|
? await runSkillTasksWithInk(tasks, taskOptions)
|
|
@@ -739,10 +1104,6 @@ async function runConfigMode(options, reporter) {
|
|
|
739
1104
|
const processed = processTaskResults(results, options.reportOn, effectiveMinConfidence);
|
|
740
1105
|
return outputResultsAndHandleFixes(processed, options, reporter, runLog, totalDuration, failFastController?.signal.aborted);
|
|
741
1106
|
}
|
|
742
|
-
/**
|
|
743
|
-
* Run in direct skill mode: run a specific skill on uncommitted changes.
|
|
744
|
-
* Used when --skill is specified without targets.
|
|
745
|
-
*/
|
|
746
1107
|
async function runDirectSkillMode(options, reporter) {
|
|
747
1108
|
const cwd = process.cwd();
|
|
748
1109
|
let repoPath;
|
|
@@ -757,13 +1118,18 @@ async function runDirectSkillMode(options, reporter) {
|
|
|
757
1118
|
// Load config to get defaultBranch if available
|
|
758
1119
|
const configPath = resolveConfigPath(options, repoPath);
|
|
759
1120
|
const config = existsSync(configPath) ? loadWardenConfig(dirname(configPath)) : null;
|
|
760
|
-
// Build context from local git
|
|
761
|
-
|
|
1121
|
+
// Build context from local git. By default, mirror PR-style analysis:
|
|
1122
|
+
// compare the configured/default branch merge base to HEAD.
|
|
1123
|
+
const localReviewBase = resolveLocalReviewBase(config?.defaults?.defaultBranch, repoPath);
|
|
1124
|
+
const statusMessage = options.staged
|
|
1125
|
+
? 'Analyzing staged changes...'
|
|
1126
|
+
: `Analyzing changes from ${localReviewBase.base} to HEAD...`;
|
|
762
1127
|
reporter.startContext(statusMessage);
|
|
763
1128
|
const context = buildLocalEventContext({
|
|
764
|
-
base: 'HEAD',
|
|
1129
|
+
base: options.staged ? 'HEAD' : localReviewBase.base,
|
|
1130
|
+
head: options.staged ? undefined : 'HEAD',
|
|
765
1131
|
cwd: repoPath,
|
|
766
|
-
defaultBranch:
|
|
1132
|
+
defaultBranch: localReviewBase.defaultBranch,
|
|
767
1133
|
staged: options.staged,
|
|
768
1134
|
});
|
|
769
1135
|
const pullRequest = context.pullRequest;
|
|
@@ -778,8 +1144,7 @@ async function runDirectSkillMode(options, reporter) {
|
|
|
778
1144
|
reporter.renderEmptyState('No staged changes found');
|
|
779
1145
|
}
|
|
780
1146
|
else {
|
|
781
|
-
|
|
782
|
-
reporter.renderEmptyState('No uncommitted changes found', tip);
|
|
1147
|
+
reporter.renderEmptyState(`No changes found from ${pullRequest.baseBranch} to HEAD`, 'Use --staged to analyze staged changes');
|
|
783
1148
|
}
|
|
784
1149
|
reporter.blank();
|
|
785
1150
|
}
|
|
@@ -794,7 +1159,7 @@ async function runCommand(options, reporter) {
|
|
|
794
1159
|
if (options.staged && targets.length > 0) {
|
|
795
1160
|
reporter.warning('--staged is ignored when targets are specified');
|
|
796
1161
|
}
|
|
797
|
-
// No targets with --skill → run skill directly on
|
|
1162
|
+
// No targets with --skill → run skill directly on current branch changes
|
|
798
1163
|
if (targets.length === 0 && options.skill) {
|
|
799
1164
|
return runDirectSkillMode(options, reporter);
|
|
800
1165
|
}
|
|
@@ -824,16 +1189,31 @@ async function runCommand(options, reporter) {
|
|
|
824
1189
|
// File mode
|
|
825
1190
|
return runFileMode(filePatterns, options, reporter);
|
|
826
1191
|
}
|
|
1192
|
+
/** Parse CLI input, dispatch the selected command, and perform shutdown cleanup. */
|
|
827
1193
|
export async function main() {
|
|
828
|
-
const { command, options, setupAppOptions, runsOptions } = parseCliArgs();
|
|
1194
|
+
const { command, options, helpTarget, setupAppOptions, runsOptions } = parseCliArgs();
|
|
829
1195
|
if (command === 'help') {
|
|
830
|
-
showHelp();
|
|
1196
|
+
showHelp(helpTarget);
|
|
831
1197
|
process.exit(0);
|
|
832
1198
|
}
|
|
833
1199
|
if (command === 'version') {
|
|
834
1200
|
showVersion();
|
|
835
1201
|
process.exit(0);
|
|
836
1202
|
}
|
|
1203
|
+
const reporter = createReporter(options);
|
|
1204
|
+
const originalCwd = process.cwd();
|
|
1205
|
+
const invocationCwd = resolveInvocationCwd(originalCwd, options.cwd);
|
|
1206
|
+
if (invocationCwd !== originalCwd) {
|
|
1207
|
+
try {
|
|
1208
|
+
process.chdir(invocationCwd);
|
|
1209
|
+
}
|
|
1210
|
+
catch (error) {
|
|
1211
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1212
|
+
reporter.error(`Unable to change to ${invocationCwd}: ${message}`);
|
|
1213
|
+
process.exit(1);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
837
1217
|
// Load environment variables from .env files at CLI entry point.
|
|
838
1218
|
// Try repo root first, fall back to cwd if not in a git repo.
|
|
839
1219
|
const cwd = process.cwd();
|
|
@@ -845,8 +1225,6 @@ export async function main() {
|
|
|
845
1225
|
// Not in a git repo - use cwd
|
|
846
1226
|
}
|
|
847
1227
|
loadEnvFiles(envDir);
|
|
848
|
-
// Create reporter based on options
|
|
849
|
-
const reporter = createReporter(options);
|
|
850
1228
|
// Show header (unless JSON output or quiet)
|
|
851
1229
|
if (!options.json) {
|
|
852
1230
|
reporter.header();
|
|
@@ -876,6 +1254,10 @@ export async function main() {
|
|
|
876
1254
|
process.exit(1);
|
|
877
1255
|
}
|
|
878
1256
|
return runRuns(runsOptions, options, reporter);
|
|
1257
|
+
case 'build':
|
|
1258
|
+
return runBuild(options, reporter, { abortController, interrupted });
|
|
1259
|
+
case 'improve':
|
|
1260
|
+
return runImprove(options, reporter, { abortController, interrupted });
|
|
879
1261
|
default:
|
|
880
1262
|
return runCommand(options, reporter);
|
|
881
1263
|
}
|