sentinelayer-cli 0.46.5 → 0.48.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 +47 -3
- package/package.json +12 -7
- package/skills/omar-gate-setup/SKILL.md +103 -0
- package/skills/senti-agent-identity/SKILL.md +80 -0
- package/skills/senti-judge/SKILL.md +139 -0
- package/skills/senti-room-notifications/SKILL.md +73 -0
- package/skills/senti-room-notifications/listen-wrapper.cjs +150 -0
- package/skills/senti-room-notifications/package.json +1 -0
- package/skills/senti-room-notifications/test-classifier.js +109 -0
- package/skills/senti-room-notifications/test-receipt.js +54 -0
- package/skills/sentinelayer-cli/SKILL.md +222 -0
- package/src/agents/devtestbot/runner.js +22 -11
- package/src/agents/devtestbot/tool.js +24 -11
- package/src/agents/jules/fix-cycle.js +7 -1
- package/src/agents/jules/tools/runtime-audit.js +17 -12
- package/src/agents/shared-tools/dispatch-core.js +10 -5
- package/src/ai/aidenid.js +4 -2
- package/src/ai/domain-target-store.js +11 -5
- package/src/ai/identity-store.js +11 -5
- package/src/ai/site-store.js +8 -4
- package/src/audit/orchestrator.js +21 -14
- package/src/audit/package.js +6 -4
- package/src/audit/persona-loop.js +7 -0
- package/src/audit/replay.js +4 -2
- package/src/auth/gate.js +32 -1
- package/src/auth/session-store.js +37 -0
- package/src/board/evidence-resolvers.js +3 -4
- package/src/cli.js +23 -3
- package/src/commands/ai/identity-lifecycle.js +30 -26
- package/src/commands/ai/provision-governance.js +32 -24
- package/src/commands/ai/shared.js +6 -4
- package/src/commands/audit.js +14 -12
- package/src/commands/board.js +2 -2
- package/src/commands/chat.js +8 -6
- package/src/commands/judge.js +125 -0
- package/src/commands/legacy-args.js +1 -0
- package/src/commands/mcp.js +45 -52
- package/src/commands/omargate.js +2 -1
- package/src/commands/plugin.js +11 -3
- package/src/commands/review.js +9 -4
- package/src/commands/scan.js +7 -7
- package/src/commands/session.js +516 -30
- package/src/commands/skills.js +45 -0
- package/src/commands/watch.js +9 -7
- package/src/config/output-filesystem.js +156 -0
- package/src/config/schema.js +4 -2
- package/src/config/service.js +40 -4
- package/src/cost/history.js +15 -9
- package/src/daemon/artifact-lineage.js +15 -12
- package/src/daemon/assignment-ledger.js +31 -25
- package/src/daemon/ast-drift.js +7 -3
- package/src/daemon/budget-governor.js +26 -20
- package/src/daemon/error-worker.js +28 -22
- package/src/daemon/fix-cycle.js +5 -4
- package/src/daemon/hybrid-mapper.js +19 -16
- package/src/daemon/ingest-refresh.js +4 -0
- package/src/daemon/jira-lifecycle.js +21 -17
- package/src/daemon/operator-control.js +18 -15
- package/src/daemon/reliability-lane.js +18 -15
- package/src/daemon/scope-engine.js +12 -8
- package/src/daemon/watchdog.js +15 -12
- package/src/engram/build-state.js +24 -3
- package/src/engram/capability-index.js +367 -0
- package/src/engram/compile-graph.js +1063 -0
- package/src/engram/contracts/action-receipt.js +82 -0
- package/src/engram/contracts/capability-index.js +259 -0
- package/src/engram/contracts/capability.js +377 -0
- package/src/engram/contracts/content-hash.js +8 -0
- package/src/engram/contracts/evidence.js +258 -0
- package/src/engram/contracts/graph.js +594 -0
- package/src/engram/contracts/identity.js +8 -0
- package/src/engram/contracts/index.js +149 -0
- package/src/engram/contracts/media-asset.js +85 -0
- package/src/engram/contracts/money.js +249 -0
- package/src/engram/contracts/source-snapshot.js +57 -0
- package/src/engram/contracts/task-context-pack.js +114 -0
- package/src/engram/contracts/temporal-segment.js +72 -0
- package/src/engram/contracts/versions.js +96 -0
- package/src/engram/extract-html.js +692 -0
- package/src/engram/graph-store.js +877 -0
- package/src/engram/index.js +98 -14
- package/src/engram/ingest-site.js +132 -0
- package/src/engram/live-state.js +1180 -0
- package/src/engram/namespace.js +34 -1
- package/src/engram/retrieve-site.js +753 -0
- package/src/engram/safe-reader.js +597 -0
- package/src/engram/source-intake.js +251 -0
- package/src/engram/store.js +227 -9
- package/src/ingest/engine.js +21 -28
- package/src/judge/verdict.js +305 -0
- package/src/legacy-cli.js +96 -19
- package/src/mcp/bridge-inputs.js +25 -4
- package/src/mcp/cli-command-tools.js +70 -0
- package/src/mcp/registry.js +17 -13
- package/src/mcp/session-stdio-server.js +297 -19
- package/src/memory/blackboard.js +5 -4
- package/src/net/gh-api-path.js +35 -9
- package/src/net/test-egress-guard.js +16 -4
- package/src/plugin/manifest.js +12 -8
- package/src/review/ai-review.js +110 -10
- package/src/review/investor-dd-devtestbot.js +17 -6
- package/src/review/investor-dd-orchestrator.js +21 -13
- package/src/review/local-review.js +26 -16
- package/src/review/omargate-cache.js +5 -5
- package/src/review/omargate-orchestrator.js +89 -17
- package/src/review/replay.js +9 -4
- package/src/review/report.js +22 -13
- package/src/review/typesafe-severity.js +201 -0
- package/src/session/admission-auth.js +34 -7
- package/src/session/admission.js +110 -15
- package/src/session/attachment-summary.js +77 -0
- package/src/session/edit-summary.js +19 -0
- package/src/session/invitations.js +65 -1
- package/src/session/mentions.js +17 -9
- package/src/session/message-edits.js +5 -4
- package/src/session/preview.js +10 -2
- package/src/session/recap.js +3 -2
- package/src/session/room-files.js +519 -0
- package/src/session/route-classes.js +22 -8
- package/src/session/setup-guides.js +4 -0
- package/src/session/standup.js +192 -0
- package/src/session/sync.js +229 -10
- package/src/session/tickets.js +41 -0
- package/src/session/transcript.js +2 -1
- package/src/session/wake/resolve-target.js +7 -3
- package/src/skills/listener-runtime.cjs +92 -0
- package/src/skills/service.js +207 -0
- package/src/swarm/factory.js +5 -5
- package/src/swarm/pentest.js +9 -9
- package/src/swarm/report.js +4 -4
- package/src/swarm/runtime.js +15 -12
- package/src/swarm/scenario-dsl.js +7 -5
- package/src/telemetry/ledger.js +16 -7
package/src/commands/mcp.js
CHANGED
|
@@ -34,7 +34,8 @@ import { runMcpStdioServer } from "../mcp/session-stdio-server.js";
|
|
|
34
34
|
import { runMcpDoctorProbes } from "../mcp/doctor.js";
|
|
35
35
|
import { runHostedMcpSmoke } from "../mcp/smoke.js";
|
|
36
36
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
37
|
-
import { resolveOutputRoot } from "../config/service.js";
|
|
37
|
+
import { resolveOutputContext, resolveOutputRoot } from "../config/service.js";
|
|
38
|
+
import { createOutputFileSystem } from "../config/output-filesystem.js";
|
|
38
39
|
|
|
39
40
|
function shouldEmitJson(options, command) {
|
|
40
41
|
const local = Boolean(options && options.json);
|
|
@@ -51,6 +52,27 @@ function normalizeOutputPath(rawValue, fallbackPath) {
|
|
|
51
52
|
return path.resolve(process.cwd(), candidate);
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
async function resolveWriteDestination(options, resolveDefaultPath, { suffix, ...pathOptions } = {}) {
|
|
56
|
+
if (String(options.path || "").trim()) {
|
|
57
|
+
const { outputRoot: outputPath, outputFs } = await resolveOutputContext({
|
|
58
|
+
cwd: process.cwd(),
|
|
59
|
+
outputDirOverride: options.path,
|
|
60
|
+
env: process.env,
|
|
61
|
+
});
|
|
62
|
+
return { outputPath, outputFs };
|
|
63
|
+
}
|
|
64
|
+
const outputContext = await resolveOutputContext({
|
|
65
|
+
cwd: process.cwd(),
|
|
66
|
+
outputDirOverride: options.outputDir,
|
|
67
|
+
env: process.env,
|
|
68
|
+
});
|
|
69
|
+
const defaultPath = await resolveDefaultPath({ ...pathOptions, outputContext });
|
|
70
|
+
return {
|
|
71
|
+
outputPath: suffix ? defaultPath.replace(".schema", suffix) : defaultPath,
|
|
72
|
+
outputFs: outputContext.outputFs,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
function zodIssueSummary(error) {
|
|
55
77
|
if (!(error instanceof ZodError)) {
|
|
56
78
|
return String(error instanceof Error ? error.message : error);
|
|
@@ -390,14 +412,9 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
390
412
|
.option("--force", "Overwrite destination file if it already exists")
|
|
391
413
|
.option("--json", "Emit machine-readable output")
|
|
392
414
|
.action(async (options, command) => {
|
|
393
|
-
const
|
|
394
|
-
cwd: process.cwd(),
|
|
395
|
-
outputDir: options.outputDir,
|
|
396
|
-
env: process.env,
|
|
397
|
-
});
|
|
398
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath);
|
|
415
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultMcpOutputPath);
|
|
399
416
|
const schemaPayload = buildMcpToolRegistrySchema();
|
|
400
|
-
const writtenPath = await writeJsonFile(outputPath, schemaPayload, { force: Boolean(options.force) });
|
|
417
|
+
const writtenPath = await writeJsonFile(outputPath, schemaPayload, { force: Boolean(options.force), outputFs });
|
|
401
418
|
|
|
402
419
|
const payload = {
|
|
403
420
|
command: "mcp schema write",
|
|
@@ -420,15 +437,10 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
420
437
|
.option("--force", "Overwrite destination file if it already exists")
|
|
421
438
|
.option("--json", "Emit machine-readable output")
|
|
422
439
|
.action(async (options, command) => {
|
|
423
|
-
const
|
|
424
|
-
cwd: process.cwd(),
|
|
425
|
-
outputDir: options.outputDir,
|
|
426
|
-
env: process.env,
|
|
427
|
-
});
|
|
428
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath.replace(".schema", ".aidenid-template"));
|
|
440
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultMcpOutputPath, { suffix: ".aidenid-template" });
|
|
429
441
|
const template = buildAidenIdRegistryTemplate();
|
|
430
442
|
validateMcpToolRegistry(template);
|
|
431
|
-
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
|
|
443
|
+
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force), outputFs });
|
|
432
444
|
const payload = {
|
|
433
445
|
command: "mcp registry init-aidenid",
|
|
434
446
|
outputPath: writtenPath,
|
|
@@ -456,17 +468,12 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
456
468
|
.option("--force", "Overwrite destination file if it already exists")
|
|
457
469
|
.option("--json", "Emit machine-readable output")
|
|
458
470
|
.action(async (options, command) => {
|
|
459
|
-
const
|
|
460
|
-
cwd: process.cwd(),
|
|
461
|
-
outputDir: options.outputDir,
|
|
462
|
-
env: process.env,
|
|
463
|
-
});
|
|
464
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath);
|
|
471
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultAidenIdAdapterContractPath);
|
|
465
472
|
const template = buildAidenIdProvisioningAdapterTemplate({
|
|
466
473
|
registryFile: options.registryFile,
|
|
467
474
|
});
|
|
468
475
|
validateAidenIdAdapterContract(template);
|
|
469
|
-
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
|
|
476
|
+
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force), outputFs });
|
|
470
477
|
const payload = {
|
|
471
478
|
command: "mcp registry init-aidenid-adapter",
|
|
472
479
|
outputPath: writtenPath,
|
|
@@ -497,15 +504,10 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
497
504
|
.option("--force", "Overwrite destination file if it already exists")
|
|
498
505
|
.option("--json", "Emit machine-readable output")
|
|
499
506
|
.action(async (options, command) => {
|
|
500
|
-
const
|
|
501
|
-
cwd: process.cwd(),
|
|
502
|
-
outputDir: options.outputDir,
|
|
503
|
-
env: process.env,
|
|
504
|
-
});
|
|
505
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath.replace(".schema", outputSuffix));
|
|
507
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultMcpOutputPath, { suffix: outputSuffix });
|
|
506
508
|
const template = buildSentinelayerSessionRegistryTemplate();
|
|
507
509
|
validateMcpToolRegistry(template);
|
|
508
|
-
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
|
|
510
|
+
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force), outputFs });
|
|
509
511
|
const payload = {
|
|
510
512
|
command: payloadCommand,
|
|
511
513
|
outputPath: writtenPath,
|
|
@@ -553,17 +555,12 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
553
555
|
.option("--force", "Overwrite destination file if it already exists")
|
|
554
556
|
.option("--json", "Emit machine-readable output")
|
|
555
557
|
.action(async (options, command) => {
|
|
556
|
-
const
|
|
557
|
-
cwd: process.cwd(),
|
|
558
|
-
outputDir: options.outputDir,
|
|
559
|
-
env: process.env,
|
|
560
|
-
});
|
|
561
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath);
|
|
558
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultHostedSentiSessionConnectorContractPath);
|
|
562
559
|
const template = buildHostedSentiSessionConnectorContract({
|
|
563
560
|
localRegistryFile: options.registryFile,
|
|
564
561
|
});
|
|
565
562
|
validateHostedSentiSessionConnectorContract(template);
|
|
566
|
-
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
|
|
563
|
+
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force), outputFs });
|
|
567
564
|
const payload = {
|
|
568
565
|
command: "mcp registry init-hosted-session-connector",
|
|
569
566
|
outputPath: writtenPath,
|
|
@@ -588,15 +585,10 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
588
585
|
.option("--force", "Overwrite destination file if it already exists")
|
|
589
586
|
.option("--json", "Emit machine-readable output")
|
|
590
587
|
.action(async (options, command) => {
|
|
591
|
-
const
|
|
592
|
-
cwd: process.cwd(),
|
|
593
|
-
outputDir: options.outputDir,
|
|
594
|
-
env: process.env,
|
|
595
|
-
});
|
|
596
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath.replace(".schema", ".cli-tools"));
|
|
588
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultMcpOutputPath, { suffix: ".cli-tools" });
|
|
597
589
|
const template = await buildSentinelayerCliRegistryTemplate();
|
|
598
590
|
validateMcpToolRegistry(template);
|
|
599
|
-
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
|
|
591
|
+
const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force), outputFs });
|
|
600
592
|
const payload = {
|
|
601
593
|
command: "mcp registry init-cli",
|
|
602
594
|
outputPath: writtenPath,
|
|
@@ -805,19 +797,13 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
805
797
|
const serverId = String(options.id || "")
|
|
806
798
|
.trim()
|
|
807
799
|
.toLowerCase();
|
|
808
|
-
const
|
|
809
|
-
cwd: process.cwd(),
|
|
810
|
-
outputDir: options.outputDir,
|
|
811
|
-
env: process.env,
|
|
812
|
-
serverId,
|
|
813
|
-
});
|
|
814
|
-
const outputPath = normalizeOutputPath(options.path, defaultPath);
|
|
800
|
+
const { outputPath, outputFs } = await resolveWriteDestination(options, resolveDefaultMcpServerConfigPath, { serverId });
|
|
815
801
|
const template = buildMcpServerConfigTemplate({
|
|
816
802
|
serverId,
|
|
817
803
|
registryFile: options.registryFile,
|
|
818
804
|
});
|
|
819
805
|
const config = validateMcpServerConfig(template);
|
|
820
|
-
const writtenPath = await writeJsonFile(outputPath, config, { force: Boolean(options.force) });
|
|
806
|
+
const writtenPath = await writeJsonFile(outputPath, config, { force: Boolean(options.force), outputFs });
|
|
821
807
|
|
|
822
808
|
const payload = {
|
|
823
809
|
command: "mcp server init",
|
|
@@ -924,7 +910,14 @@ Docs: https://github.com/mrrCarter/create-sentinelayer/blob/main/docs/mcp.md`,
|
|
|
924
910
|
serverId,
|
|
925
911
|
serverConfigFile: String(options.serverConfig || "").trim(),
|
|
926
912
|
});
|
|
927
|
-
const
|
|
913
|
+
const outputFs = String(options.path || "").trim()
|
|
914
|
+
? (await resolveOutputContext({
|
|
915
|
+
cwd: process.cwd(),
|
|
916
|
+
outputDirOverride: options.path,
|
|
917
|
+
env: process.env,
|
|
918
|
+
})).outputFs
|
|
919
|
+
: createOutputFileSystem({ workspaceRoot: process.cwd() });
|
|
920
|
+
const writtenPath = await writeJsonFile(outputPath, bridgePayload, { force: Boolean(options.force), outputFs });
|
|
928
921
|
|
|
929
922
|
const payload = {
|
|
930
923
|
command: "mcp bridge init-vscode",
|
package/src/commands/omargate.js
CHANGED
|
@@ -19,7 +19,8 @@ export function registerOmarGateCommand(program, invokeLegacy) {
|
|
|
19
19
|
.option("--max-parallel <n>", "Max concurrent persona calls (default: 4)")
|
|
20
20
|
.option("--model <id>", "LLM model override (default: gpt-5.3-codex)")
|
|
21
21
|
.option("--provider <name>", "LLM provider: sentinelayer, openai, anthropic")
|
|
22
|
-
.option("--max-cost <usd>", "
|
|
22
|
+
.option("--max-cost <usd>", "Reported AI cost stop threshold in USD (default: 5.0), not a pre-dispatch spending cap; with TypeSafe evaluation only, explicit 0 means NO monetary cap")
|
|
23
|
+
.option("--severity-classifier <mode>", "Opt-in classification evaluation: off|typesafe; sends generated finding evidence to TypeSafe, never changes gate severities (positive cost caps skip)", "off")
|
|
23
24
|
.option("--notify-session <session-id>", "Write Omar Deep billing-grade usage entries into this Senti session (default: auto-detect active Senti session)")
|
|
24
25
|
.option("--require-usage-ledger", "Fail if Omar Deep AI calls cannot write billing-grade session_usage")
|
|
25
26
|
.option("--persona <csv>", "Only run these personas (comma-separated IDs); unknown IDs are dropped + warned")
|
package/src/commands/plugin.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
|
|
4
4
|
import pc from "picocolors";
|
|
5
|
+
import { resolveOutputContext } from "../config/service.js";
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
buildPluginManifestTemplate,
|
|
@@ -61,11 +62,15 @@ export function registerPluginCommand(program) {
|
|
|
61
62
|
const pluginId = normalizePluginId(options.id);
|
|
62
63
|
const packType = normalizePluginPackType(options.packType);
|
|
63
64
|
const stage = normalizePluginLoadStage(options.stage);
|
|
64
|
-
const
|
|
65
|
+
const explicitPath = String(options.path || "").trim();
|
|
66
|
+
const outputContext = await resolveOutputContext({
|
|
65
67
|
cwd: process.cwd(),
|
|
66
|
-
|
|
68
|
+
outputDirOverride: explicitPath || options.outputDir,
|
|
67
69
|
env: process.env,
|
|
70
|
+
});
|
|
71
|
+
const defaultPath = explicitPath ? undefined : await resolveDefaultPluginManifestPath({
|
|
68
72
|
pluginId,
|
|
73
|
+
outputContext,
|
|
69
74
|
});
|
|
70
75
|
const outputPath = normalizeOutputPath(options.path, defaultPath);
|
|
71
76
|
const template = buildPluginManifestTemplate({
|
|
@@ -74,7 +79,10 @@ export function registerPluginCommand(program) {
|
|
|
74
79
|
stage,
|
|
75
80
|
});
|
|
76
81
|
const manifest = validatePluginManifest(template);
|
|
77
|
-
const writtenPath = await writeJsonFile(outputPath, manifest, {
|
|
82
|
+
const writtenPath = await writeJsonFile(outputPath, manifest, {
|
|
83
|
+
force: Boolean(options.force),
|
|
84
|
+
outputFs: outputContext.outputFs,
|
|
85
|
+
});
|
|
78
86
|
|
|
79
87
|
const payload = {
|
|
80
88
|
command: "plugin init",
|
package/src/commands/review.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fsp from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import process from "node:process";
|
|
4
3
|
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
writeReviewRunContext,
|
|
27
26
|
} from "../review/replay.js";
|
|
28
27
|
import { resolveActionOptions } from "./action-options.js";
|
|
28
|
+
import { resolveOutputContext } from "../config/service.js";
|
|
29
29
|
|
|
30
30
|
function shouldEmitJson(options, command) {
|
|
31
31
|
const local = Boolean(options && options.json);
|
|
@@ -134,12 +134,14 @@ async function executeReviewRun({
|
|
|
134
134
|
specFile,
|
|
135
135
|
});
|
|
136
136
|
const unifiedArtifacts = await writeUnifiedReviewArtifacts({
|
|
137
|
+
outputFs: deterministic.artifacts.outputFs,
|
|
137
138
|
runDirectory: deterministic.artifacts.runDirectory,
|
|
138
139
|
report: unified.report,
|
|
139
140
|
markdown: unified.markdown,
|
|
140
141
|
});
|
|
141
142
|
|
|
142
143
|
const contextWrite = await writeReviewRunContext({
|
|
144
|
+
outputFs: deterministic.artifacts.outputFs,
|
|
143
145
|
runDirectory: deterministic.artifacts.runDirectory,
|
|
144
146
|
runId: deterministic.runId,
|
|
145
147
|
targetPath: deterministic.targetPath,
|
|
@@ -511,6 +513,7 @@ export function registerReviewCommand(program) {
|
|
|
511
513
|
const comparison = compareUnifiedReports(source.report, replayOutcome.unified.report);
|
|
512
514
|
const artifact = await writeReviewComparisonArtifact({
|
|
513
515
|
runDirectory: replayOutcome.deterministic.artifacts.runDirectory,
|
|
516
|
+
outputFs: replayOutcome.deterministic.artifacts.outputFs,
|
|
514
517
|
baseRunId: source.report.runId,
|
|
515
518
|
candidateRunId: replayOutcome.unified.report.runId,
|
|
516
519
|
comparison,
|
|
@@ -571,6 +574,7 @@ export function registerReviewCommand(program) {
|
|
|
571
574
|
const comparison = compareUnifiedReports(base.report, candidate.report);
|
|
572
575
|
const artifact = await writeReviewComparisonArtifact({
|
|
573
576
|
runDirectory: candidate.runDirectory,
|
|
577
|
+
outputFs: candidate.outputFs,
|
|
574
578
|
baseRunId: base.report.runId,
|
|
575
579
|
candidateRunId: candidate.report.runId,
|
|
576
580
|
comparison,
|
|
@@ -578,9 +582,10 @@ export function registerReviewCommand(program) {
|
|
|
578
582
|
|
|
579
583
|
let outputPath = artifact.artifactPath;
|
|
580
584
|
if (actionOptions.outputFile) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
await
|
|
585
|
+
const outputContext = await resolveOutputContext({ cwd: targetPath, outputDirOverride: String(actionOptions.outputFile) });
|
|
586
|
+
outputPath = outputContext.outputRoot;
|
|
587
|
+
await outputContext.outputFs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
588
|
+
await outputContext.outputFs.writeFile(outputPath, `${JSON.stringify(artifact.payload, null, 2)}\n`, "utf-8");
|
|
584
589
|
}
|
|
585
590
|
|
|
586
591
|
const payload = {
|
package/src/commands/scan.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
resolveProvider,
|
|
13
13
|
} from "../ai/client.js";
|
|
14
14
|
import { recordCliLlmSessionUsage, usageNumber } from "../billing/llm-session-usage.js";
|
|
15
|
-
import { loadConfig,
|
|
15
|
+
import { loadConfig, resolveOutputContext, userConfigValues } from "../config/service.js";
|
|
16
16
|
import { evaluateBudget } from "../cost/budget.js";
|
|
17
17
|
import { appendCostEntry, summarizeCostHistory } from "../cost/history.js";
|
|
18
18
|
import { estimateModelCost } from "../cost/tracker.js";
|
|
@@ -292,15 +292,15 @@ async function resolvePreScanReportPath({
|
|
|
292
292
|
} = {}) {
|
|
293
293
|
const explicit = String(outputFile || "").trim();
|
|
294
294
|
if (explicit) {
|
|
295
|
-
return path.resolve(targetPath, explicit);
|
|
295
|
+
return { reportPath: path.resolve(targetPath, explicit), outputFs: fsp };
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
const outputRoot = await
|
|
298
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
299
299
|
cwd: targetPath,
|
|
300
300
|
outputDirOverride,
|
|
301
301
|
env: process.env,
|
|
302
302
|
});
|
|
303
|
-
return path.join(outputRoot, "reports", `scan-precheck-${createTimestampToken()}.md`);
|
|
303
|
+
return { reportPath: path.join(outputRoot, "reports", `scan-precheck-${createTimestampToken()}.md`), outputFs };
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
function printAiPreScanSummary({ reportPath, ai }) {
|
|
@@ -610,13 +610,13 @@ export function registerScanCommand(program) {
|
|
|
610
610
|
model: response.model,
|
|
611
611
|
aiMarkdown,
|
|
612
612
|
});
|
|
613
|
-
const reportPath = await resolvePreScanReportPath({
|
|
613
|
+
const { reportPath, outputFs } = await resolvePreScanReportPath({
|
|
614
614
|
targetPath,
|
|
615
615
|
outputDirOverride: options.outputDir,
|
|
616
616
|
outputFile: options.outputFile,
|
|
617
617
|
});
|
|
618
|
-
await
|
|
619
|
-
await
|
|
618
|
+
await outputFs.mkdir(path.dirname(reportPath), { recursive: true });
|
|
619
|
+
await outputFs.writeFile(reportPath, reportMarkdown, "utf-8");
|
|
620
620
|
|
|
621
621
|
const estimatedInputTokens = estimateTokens(prompt, { model: response.model });
|
|
622
622
|
const estimatedOutputTokens = estimateTokens(aiMarkdown, { model: response.model });
|