@sentry/warden 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/args.d.ts +15 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +53 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/service.d.ts +340 -0
- package/dist/cli/commands/service.d.ts.map +1 -0
- package/dist/cli/commands/service.js +99 -0
- package/dist/cli/commands/service.js.map +1 -0
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +49 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +291 -36
- package/dist/cli/main.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +16 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +41 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/sdk/analyze.js +1 -1
- package/dist/sdk/analyze.js.map +1 -1
- package/dist/sdk/local.d.ts +6 -0
- package/dist/sdk/local.d.ts.map +1 -1
- package/dist/sdk/local.js +78 -2
- package/dist/sdk/local.js.map +1 -1
- package/dist/sdk/offline.d.ts +13 -0
- package/dist/sdk/offline.d.ts.map +1 -0
- package/dist/sdk/offline.js +22 -0
- package/dist/sdk/offline.js.map +1 -0
- package/dist/sdk/prompt.d.ts +1 -1
- package/dist/sdk/prompt.d.ts.map +1 -1
- package/dist/sdk/prompt.js +3 -1
- package/dist/sdk/prompt.js.map +1 -1
- package/dist/sdk/runner.d.ts +1 -1
- package/dist/sdk/runner.d.ts.map +1 -1
- package/dist/sdk/runner.js.map +1 -1
- package/dist/sdk/runtimes/pi.d.ts.map +1 -1
- package/dist/sdk/runtimes/pi.js +148 -5
- package/dist/sdk/runtimes/pi.js.map +1 -1
- package/dist/sdk/types.d.ts +2 -0
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/service/client.d.ts +12 -0
- package/dist/service/client.d.ts.map +1 -0
- package/dist/service/client.js +35 -0
- package/dist/service/client.js.map +1 -0
- package/dist/service/findings.d.ts +337 -0
- package/dist/service/findings.d.ts.map +1 -0
- package/dist/service/findings.js +194 -0
- package/dist/service/findings.js.map +1 -0
- package/dist/service/index.d.ts +8 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +6 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/memory.d.ts +4 -0
- package/dist/service/memory.d.ts.map +1 -0
- package/dist/service/memory.js +12 -0
- package/dist/service/memory.js.map +1 -0
- package/dist/service/options.d.ts +31 -0
- package/dist/service/options.d.ts.map +1 -0
- package/dist/service/options.js +51 -0
- package/dist/service/options.js.map +1 -0
- package/dist/service/projection.d.ts +43 -0
- package/dist/service/projection.d.ts.map +1 -0
- package/dist/service/projection.js +155 -0
- package/dist/service/projection.js.map +1 -0
- package/package.json +3 -2
package/dist/cli/main.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join, relative, resolve } from 'node:path';
|
|
2
|
+
import { extname, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { config as dotenvConfig } from 'dotenv';
|
|
4
4
|
import { Sentry, flushSentry, setRepositoryScope, emitRunMetric, getTraceId, initSentry } from '../sentry.js';
|
|
5
5
|
import { emptyToUndefined, loadWardenConfigFile, resolveSkillConfigs } from '../config/loader.js';
|
|
6
6
|
import { verifyAuth } from '../sdk/runner.js';
|
|
7
7
|
import { findInvalidPiModelSelector as findInvalidPiModelSelectorTarget, invalidPiModelSelectorMessage, } from '../sdk/runtimes/model-selectors.js';
|
|
8
|
+
import { configureWardenOffline, isWardenOffline } from '../sdk/offline.js';
|
|
8
9
|
import { mapExtractionErrorCode } from '../sdk/errors.js';
|
|
9
10
|
import { aggregateAuxiliaryUsageAttribution, mergeAuxiliaryUsage } from '../sdk/usage.js';
|
|
10
11
|
import { resolveSkillAsync, SkillLoaderError } from '../skills/loader.js';
|
|
11
12
|
import { matchTrigger, filterContextByPaths, shouldFail, countFindingsAtOrAbove } from '../triggers/matcher.js';
|
|
12
13
|
import { filterFindings } from '../types/index.js';
|
|
13
|
-
import { DEFAULT_CONCURRENCY, getAnthropicApiKey } from '../utils/index.js';
|
|
14
|
+
import { DEFAULT_CONCURRENCY, getAnthropicApiKey, getVersion } from '../utils/index.js';
|
|
15
|
+
import { buildServiceRunEnvelope, publishRunFailOpen, recallMemoryFailOpen, renderHistoricalMemory, resolveServiceOptions, } from '../service/index.js';
|
|
14
16
|
import { isRepoRelativePath, normalizePath, resolveConfigInput } from '../utils/path.js';
|
|
15
17
|
import { parseCliArgs, showVersion, classifyTargets, expandTargetFileReferences } from './args.js';
|
|
16
18
|
import { showHelp } from './help.js';
|
|
@@ -26,6 +28,7 @@ import { runSetupApp } from './commands/setup-app.js';
|
|
|
26
28
|
import { runSync } from './commands/sync.js';
|
|
27
29
|
import { runRuns } from './commands/runs.js';
|
|
28
30
|
import { runBuild, runImprove } from './commands/build.js';
|
|
31
|
+
import { runServiceCommand } from './commands/service.js';
|
|
29
32
|
import { generatedSkillDefinitionRootExists, resolveGeneratedSkillTarget, } from '../skill-builder/definition.js';
|
|
30
33
|
/**
|
|
31
34
|
* Global abort controller for graceful shutdown on SIGINT.
|
|
@@ -95,6 +98,7 @@ function resolveLocalReviewBase(configDefaultBranch, repoPath) {
|
|
|
95
98
|
function emitEmptyRunLog(repoPath, options, error) {
|
|
96
99
|
const runId = generateRunId();
|
|
97
100
|
const timestamp = new Date();
|
|
101
|
+
const traceId = getTraceId();
|
|
98
102
|
let headSha;
|
|
99
103
|
try {
|
|
100
104
|
headSha = getHeadSha(repoPath);
|
|
@@ -104,7 +108,7 @@ function emitEmptyRunLog(repoPath, options, error) {
|
|
|
104
108
|
}
|
|
105
109
|
const content = renderJsonlString([], 0, {
|
|
106
110
|
runId,
|
|
107
|
-
traceId
|
|
111
|
+
traceId,
|
|
108
112
|
timestamp,
|
|
109
113
|
headSha,
|
|
110
114
|
error,
|
|
@@ -129,6 +133,7 @@ function emitEmptyRunLog(repoPath, options, error) {
|
|
|
129
133
|
if (options.json) {
|
|
130
134
|
process.stdout.write(content);
|
|
131
135
|
}
|
|
136
|
+
return { runId, timestamp, traceId, headSha };
|
|
132
137
|
}
|
|
133
138
|
function initializeRunLog(args) {
|
|
134
139
|
const { repoPath, runId, timestamp, traceId, headSha, model, outputPath, reporter, startTime } = args;
|
|
@@ -560,9 +565,15 @@ function loadOptionalConfig(options, repoPath) {
|
|
|
560
565
|
: repoPath
|
|
561
566
|
? resolve(repoPath, 'warden.toml')
|
|
562
567
|
: null;
|
|
563
|
-
|
|
568
|
+
const config = configPath && existsSync(configPath)
|
|
564
569
|
? loadWardenConfigFile(configPath)
|
|
565
570
|
: null;
|
|
571
|
+
applyWardenOfflinePolicy(options, config);
|
|
572
|
+
return config;
|
|
573
|
+
}
|
|
574
|
+
/** Apply durable config and CLI offline intent for Warden network access. */
|
|
575
|
+
function applyWardenOfflinePolicy(options, config) {
|
|
576
|
+
configureWardenOffline(Boolean(options.offline || config?.defaults?.offline));
|
|
566
577
|
}
|
|
567
578
|
/**
|
|
568
579
|
* Find the first Pi runner option using a model ID that is not provider/model.
|
|
@@ -578,11 +589,12 @@ function reportInvalidPiModelSelector(reporter, invalid) {
|
|
|
578
589
|
reporter.tip('Set a Pi model selector such as anthropic/claude-sonnet-4-6.');
|
|
579
590
|
}
|
|
580
591
|
function emitInvalidPiModelSelectorRunLog(repoPath, options, invalid) {
|
|
581
|
-
|
|
592
|
+
const error = {
|
|
582
593
|
code: 'invalid_model_selector',
|
|
583
594
|
message: invalidPiModelSelectorMessage(invalid),
|
|
584
595
|
timestamp: new Date().toISOString(),
|
|
585
|
-
}
|
|
596
|
+
};
|
|
597
|
+
return { run: emitEmptyRunLog(repoPath, options, error), error };
|
|
586
598
|
}
|
|
587
599
|
function verifyClaudeAuthForRun(args) {
|
|
588
600
|
const { apiKey, pathToClaudeCodeExecutable, reporter, repoPath, options } = args;
|
|
@@ -591,17 +603,17 @@ function verifyClaudeAuthForRun(args) {
|
|
|
591
603
|
}
|
|
592
604
|
try {
|
|
593
605
|
verifyAuth({ apiKey, pathToClaudeCodeExecutable });
|
|
594
|
-
return
|
|
606
|
+
return undefined;
|
|
595
607
|
}
|
|
596
608
|
catch (error) {
|
|
597
609
|
const message = error.message;
|
|
598
610
|
reporter.error(message);
|
|
599
|
-
|
|
611
|
+
const skillError = {
|
|
600
612
|
code: 'auth_failed',
|
|
601
613
|
message,
|
|
602
614
|
timestamp: new Date().toISOString(),
|
|
603
|
-
}
|
|
604
|
-
return
|
|
615
|
+
};
|
|
616
|
+
return { run: emitEmptyRunLog(repoPath, options, skillError), error: skillError };
|
|
605
617
|
}
|
|
606
618
|
}
|
|
607
619
|
function renderSkillRunHeader(args) {
|
|
@@ -621,7 +633,7 @@ async function createDirectSkillTask(args) {
|
|
|
621
633
|
try {
|
|
622
634
|
skill = await resolveSkillAsync(spec.skill, repoPath, {
|
|
623
635
|
remote: spec.remote,
|
|
624
|
-
offline:
|
|
636
|
+
offline: isWardenOffline(),
|
|
625
637
|
});
|
|
626
638
|
}
|
|
627
639
|
catch (error) {
|
|
@@ -794,6 +806,146 @@ async function outputResultsAndHandleFixes(processed, options, reporter, runLog,
|
|
|
794
806
|
}
|
|
795
807
|
return 0;
|
|
796
808
|
}
|
|
809
|
+
function resolveCliService(options, config, reporter) {
|
|
810
|
+
return resolveServiceOptions({
|
|
811
|
+
explicit: {
|
|
812
|
+
url: options.serviceUrl,
|
|
813
|
+
data: options.serviceData,
|
|
814
|
+
memory: options.serviceMemory,
|
|
815
|
+
timeoutMs: options.serviceTimeoutMs,
|
|
816
|
+
disabled: options.noService,
|
|
817
|
+
},
|
|
818
|
+
config: config?.service,
|
|
819
|
+
onWarning: (message) => reporter.warning(message),
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
/** Convert the finalized CLI exit status into its service run outcome. */
|
|
823
|
+
export function cliRunOutcome(exitCode) {
|
|
824
|
+
if (exitCode === 130)
|
|
825
|
+
return 'cancelled';
|
|
826
|
+
if (exitCode === 0)
|
|
827
|
+
return 'success';
|
|
828
|
+
return 'failure';
|
|
829
|
+
}
|
|
830
|
+
async function publishCliRun(args) {
|
|
831
|
+
if (!args.service)
|
|
832
|
+
return;
|
|
833
|
+
const service = args.service;
|
|
834
|
+
const repository = args.context.repository;
|
|
835
|
+
await publishRunFailOpen(service, {
|
|
836
|
+
clientRunId: args.runLog.baseRun.runId,
|
|
837
|
+
build: () => buildServiceRunEnvelope({
|
|
838
|
+
service,
|
|
839
|
+
clientRunId: args.runLog.baseRun.runId,
|
|
840
|
+
source: 'cli',
|
|
841
|
+
wardenVersion: getVersion(),
|
|
842
|
+
startedAt: new Date(args.runLog.baseRun.timestamp),
|
|
843
|
+
completedAt: new Date(new Date(args.runLog.baseRun.timestamp).getTime() + args.totalDuration),
|
|
844
|
+
outcome: cliRunOutcome(args.exitCode),
|
|
845
|
+
repository: {
|
|
846
|
+
provider: 'local',
|
|
847
|
+
owner: repository.owner,
|
|
848
|
+
name: repository.name,
|
|
849
|
+
fullName: repository.fullName,
|
|
850
|
+
},
|
|
851
|
+
reports: args.processed.reports.map((report, index) => ({
|
|
852
|
+
executionId: `${index + 1}:${report.skill}`,
|
|
853
|
+
report,
|
|
854
|
+
})),
|
|
855
|
+
...(args.recalledMemories?.length ? { recalledMemories: [...args.recalledMemories] } : {}),
|
|
856
|
+
...(args.memoryRecallId ? { memoryRecallId: args.memoryRecallId } : {}),
|
|
857
|
+
...(args.runLog.baseRun.traceId ? { traceId: args.runLog.baseRun.traceId } : {}),
|
|
858
|
+
...(args.runLog.baseRun.headSha ? { headSha: args.runLog.baseRun.headSha } : {}),
|
|
859
|
+
event: args.context.eventType,
|
|
860
|
+
}),
|
|
861
|
+
}, (message) => args.reporter.warning(message));
|
|
862
|
+
}
|
|
863
|
+
async function publishCliEarlyFailure(args) {
|
|
864
|
+
if (!args.service)
|
|
865
|
+
return;
|
|
866
|
+
const service = args.service;
|
|
867
|
+
const completedAt = new Date();
|
|
868
|
+
const repository = args.context.repository;
|
|
869
|
+
await publishRunFailOpen(service, {
|
|
870
|
+
clientRunId: args.run.runId,
|
|
871
|
+
build: () => buildServiceRunEnvelope({
|
|
872
|
+
service: { ...service, data: 'metrics', memory: false },
|
|
873
|
+
clientRunId: args.run.runId,
|
|
874
|
+
source: 'cli',
|
|
875
|
+
wardenVersion: getVersion(),
|
|
876
|
+
startedAt: args.run.timestamp,
|
|
877
|
+
completedAt,
|
|
878
|
+
outcome: 'failure',
|
|
879
|
+
repository: {
|
|
880
|
+
provider: 'local',
|
|
881
|
+
owner: repository.owner,
|
|
882
|
+
name: repository.name,
|
|
883
|
+
fullName: repository.fullName,
|
|
884
|
+
},
|
|
885
|
+
reports: args.skills.map((skill, index) => ({
|
|
886
|
+
executionId: `${index + 1}:${skill.skill}`,
|
|
887
|
+
report: {
|
|
888
|
+
skill: skill.skill,
|
|
889
|
+
summary: 'Skill did not start',
|
|
890
|
+
findings: [],
|
|
891
|
+
...(skill.model ? { model: skill.model } : {}),
|
|
892
|
+
...(skill.runtime ? { runtime: skill.runtime } : {}),
|
|
893
|
+
durationMs: Math.max(0, completedAt.getTime() - args.run.timestamp.getTime()),
|
|
894
|
+
error: args.error,
|
|
895
|
+
},
|
|
896
|
+
})),
|
|
897
|
+
...(args.run.traceId ? { traceId: args.run.traceId } : {}),
|
|
898
|
+
...(args.run.headSha ? { headSha: args.run.headSha } : {}),
|
|
899
|
+
event: args.context.eventType,
|
|
900
|
+
}),
|
|
901
|
+
}, (message) => args.reporter.warning(message));
|
|
902
|
+
}
|
|
903
|
+
async function publishCliEmptyRun(args) {
|
|
904
|
+
if (!args.service)
|
|
905
|
+
return;
|
|
906
|
+
const service = args.service;
|
|
907
|
+
const repository = args.context.repository;
|
|
908
|
+
await publishRunFailOpen(service, {
|
|
909
|
+
clientRunId: args.run.runId,
|
|
910
|
+
build: () => buildServiceRunEnvelope({
|
|
911
|
+
service,
|
|
912
|
+
clientRunId: args.run.runId,
|
|
913
|
+
source: 'cli',
|
|
914
|
+
wardenVersion: getVersion(),
|
|
915
|
+
startedAt: args.run.timestamp,
|
|
916
|
+
completedAt: new Date(),
|
|
917
|
+
outcome: 'skipped',
|
|
918
|
+
repository: {
|
|
919
|
+
provider: 'local',
|
|
920
|
+
owner: repository.owner,
|
|
921
|
+
name: repository.name,
|
|
922
|
+
fullName: repository.fullName,
|
|
923
|
+
},
|
|
924
|
+
reports: [],
|
|
925
|
+
...(args.run.traceId ? { traceId: args.run.traceId } : {}),
|
|
926
|
+
...(args.run.headSha ? { headSha: args.run.headSha } : {}),
|
|
927
|
+
event: args.context.eventType,
|
|
928
|
+
}),
|
|
929
|
+
}, (message) => args.reporter.warning(message));
|
|
930
|
+
}
|
|
931
|
+
async function recallCliMemory(args) {
|
|
932
|
+
if (!args.service?.memory)
|
|
933
|
+
return undefined;
|
|
934
|
+
const paths = args.context.pullRequest?.files.map((file) => file.filename) ?? [];
|
|
935
|
+
return recallMemoryFailOpen(args.service, {
|
|
936
|
+
protocolVersion: 1,
|
|
937
|
+
clientRecallId: generateRunId(),
|
|
938
|
+
repository: {
|
|
939
|
+
provider: 'local',
|
|
940
|
+
owner: args.context.repository.owner,
|
|
941
|
+
name: args.context.repository.name,
|
|
942
|
+
fullName: args.context.repository.fullName,
|
|
943
|
+
},
|
|
944
|
+
skills: [...args.skills],
|
|
945
|
+
languages: [...new Set(paths.map((path) => extname(path).slice(1)).filter(Boolean))],
|
|
946
|
+
paths,
|
|
947
|
+
});
|
|
948
|
+
}
|
|
797
949
|
/** Run one or more skills against an already constructed review context. */
|
|
798
950
|
export async function runSkills(context, options, reporter) {
|
|
799
951
|
const cwd = process.cwd();
|
|
@@ -802,6 +954,7 @@ export async function runSkills(context, options, reporter) {
|
|
|
802
954
|
const apiKey = getAnthropicApiKey();
|
|
803
955
|
const repoPath = findRepoPath(cwd);
|
|
804
956
|
const config = loadOptionalConfig(options, repoPath);
|
|
957
|
+
const service = resolveCliService(options, config, reporter);
|
|
805
958
|
const defaultModel = resolveCliDefaultModel(config, options.model);
|
|
806
959
|
const defaultAuxiliaryModel = resolveCliDefaultAuxiliaryModel(config);
|
|
807
960
|
const defaultSynthesisModel = resolveCliDefaultSynthesisModel(config);
|
|
@@ -873,22 +1026,39 @@ export async function runSkills(context, options, reporter) {
|
|
|
873
1026
|
emitRunMetric();
|
|
874
1027
|
// Handle case where no skills to run
|
|
875
1028
|
if (skillsToRun.length === 0) {
|
|
876
|
-
emitEmptyRunLog(repoPath ?? cwd, options);
|
|
1029
|
+
const run = emitEmptyRunLog(repoPath ?? cwd, options);
|
|
1030
|
+
await publishCliEmptyRun({ service, context, run, reporter });
|
|
877
1031
|
if (!options.json) {
|
|
878
1032
|
reporter.warning('No triggers matched for the changed files');
|
|
879
1033
|
reporter.tip('Specify a skill explicitly: warden <target> --skill <name>');
|
|
880
1034
|
}
|
|
881
1035
|
return 0;
|
|
882
1036
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1037
|
+
const authFailure = needsClaudeAuth(skillsToRun)
|
|
1038
|
+
? verifyClaudeAuthForRun({
|
|
1039
|
+
apiKey,
|
|
1040
|
+
pathToClaudeCodeExecutable,
|
|
1041
|
+
reporter,
|
|
1042
|
+
repoPath: repoPath ?? cwd,
|
|
1043
|
+
options,
|
|
1044
|
+
})
|
|
1045
|
+
: undefined;
|
|
1046
|
+
if (authFailure) {
|
|
1047
|
+
await publishCliEarlyFailure({
|
|
1048
|
+
service,
|
|
1049
|
+
context,
|
|
1050
|
+
...authFailure,
|
|
1051
|
+
skills: skillsToRun,
|
|
1052
|
+
reporter,
|
|
1053
|
+
});
|
|
890
1054
|
return 1;
|
|
891
1055
|
}
|
|
1056
|
+
const recall = await recallCliMemory({
|
|
1057
|
+
service,
|
|
1058
|
+
context,
|
|
1059
|
+
skills: skillsToRun.map((skill) => skill.skill),
|
|
1060
|
+
});
|
|
1061
|
+
const recalledMemories = recall?.memories ?? [];
|
|
892
1062
|
// Build skill tasks
|
|
893
1063
|
// Model precedence: defaults.agent.model > defaults.model > CLI flag > WARDEN_MODEL env var > SDK default
|
|
894
1064
|
// sdkModel is undefined when no model is explicitly configured (lets SDK use its default).
|
|
@@ -915,6 +1085,7 @@ export async function runSkills(context, options, reporter) {
|
|
|
915
1085
|
config?.defaults?.auxiliaryMaxRetries,
|
|
916
1086
|
verifyFindings: config?.defaults?.verification?.enabled !== false,
|
|
917
1087
|
captureTraces: options.traces,
|
|
1088
|
+
historicalEvidence: renderHistoricalMemory(recalledMemories),
|
|
918
1089
|
};
|
|
919
1090
|
const specs = skillsToRun.map(({ skill, remote, filters, ...skillOptions }) => ({
|
|
920
1091
|
name: skill,
|
|
@@ -927,7 +1098,8 @@ export async function runSkills(context, options, reporter) {
|
|
|
927
1098
|
const invalidModelSelector = findInvalidPiModelSelector(specs);
|
|
928
1099
|
if (invalidModelSelector) {
|
|
929
1100
|
reportInvalidPiModelSelector(reporter, invalidModelSelector);
|
|
930
|
-
emitInvalidPiModelSelectorRunLog(repoPath ?? cwd, options, invalidModelSelector);
|
|
1101
|
+
const failure = emitInvalidPiModelSelectorRunLog(repoPath ?? cwd, options, invalidModelSelector);
|
|
1102
|
+
await publishCliEarlyFailure({ service, context, ...failure, skills: skillsToRun, reporter });
|
|
931
1103
|
return 1;
|
|
932
1104
|
}
|
|
933
1105
|
let tasks;
|
|
@@ -944,11 +1116,13 @@ export async function runSkills(context, options, reporter) {
|
|
|
944
1116
|
catch (error) {
|
|
945
1117
|
const message = error instanceof Error ? error.message : String(error);
|
|
946
1118
|
reporter.error(message);
|
|
947
|
-
|
|
1119
|
+
const skillError = {
|
|
948
1120
|
code: 'unknown',
|
|
949
1121
|
message,
|
|
950
1122
|
timestamp: new Date().toISOString(),
|
|
951
|
-
}
|
|
1123
|
+
};
|
|
1124
|
+
const run = emitEmptyRunLog(repoPath ?? cwd, options, skillError);
|
|
1125
|
+
await publishCliEarlyFailure({ service, context, run, error: skillError, skills: skillsToRun, reporter });
|
|
952
1126
|
return 1;
|
|
953
1127
|
}
|
|
954
1128
|
// Open the run's JSONL log before launching skills so `warden runs
|
|
@@ -991,7 +1165,19 @@ export async function runSkills(context, options, reporter) {
|
|
|
991
1165
|
const totalDuration = Date.now() - startTime;
|
|
992
1166
|
const effectiveMinConfidence = options.minConfidence ?? config?.defaults?.minConfidence ?? 'medium';
|
|
993
1167
|
const processed = processTaskResults(results, options.reportOn, effectiveMinConfidence);
|
|
994
|
-
|
|
1168
|
+
const exitCode = await outputResultsAndHandleFixes(processed, options, reporter, runLog, totalDuration, failFastController?.signal.aborted);
|
|
1169
|
+
await publishCliRun({
|
|
1170
|
+
service,
|
|
1171
|
+
context,
|
|
1172
|
+
processed,
|
|
1173
|
+
runLog,
|
|
1174
|
+
totalDuration,
|
|
1175
|
+
exitCode,
|
|
1176
|
+
reporter,
|
|
1177
|
+
recalledMemories,
|
|
1178
|
+
memoryRecallId: recall?.clientRecallId,
|
|
1179
|
+
});
|
|
1180
|
+
return exitCode;
|
|
995
1181
|
}
|
|
996
1182
|
async function runFileMode(filePatterns, options, reporter) {
|
|
997
1183
|
const cwd = process.cwd();
|
|
@@ -1063,6 +1249,7 @@ async function runGitRefMode(gitRef, options, reporter) {
|
|
|
1063
1249
|
// Load config to get defaultBranch if available
|
|
1064
1250
|
const configPath = resolveConfigPath(options, repoPath);
|
|
1065
1251
|
const config = existsSync(configPath) ? loadWardenConfigFile(configPath) : null;
|
|
1252
|
+
applyWardenOfflinePolicy(options, config);
|
|
1066
1253
|
// Build context from local git
|
|
1067
1254
|
reporter.startContext(`Analyzing changes from ${gitRef}...`);
|
|
1068
1255
|
const context = buildLocalEventContext({
|
|
@@ -1108,6 +1295,8 @@ async function runConfigMode(options, reporter) {
|
|
|
1108
1295
|
}
|
|
1109
1296
|
// Load config
|
|
1110
1297
|
const config = loadWardenConfigFile(configPath);
|
|
1298
|
+
applyWardenOfflinePolicy(options, config);
|
|
1299
|
+
const service = resolveCliService(options, config, reporter);
|
|
1111
1300
|
// Build context from local git. By default, mirror PR-style analysis:
|
|
1112
1301
|
// compare the configured/default branch merge base to HEAD.
|
|
1113
1302
|
const localReviewBase = resolveLocalReviewBase(config.defaults?.defaultBranch, repoPath);
|
|
@@ -1128,7 +1317,8 @@ async function runConfigMode(options, reporter) {
|
|
|
1128
1317
|
return 1;
|
|
1129
1318
|
}
|
|
1130
1319
|
if (pullRequest.files.length === 0) {
|
|
1131
|
-
emitEmptyRunLog(repoPath, options);
|
|
1320
|
+
const run = emitEmptyRunLog(repoPath, options);
|
|
1321
|
+
await publishCliEmptyRun({ service, context, run, reporter });
|
|
1132
1322
|
if (!options.json) {
|
|
1133
1323
|
if (options.staged) {
|
|
1134
1324
|
reporter.renderEmptyState('No staged changes found');
|
|
@@ -1161,7 +1351,8 @@ async function runConfigMode(options, reporter) {
|
|
|
1161
1351
|
}
|
|
1162
1352
|
const triggersToRun = [...seen.values()];
|
|
1163
1353
|
if (triggersToRun.length === 0) {
|
|
1164
|
-
emitEmptyRunLog(repoPath, options);
|
|
1354
|
+
const run = emitEmptyRunLog(repoPath, options);
|
|
1355
|
+
await publishCliEmptyRun({ service, context, run, reporter });
|
|
1165
1356
|
if (!options.json) {
|
|
1166
1357
|
reporter.blank();
|
|
1167
1358
|
if (options.skill) {
|
|
@@ -1181,15 +1372,35 @@ async function runConfigMode(options, reporter) {
|
|
|
1181
1372
|
const triggerRuntimes = triggersToRun.map((trigger) => ({
|
|
1182
1373
|
runtime: options.runtime ?? trigger.runtime,
|
|
1183
1374
|
}));
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1375
|
+
const authFailure = needsClaudeAuth(triggerRuntimes)
|
|
1376
|
+
? verifyClaudeAuthForRun({
|
|
1377
|
+
apiKey,
|
|
1378
|
+
pathToClaudeCodeExecutable,
|
|
1379
|
+
reporter,
|
|
1380
|
+
repoPath,
|
|
1381
|
+
options,
|
|
1382
|
+
})
|
|
1383
|
+
: undefined;
|
|
1384
|
+
if (authFailure) {
|
|
1385
|
+
await publishCliEarlyFailure({
|
|
1386
|
+
service,
|
|
1387
|
+
context,
|
|
1388
|
+
...authFailure,
|
|
1389
|
+
skills: triggersToRun.map((trigger) => ({
|
|
1390
|
+
skill: trigger.skill,
|
|
1391
|
+
model: trigger.model,
|
|
1392
|
+
runtime: options.runtime ?? trigger.runtime,
|
|
1393
|
+
})),
|
|
1394
|
+
reporter,
|
|
1395
|
+
});
|
|
1191
1396
|
return 1;
|
|
1192
1397
|
}
|
|
1398
|
+
const recall = await recallCliMemory({
|
|
1399
|
+
service,
|
|
1400
|
+
context,
|
|
1401
|
+
skills: triggersToRun.map((trigger) => trigger.skill),
|
|
1402
|
+
});
|
|
1403
|
+
const recalledMemories = recall?.memories ?? [];
|
|
1193
1404
|
// Build trigger tasks
|
|
1194
1405
|
const effectiveMinConfidence = options.minConfidence ?? config.defaults?.minConfidence ?? 'medium';
|
|
1195
1406
|
const specs = triggersToRun.map((trigger) => ({
|
|
@@ -1219,12 +1430,24 @@ async function runConfigMode(options, reporter) {
|
|
|
1219
1430
|
auxiliaryMaxRetries: trigger.auxiliaryMaxRetries,
|
|
1220
1431
|
verifyFindings: trigger.verifyFindings,
|
|
1221
1432
|
captureTraces: options.traces,
|
|
1433
|
+
historicalEvidence: renderHistoricalMemory(recalledMemories),
|
|
1222
1434
|
},
|
|
1223
1435
|
}));
|
|
1224
1436
|
const invalidModelSelector = findInvalidPiModelSelector(specs);
|
|
1225
1437
|
if (invalidModelSelector) {
|
|
1226
1438
|
reportInvalidPiModelSelector(reporter, invalidModelSelector);
|
|
1227
|
-
emitInvalidPiModelSelectorRunLog(repoPath, options, invalidModelSelector);
|
|
1439
|
+
const failure = emitInvalidPiModelSelectorRunLog(repoPath, options, invalidModelSelector);
|
|
1440
|
+
await publishCliEarlyFailure({
|
|
1441
|
+
service,
|
|
1442
|
+
context,
|
|
1443
|
+
...failure,
|
|
1444
|
+
skills: triggersToRun.map((trigger) => ({
|
|
1445
|
+
skill: trigger.skill,
|
|
1446
|
+
model: trigger.model,
|
|
1447
|
+
runtime: options.runtime ?? trigger.runtime,
|
|
1448
|
+
})),
|
|
1449
|
+
reporter,
|
|
1450
|
+
});
|
|
1228
1451
|
return 1;
|
|
1229
1452
|
}
|
|
1230
1453
|
let tasks;
|
|
@@ -1241,10 +1464,23 @@ async function runConfigMode(options, reporter) {
|
|
|
1241
1464
|
catch (error) {
|
|
1242
1465
|
const message = error instanceof Error ? error.message : String(error);
|
|
1243
1466
|
reporter.error(message);
|
|
1244
|
-
|
|
1467
|
+
const skillError = {
|
|
1245
1468
|
code: 'unknown',
|
|
1246
1469
|
message,
|
|
1247
1470
|
timestamp: new Date().toISOString(),
|
|
1471
|
+
};
|
|
1472
|
+
const run = emitEmptyRunLog(repoPath, options, skillError);
|
|
1473
|
+
await publishCliEarlyFailure({
|
|
1474
|
+
service,
|
|
1475
|
+
context,
|
|
1476
|
+
run,
|
|
1477
|
+
error: skillError,
|
|
1478
|
+
skills: triggersToRun.map((trigger) => ({
|
|
1479
|
+
skill: trigger.skill,
|
|
1480
|
+
model: trigger.model,
|
|
1481
|
+
runtime: options.runtime ?? trigger.runtime,
|
|
1482
|
+
})),
|
|
1483
|
+
reporter,
|
|
1248
1484
|
});
|
|
1249
1485
|
return 1;
|
|
1250
1486
|
}
|
|
@@ -1291,7 +1527,19 @@ async function runConfigMode(options, reporter) {
|
|
|
1291
1527
|
// Process results and output
|
|
1292
1528
|
const totalDuration = Date.now() - startTime;
|
|
1293
1529
|
const processed = processTaskResults(results, options.reportOn, effectiveMinConfidence);
|
|
1294
|
-
|
|
1530
|
+
const exitCode = await outputResultsAndHandleFixes(processed, options, reporter, runLog, totalDuration, failFastController?.signal.aborted);
|
|
1531
|
+
await publishCliRun({
|
|
1532
|
+
service,
|
|
1533
|
+
context,
|
|
1534
|
+
processed,
|
|
1535
|
+
runLog,
|
|
1536
|
+
totalDuration,
|
|
1537
|
+
exitCode,
|
|
1538
|
+
reporter,
|
|
1539
|
+
recalledMemories,
|
|
1540
|
+
memoryRecallId: recall?.clientRecallId,
|
|
1541
|
+
});
|
|
1542
|
+
return exitCode;
|
|
1295
1543
|
}
|
|
1296
1544
|
async function runDirectSkillMode(options, reporter) {
|
|
1297
1545
|
const cwd = process.cwd();
|
|
@@ -1307,6 +1555,7 @@ async function runDirectSkillMode(options, reporter) {
|
|
|
1307
1555
|
// Load config to get defaultBranch if available
|
|
1308
1556
|
const configPath = resolveConfigPath(options, repoPath);
|
|
1309
1557
|
const config = existsSync(configPath) ? loadWardenConfigFile(configPath) : null;
|
|
1558
|
+
applyWardenOfflinePolicy(options, config);
|
|
1310
1559
|
// Build context from local git. By default, mirror PR-style analysis:
|
|
1311
1560
|
// compare the configured/default branch merge base to HEAD.
|
|
1312
1561
|
const localReviewBase = resolveLocalReviewBase(config?.defaults?.defaultBranch, repoPath);
|
|
@@ -1391,7 +1640,7 @@ async function runCommand(options, reporter) {
|
|
|
1391
1640
|
}
|
|
1392
1641
|
/** Parse CLI input, dispatch the selected command, and perform shutdown cleanup. */
|
|
1393
1642
|
export async function main() {
|
|
1394
|
-
const { command, options, helpTarget, setupAppOptions, runsOptions } = parseCliArgs();
|
|
1643
|
+
const { command, options, helpTarget, setupAppOptions, runsOptions, serviceOptions } = parseCliArgs();
|
|
1395
1644
|
if (command === 'help') {
|
|
1396
1645
|
showHelp(helpTarget);
|
|
1397
1646
|
process.exit(0);
|
|
@@ -1455,6 +1704,12 @@ export async function main() {
|
|
|
1455
1704
|
process.exit(1);
|
|
1456
1705
|
}
|
|
1457
1706
|
return runRuns(runsOptions, options, reporter);
|
|
1707
|
+
case 'service':
|
|
1708
|
+
if (!serviceOptions) {
|
|
1709
|
+
reporter.error('Missing service command options');
|
|
1710
|
+
return 1;
|
|
1711
|
+
}
|
|
1712
|
+
return runServiceCommand(serviceOptions, options, reporter);
|
|
1458
1713
|
case 'build':
|
|
1459
1714
|
return runBuild(options, reporter, { abortController, interrupted });
|
|
1460
1715
|
case 'improve':
|