sentinelayer-cli 0.8.0 → 0.8.2
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 +23 -2
- package/package.json +4 -4
- package/src/agents/ai-governance/index.js +12 -0
- package/src/agents/ai-governance/tools/base.js +171 -0
- package/src/agents/ai-governance/tools/eval-regression.js +47 -0
- package/src/agents/ai-governance/tools/hitl-audit.js +81 -0
- package/src/agents/ai-governance/tools/index.js +52 -0
- package/src/agents/ai-governance/tools/prompt-drift.js +42 -0
- package/src/agents/ai-governance/tools/provenance-check.js +69 -0
- package/src/agents/backend/index.js +12 -0
- package/src/agents/backend/tools/base.js +189 -0
- package/src/agents/backend/tools/circuit-breaker-check.js +123 -0
- package/src/agents/backend/tools/idempotency-audit.js +105 -0
- package/src/agents/backend/tools/index.js +87 -0
- package/src/agents/backend/tools/retry-audit.js +132 -0
- package/src/agents/backend/tools/timeout-audit.js +144 -0
- package/src/agents/code-quality/index.js +12 -0
- package/src/agents/code-quality/tools/base.js +159 -0
- package/src/agents/code-quality/tools/complexity-measure.js +197 -0
- package/src/agents/code-quality/tools/coupling-analysis.js +81 -0
- package/src/agents/code-quality/tools/cycle-detect.js +49 -0
- package/src/agents/code-quality/tools/dep-graph.js +196 -0
- package/src/agents/code-quality/tools/index.js +89 -0
- package/src/agents/data-layer/index.js +12 -0
- package/src/agents/data-layer/tools/base.js +181 -0
- package/src/agents/data-layer/tools/index-audit.js +165 -0
- package/src/agents/data-layer/tools/index.js +83 -0
- package/src/agents/data-layer/tools/migration-scan.js +135 -0
- package/src/agents/data-layer/tools/query-explain.js +120 -0
- package/src/agents/data-layer/tools/tenancy-scan.js +166 -0
- package/src/agents/documentation/index.js +12 -0
- package/src/agents/documentation/tools/api-diff.js +91 -0
- package/src/agents/documentation/tools/base.js +151 -0
- package/src/agents/documentation/tools/dead-link-check.js +58 -0
- package/src/agents/documentation/tools/docstring-coverage.js +78 -0
- package/src/agents/documentation/tools/index.js +52 -0
- package/src/agents/documentation/tools/readme-freshness.js +61 -0
- package/src/agents/envelope/fix-cycle.js +45 -0
- package/src/agents/envelope/index.js +31 -0
- package/src/agents/envelope/loop.js +150 -0
- package/src/agents/envelope/pulse.js +18 -0
- package/src/agents/envelope/stream.js +40 -0
- package/src/agents/infrastructure/index.js +12 -0
- package/src/agents/infrastructure/tools/base.js +171 -0
- package/src/agents/infrastructure/tools/checkov-run.js +32 -0
- package/src/agents/infrastructure/tools/drift-detect.js +59 -0
- package/src/agents/infrastructure/tools/iam-least-priv-check.js +78 -0
- package/src/agents/infrastructure/tools/index.js +52 -0
- package/src/agents/infrastructure/tools/tflint-run.js +31 -0
- package/src/agents/jules/loop.js +7 -4
- package/src/agents/jules/swarm/sub-agent.js +5 -1
- package/src/agents/jules/tools/auth-audit.js +10 -1
- package/src/agents/mode.js +113 -0
- package/src/agents/observability/index.js +12 -0
- package/src/agents/observability/tools/alert-audit.js +39 -0
- package/src/agents/observability/tools/base.js +181 -0
- package/src/agents/observability/tools/dashboard-gap.js +42 -0
- package/src/agents/observability/tools/index.js +54 -0
- package/src/agents/observability/tools/log-schema-check.js +74 -0
- package/src/agents/observability/tools/span-coverage.js +74 -0
- package/src/agents/persona-visuals.js +38 -0
- package/src/agents/release/index.js +12 -0
- package/src/agents/release/tools/base.js +181 -0
- package/src/agents/release/tools/changelog-diff.js +86 -0
- package/src/agents/release/tools/feature-flag-audit.js +126 -0
- package/src/agents/release/tools/index.js +61 -0
- package/src/agents/release/tools/rollback-verify.js +129 -0
- package/src/agents/release/tools/semver-check.js +109 -0
- package/src/agents/reliability/index.js +12 -0
- package/src/agents/reliability/tools/backpressure-check.js +129 -0
- package/src/agents/reliability/tools/base.js +181 -0
- package/src/agents/reliability/tools/chaos-probe.js +109 -0
- package/src/agents/reliability/tools/graceful-degradation-check.js +114 -0
- package/src/agents/reliability/tools/health-check-audit.js +111 -0
- package/src/agents/reliability/tools/index.js +87 -0
- package/src/agents/run-persona.js +109 -0
- package/src/agents/security/index.js +12 -0
- package/src/agents/security/tools/authz-audit.js +134 -0
- package/src/agents/security/tools/base.js +190 -0
- package/src/agents/security/tools/crypto-review.js +175 -0
- package/src/agents/security/tools/index.js +97 -0
- package/src/agents/security/tools/sast-scan.js +175 -0
- package/src/agents/security/tools/secrets-scan.js +216 -0
- package/src/agents/supply-chain/index.js +12 -0
- package/src/agents/supply-chain/tools/attestation-check.js +42 -0
- package/src/agents/supply-chain/tools/base.js +151 -0
- package/src/agents/supply-chain/tools/index.js +52 -0
- package/src/agents/supply-chain/tools/lockfile-integrity.js +73 -0
- package/src/agents/supply-chain/tools/package-verify.js +56 -0
- package/src/agents/supply-chain/tools/sbom-diff.js +34 -0
- package/src/agents/testing/index.js +12 -0
- package/src/agents/testing/tools/base.js +202 -0
- package/src/agents/testing/tools/coverage-gap.js +144 -0
- package/src/agents/testing/tools/flake-detect.js +125 -0
- package/src/agents/testing/tools/index.js +85 -0
- package/src/agents/testing/tools/mutation-test.js +143 -0
- package/src/agents/testing/tools/snapshot-diff.js +103 -0
- package/src/auth/gate.js +65 -37
- package/src/cli.js +1 -1
- package/src/commands/chat.js +3 -10
- package/src/commands/legacy-args.js +10 -0
- package/src/commands/omargate.js +36 -2
- package/src/commands/persona.js +46 -1
- package/src/commands/scan.js +3 -10
- package/src/commands/session.js +654 -6
- package/src/commands/spec.js +3 -10
- package/src/coord/events-log.js +141 -0
- package/src/coord/handshake.js +719 -0
- package/src/coord/index.js +35 -0
- package/src/coord/paths.js +84 -0
- package/src/coord/priority.js +62 -0
- package/src/coord/tarjan.js +157 -0
- package/src/cost/tokenizer.js +160 -0
- package/src/cost/tracker.js +61 -0
- package/src/daemon/artifact-lineage.js +362 -0
- package/src/daemon/assignment-ledger.js +117 -0
- package/src/daemon/ast-drift.js +496 -0
- package/src/daemon/ingest-refresh.js +69 -2
- package/src/ingest/engine.js +15 -0
- package/src/ingest/ownership.js +380 -0
- package/src/legacy-cli.js +68 -1
- package/src/orchestrator/kai-chen.js +126 -0
- package/src/review/ai-review.js +3 -10
- package/src/review/compliance-pack.js +389 -0
- package/src/review/investor-dd-config.js +54 -0
- package/src/review/investor-dd-file-loop.js +303 -0
- package/src/review/investor-dd-file-router.js +406 -0
- package/src/review/investor-dd-html-report.js +233 -0
- package/src/review/investor-dd-notification.js +120 -0
- package/src/review/investor-dd-orchestrator.js +405 -0
- package/src/review/investor-dd-persona-runner.js +275 -0
- package/src/review/live-validator.js +253 -0
- package/src/review/omargate-orchestrator.js +90 -2
- package/src/review/persona-prompts.js +244 -56
- package/src/review/reconciliation-rules.js +329 -0
- package/src/review/reproducibility-chain.js +136 -0
- package/src/review/scan-modes.js +102 -3
- package/src/session/agent-registry.js +7 -0
- package/src/session/analytics.js +479 -0
- package/src/session/daemon.js +609 -14
- package/src/session/file-locks.js +666 -0
- package/src/session/paths.js +4 -0
- package/src/session/recap.js +567 -0
- package/src/session/redact.js +82 -0
- package/src/session/runtime-bridge.js +24 -1
- package/src/session/scoring.js +406 -0
- package/src/session/setup-guides.js +304 -0
- package/src/session/store.js +318 -2
- package/src/session/stream.js +9 -1
- package/src/session/sync.js +753 -0
- package/src/session/tasks.js +1054 -0
- package/src/session/templates.js +188 -0
- package/src/swarm/runtime.js +1 -8
package/src/auth/gate.js
CHANGED
|
@@ -292,25 +292,6 @@ function hasTrustedBypassContext(args = []) {
|
|
|
292
292
|
return consumeNonceEnvelope(nonceEnvelope.nonceFile);
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
function isValidSessionToken(session) {
|
|
296
|
-
const token = String(session?.token || "");
|
|
297
|
-
if (!token || token !== token.trim()) {
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
if (/\s/.test(token)) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
// Require printable ASCII only for bearer token material in local metadata.
|
|
304
|
-
if (/[^\x21-\x7E]/.test(token)) {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
const tokenPrefix = String(session?.tokenPrefix || "").trim();
|
|
308
|
-
if (tokenPrefix && !token.includes(tokenPrefix)) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
295
|
function isSessionUnexpired(tokenExpiresAt) {
|
|
315
296
|
const normalized = String(tokenExpiresAt || "").trim();
|
|
316
297
|
if (!normalized) {
|
|
@@ -323,13 +304,25 @@ function isSessionUnexpired(tokenExpiresAt) {
|
|
|
323
304
|
return expiresAt >= Date.now();
|
|
324
305
|
}
|
|
325
306
|
|
|
307
|
+
// Gate-level session validation.
|
|
308
|
+
//
|
|
309
|
+
// Design principle: the gate is a "do they have a token?" check, not a
|
|
310
|
+
// "is the token cryptographically well-formed?" check. Server-side /auth/me
|
|
311
|
+
// and per-call bearer validation are the authoritative gate on the token
|
|
312
|
+
// material itself. Over-strict client-side checks (ASCII-only, exact-prefix
|
|
313
|
+
// inclusion, etc.) surface as "Authentication required" even when the user
|
|
314
|
+
// has a perfectly valid keyring entry, forcing them to logout/login repeatedly
|
|
315
|
+
// without fixing anything.
|
|
316
|
+
//
|
|
317
|
+
// So the gate checks:
|
|
318
|
+
// - session.token is present and non-empty
|
|
319
|
+
// - for source === "session", expiry is in the future
|
|
320
|
+
// - for source === "env" or "config", the downstream API call is the gate
|
|
326
321
|
function isAuthenticatedSessionValid(session) {
|
|
327
|
-
|
|
322
|
+
const token = String(session?.token || "").trim();
|
|
323
|
+
if (!token) {
|
|
328
324
|
return false;
|
|
329
325
|
}
|
|
330
|
-
|
|
331
|
-
// Persisted sessions must include a valid expiry bound. Env/config tokens
|
|
332
|
-
// are accepted as active auth sources and validated downstream by API calls.
|
|
333
326
|
if (String(session?.source || "").trim() === "session") {
|
|
334
327
|
return isSessionUnexpired(session?.tokenExpiresAt);
|
|
335
328
|
}
|
|
@@ -341,28 +334,29 @@ function isAuthenticatedSessionValid(session) {
|
|
|
341
334
|
* Returns true if auth is required but user is not logged in.
|
|
342
335
|
*
|
|
343
336
|
* @param {string[]} args - CLI arguments (after normalization)
|
|
344
|
-
* @returns {Promise<{ authenticated: boolean, session: object|null, bypassReason: string|null }>}
|
|
337
|
+
* @returns {Promise<{ authenticated: boolean, session: object|null, bypassReason: string|null, failureReason: string|null }>}
|
|
345
338
|
*/
|
|
346
339
|
export async function checkAuthGate(args) {
|
|
347
340
|
const first = String(args[0] || "").trim().toLowerCase();
|
|
348
341
|
|
|
349
342
|
if (!first || AUTH_BYPASS_COMMANDS.has(first)) {
|
|
350
|
-
return { authenticated: true, session: null, bypassReason: "auth_bypass_command" };
|
|
343
|
+
return { authenticated: true, session: null, bypassReason: "auth_bypass_command", failureReason: null };
|
|
351
344
|
}
|
|
352
345
|
|
|
353
346
|
if (NO_AUTH_REQUIRED.has(first)) {
|
|
354
|
-
return { authenticated: true, session: null, bypassReason: "no_auth_required" };
|
|
347
|
+
return { authenticated: true, session: null, bypassReason: "no_auth_required", failureReason: null };
|
|
355
348
|
}
|
|
356
349
|
|
|
357
350
|
if (isSessionNoAuthCommand(args)) {
|
|
358
|
-
return { authenticated: true, session: null, bypassReason: "session_no_auth_required" };
|
|
351
|
+
return { authenticated: true, session: null, bypassReason: "session_no_auth_required", failureReason: null };
|
|
359
352
|
}
|
|
360
353
|
|
|
361
354
|
if (process.env.SENTINELAYER_CLI_SKIP_AUTH === "1" && hasTrustedBypassContext(args)) {
|
|
362
|
-
return { authenticated: true, session: null, bypassReason: "env_bypass_guarded" };
|
|
355
|
+
return { authenticated: true, session: null, bypassReason: "env_bypass_guarded", failureReason: null };
|
|
363
356
|
}
|
|
364
357
|
|
|
365
358
|
// Check for active auth session across env -> config -> stored session.
|
|
359
|
+
let resolveError = null;
|
|
366
360
|
try {
|
|
367
361
|
const session = await resolveActiveAuthSession({
|
|
368
362
|
cwd: process.cwd(),
|
|
@@ -370,31 +364,65 @@ export async function checkAuthGate(args) {
|
|
|
370
364
|
autoRotate: false,
|
|
371
365
|
});
|
|
372
366
|
if (session && isAuthenticatedSessionValid(session)) {
|
|
373
|
-
return { authenticated: true, session, bypassReason: null };
|
|
367
|
+
return { authenticated: true, session, bypassReason: null, failureReason: null };
|
|
374
368
|
}
|
|
375
|
-
|
|
376
|
-
|
|
369
|
+
if (session) {
|
|
370
|
+
// Session resolved but failed validation (empty token or expired).
|
|
371
|
+
const tokenPresent = Boolean(String(session?.token || "").trim());
|
|
372
|
+
if (!tokenPresent) {
|
|
373
|
+
resolveError = "session_token_missing";
|
|
374
|
+
} else if (String(session?.source || "").trim() === "session" && !isSessionUnexpired(session?.tokenExpiresAt)) {
|
|
375
|
+
resolveError = "session_expired";
|
|
376
|
+
} else {
|
|
377
|
+
resolveError = "session_invalid";
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
resolveError = "no_session";
|
|
381
|
+
}
|
|
382
|
+
} catch (error) {
|
|
383
|
+
resolveError = error instanceof Error ? `session_read_error: ${error.message}` : "session_read_error";
|
|
377
384
|
}
|
|
378
385
|
|
|
379
|
-
return { authenticated: false, session: null, bypassReason: null };
|
|
386
|
+
return { authenticated: false, session: null, bypassReason: null, failureReason: resolveError };
|
|
380
387
|
}
|
|
381
388
|
|
|
382
389
|
/**
|
|
383
|
-
* Print auth required message and exit.
|
|
390
|
+
* Print auth required message and exit. Optional failureReason surfaces the
|
|
391
|
+
* specific reason so users can diagnose stale sessions, expired tokens, and
|
|
392
|
+
* keyring failures without a round trip.
|
|
384
393
|
*/
|
|
385
|
-
export function printAuthRequired() {
|
|
394
|
+
export function printAuthRequired(failureReason = null) {
|
|
395
|
+
const reason = String(failureReason || "").trim();
|
|
386
396
|
console.error("");
|
|
387
397
|
console.error(pc.bold(pc.red("Authentication required.")));
|
|
388
398
|
console.error("");
|
|
389
|
-
|
|
399
|
+
if (reason === "session_expired") {
|
|
400
|
+
console.error(" Your stored session has expired. Log in again:");
|
|
401
|
+
} else if (reason === "session_token_missing") {
|
|
402
|
+
console.error(" Your session metadata is present but the token is missing");
|
|
403
|
+
console.error(" (likely a keyring read failure or mismatched storage).");
|
|
404
|
+
console.error("");
|
|
405
|
+
console.error(" " + pc.yellow("Fix:") + " log out to clear the stale metadata, then log in:");
|
|
406
|
+
console.error(" " + pc.cyan("sentinelayer-cli auth logout"));
|
|
407
|
+
} else if (reason && reason.startsWith("session_read_error")) {
|
|
408
|
+
console.error(" Session read failed: " + pc.yellow(reason.replace(/^session_read_error:\s*/, "")));
|
|
409
|
+
console.error(" Log out and back in to reset local state:");
|
|
410
|
+
console.error(" " + pc.cyan("sentinelayer-cli auth logout"));
|
|
411
|
+
} else {
|
|
412
|
+
console.error(" Log in to SentinelLayer to use CLI commands:");
|
|
413
|
+
}
|
|
390
414
|
console.error("");
|
|
391
415
|
console.error(" " + pc.cyan(authLoginHint()));
|
|
392
416
|
console.error("");
|
|
393
417
|
console.error(" This opens your browser to authenticate via GitHub or Google.");
|
|
394
418
|
console.error(" Your session is encrypted and stored locally.");
|
|
395
419
|
console.error("");
|
|
396
|
-
|
|
397
|
-
|
|
420
|
+
if (!reason || reason === "no_session") {
|
|
421
|
+
console.error(" " + pc.gray("Why? All CLI operations sync to your SentinelLayer account —"));
|
|
422
|
+
console.error(" " + pc.gray("audit reports, findings, cost tracking, and run history."));
|
|
423
|
+
} else {
|
|
424
|
+
console.error(" " + pc.gray(`Diagnostic: ${reason}`));
|
|
425
|
+
}
|
|
398
426
|
console.error("");
|
|
399
427
|
process.exitCode = 1;
|
|
400
428
|
}
|
package/src/cli.js
CHANGED
|
@@ -239,7 +239,7 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
239
239
|
const { checkAuthGate, printAuthRequired } = await import("./auth/gate.js");
|
|
240
240
|
const authResult = await checkAuthGate(normalizedArgs);
|
|
241
241
|
if (!authResult.authenticated) {
|
|
242
|
-
printAuthRequired();
|
|
242
|
+
printAuthRequired(authResult.failureReason);
|
|
243
243
|
return;
|
|
244
244
|
}
|
|
245
245
|
|
package/src/commands/chat.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
resolveProvider,
|
|
11
11
|
} from "../ai/client.js";
|
|
12
12
|
import { resolveOutputRoot } from "../config/service.js";
|
|
13
|
+
import { estimateTokens } from "../cost/tokenizer.js";
|
|
13
14
|
|
|
14
15
|
function shouldEmitJson(options, command) {
|
|
15
16
|
const local = Boolean(options && options.json);
|
|
@@ -24,14 +25,6 @@ function createSessionId() {
|
|
|
24
25
|
return `${stamp}-${random}`;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
function estimateTokens(text) {
|
|
28
|
-
const normalized = String(text || "");
|
|
29
|
-
if (!normalized) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
return Math.max(1, Math.ceil(normalized.length / 4));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
28
|
async function readPromptFromStdin() {
|
|
36
29
|
if (process.stdin.isTTY) {
|
|
37
30
|
return "";
|
|
@@ -132,8 +125,8 @@ export function registerChatCommand(program) {
|
|
|
132
125
|
|
|
133
126
|
const durationMs = Date.now() - startedAt;
|
|
134
127
|
const generatedAt = new Date().toISOString();
|
|
135
|
-
const inputTokens = estimateTokens(prompt);
|
|
136
|
-
const outputTokens = estimateTokens(responseText);
|
|
128
|
+
const inputTokens = estimateTokens(prompt, { model });
|
|
129
|
+
const outputTokens = estimateTokens(responseText, { model });
|
|
137
130
|
|
|
138
131
|
await appendTranscriptEntries({
|
|
139
132
|
transcriptPath,
|
|
@@ -19,6 +19,13 @@ function appendOutputDirFlag(args, maybeOutputDir) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function appendPassthroughFlag(args, flagName, maybeValue) {
|
|
23
|
+
const value = String(maybeValue || "").trim();
|
|
24
|
+
if (value) {
|
|
25
|
+
args.push(flagName, value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
export function buildLegacyArgs(baseArgs, { commandOptions = {}, command } = {}) {
|
|
23
30
|
const args = [...baseArgs];
|
|
24
31
|
appendPathFlag(args, commandOptions.path);
|
|
@@ -26,5 +33,8 @@ export function buildLegacyArgs(baseArgs, { commandOptions = {}, command } = {})
|
|
|
26
33
|
if (wantsJsonOutput(commandOptions, command)) {
|
|
27
34
|
args.push("--json");
|
|
28
35
|
}
|
|
36
|
+
// Omar Gate per-persona filter flags (A-CLI-1).
|
|
37
|
+
appendPassthroughFlag(args, "--persona", commandOptions.persona);
|
|
38
|
+
appendPassthroughFlag(args, "--skip-persona", commandOptions.skipPersona);
|
|
29
39
|
return args;
|
|
30
40
|
}
|
package/src/commands/omargate.js
CHANGED
|
@@ -12,11 +12,13 @@ export function registerOmarGateCommand(program, invokeLegacy) {
|
|
|
12
12
|
.option("--output-dir <path>", "Artifact root for report output")
|
|
13
13
|
.option("--no-ai", "Skip AI review layer (deterministic only)")
|
|
14
14
|
.option("--ai-dry-run", "Run AI layer in dry-run mode (no LLM call)")
|
|
15
|
-
.option("--scan-mode <mode>", "Scan depth: baseline (1 persona), deep (
|
|
15
|
+
.option("--scan-mode <mode>", "Scan depth: baseline (1 persona), deep (13), full-depth (13)")
|
|
16
16
|
.option("--max-parallel <n>", "Max concurrent persona calls (default: 4)")
|
|
17
17
|
.option("--model <id>", "LLM model override (default: gpt-5.3-codex)")
|
|
18
|
-
.option("--provider <name>", "LLM provider: sentinelayer, openai, anthropic
|
|
18
|
+
.option("--provider <name>", "LLM provider: sentinelayer, openai, anthropic")
|
|
19
19
|
.option("--max-cost <usd>", "Maximum AI layer cost in USD (default: 5.0)")
|
|
20
|
+
.option("--persona <csv>", "Only run these personas (comma-separated IDs); unknown IDs are dropped + warned")
|
|
21
|
+
.option("--skip-persona <csv>", "Skip these personas (comma-separated IDs)")
|
|
20
22
|
.option("--stream", "Emit NDJSON events to stdout as personas run")
|
|
21
23
|
.option("--json", "Emit machine-readable output")
|
|
22
24
|
.action(async (options, command) => {
|
|
@@ -26,4 +28,36 @@ export function registerOmarGateCommand(program, invokeLegacy) {
|
|
|
26
28
|
});
|
|
27
29
|
await invokeLegacy(legacyArgs);
|
|
28
30
|
});
|
|
31
|
+
|
|
32
|
+
// Investor-DD mode (docs/INVESTOR_DD_ARCHITECTURE.md). Per-file agentic
|
|
33
|
+
// review across all 13 personas with deterministic file routing,
|
|
34
|
+
// reproducibility chain per finding, Senti session streaming, and a
|
|
35
|
+
// final report shipped via email + dashboard card. Trades runtime +
|
|
36
|
+
// cost for depth — budgets default to 45min / $25 vs deep's 2min / $5.
|
|
37
|
+
omargate
|
|
38
|
+
.command("investor-dd")
|
|
39
|
+
.description("Investor-grade due-diligence audit: per-file agentic review + reproducibility chain + email/dashboard report")
|
|
40
|
+
.option("--path <path>", "Target repository path")
|
|
41
|
+
.option("--output-dir <path>", "Artifact root for report output")
|
|
42
|
+
.option("--max-cost <usd>", "Maximum LLM cost in USD (default: 25.0)")
|
|
43
|
+
.option("--max-runtime-minutes <n>", "Maximum wall-clock runtime (default: 45)")
|
|
44
|
+
.option("--max-parallel <n>", "Max concurrent persona loops (default: 3)")
|
|
45
|
+
.option("--model <id>", "LLM model override (default: gpt-5.3-codex)")
|
|
46
|
+
.option("--provider <name>", "LLM provider: sentinelayer, openai, anthropic")
|
|
47
|
+
.option("--persona <csv>", "Only run these personas (comma-separated IDs)")
|
|
48
|
+
.option("--skip-persona <csv>", "Skip these personas (comma-separated IDs)")
|
|
49
|
+
.option("--stream", "Emit NDJSON events to stdout as personas work file-by-file")
|
|
50
|
+
.option("--notify-email <addr>", "Send final report to this email (default: account email)")
|
|
51
|
+
.option("--notify-session <session-id>", "Stream progress into this Senti session (default: auto-start)")
|
|
52
|
+
.option("--no-email", "Skip email dispatch")
|
|
53
|
+
.option("--no-dashboard", "Skip dashboard card persistence")
|
|
54
|
+
.option("--dry-run", "Validate config + emit plan.json; skip LLM calls")
|
|
55
|
+
.option("--json", "Emit machine-readable final output")
|
|
56
|
+
.action(async (options, command) => {
|
|
57
|
+
const legacyArgs = buildLegacyArgs(["/omargate", "investor-dd"], {
|
|
58
|
+
commandOptions: options,
|
|
59
|
+
command,
|
|
60
|
+
});
|
|
61
|
+
await invokeLegacy(legacyArgs);
|
|
62
|
+
});
|
|
29
63
|
}
|
package/src/commands/persona.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
|
|
3
|
+
import { PERSONA_MODES } from "../agents/mode.js";
|
|
4
|
+
import {
|
|
5
|
+
SUPPORTED_PERSONA_IDS,
|
|
6
|
+
runPersona,
|
|
7
|
+
} from "../agents/run-persona.js";
|
|
1
8
|
import { buildLegacyArgs } from "./legacy-args.js";
|
|
2
9
|
|
|
3
10
|
export function registerPersonaCommand(program, invokeLegacy) {
|
|
4
11
|
const persona = program
|
|
5
12
|
.command("persona")
|
|
6
|
-
.description("
|
|
13
|
+
.description("Run persona-scoped domain-tool sweeps or orchestrator reports");
|
|
7
14
|
|
|
8
15
|
persona
|
|
9
16
|
.command("orchestrator")
|
|
@@ -24,4 +31,42 @@ export function registerPersonaCommand(program, invokeLegacy) {
|
|
|
24
31
|
});
|
|
25
32
|
await invokeLegacy(legacyArgs);
|
|
26
33
|
});
|
|
34
|
+
|
|
35
|
+
persona
|
|
36
|
+
.command("run <personaId>")
|
|
37
|
+
.description(
|
|
38
|
+
`Run a single persona's domain tools over the repo and emit findings. Supported ids: ${SUPPORTED_PERSONA_IDS.join(", ")}.`
|
|
39
|
+
)
|
|
40
|
+
.option(
|
|
41
|
+
"--mode <mode>",
|
|
42
|
+
`Persona mode: ${PERSONA_MODES.join("|")}. Audit emits findings; codegen attaches allowed-tools + prompt-suffix plan so callers can drive the LLM edit loop.`,
|
|
43
|
+
"audit"
|
|
44
|
+
)
|
|
45
|
+
.option("--path <path>", "Repository root to scan (default: cwd)", ".")
|
|
46
|
+
.option(
|
|
47
|
+
"--files <csv>",
|
|
48
|
+
"Optional comma-separated list of files to focus the sweep. Empty = whole repo."
|
|
49
|
+
)
|
|
50
|
+
.option(
|
|
51
|
+
"--json",
|
|
52
|
+
"Always-on for this subcommand; kept for interface parity. Output is a single-line JSON object on stdout.",
|
|
53
|
+
true
|
|
54
|
+
)
|
|
55
|
+
.action(async (personaId, options) => {
|
|
56
|
+
try {
|
|
57
|
+
const result = await runPersona({
|
|
58
|
+
personaId,
|
|
59
|
+
mode: options.mode,
|
|
60
|
+
rootPath: options.path,
|
|
61
|
+
files: options.files,
|
|
62
|
+
});
|
|
63
|
+
process.stdout.write(JSON.stringify(result));
|
|
64
|
+
process.stdout.write("\n");
|
|
65
|
+
process.exitCode = 0;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
const message = err && err.message ? err.message : String(err);
|
|
68
|
+
process.stderr.write(`persona run failed: ${message}\n`);
|
|
69
|
+
process.exitCode = 2;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
27
72
|
}
|
package/src/commands/scan.js
CHANGED
|
@@ -15,6 +15,7 @@ import { loadConfig, resolveOutputRoot } from "../config/service.js";
|
|
|
15
15
|
import { evaluateBudget } from "../cost/budget.js";
|
|
16
16
|
import { appendCostEntry, summarizeCostHistory } from "../cost/history.js";
|
|
17
17
|
import { estimateModelCost } from "../cost/tracker.js";
|
|
18
|
+
import { estimateTokens } from "../cost/tokenizer.js";
|
|
18
19
|
import {
|
|
19
20
|
applyPolicyPackToScanProfile,
|
|
20
21
|
resolveActivePolicyPack,
|
|
@@ -168,14 +169,6 @@ function parsePercent(rawValue, field) {
|
|
|
168
169
|
return normalized;
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
function estimateTokenCount(text) {
|
|
172
|
-
const normalized = String(text || "");
|
|
173
|
-
if (!normalized) {
|
|
174
|
-
return 0;
|
|
175
|
-
}
|
|
176
|
-
return Math.max(1, Math.ceil(normalized.length / 4));
|
|
177
|
-
}
|
|
178
|
-
|
|
179
172
|
function resolveConfiguredApiKey(provider, resolvedConfig = {}) {
|
|
180
173
|
const normalizedProvider = String(provider || "").trim().toLowerCase();
|
|
181
174
|
if (normalizedProvider === "openai") {
|
|
@@ -622,8 +615,8 @@ export function registerScanCommand(program) {
|
|
|
622
615
|
await fsp.mkdir(path.dirname(reportPath), { recursive: true });
|
|
623
616
|
await fsp.writeFile(reportPath, reportMarkdown, "utf-8");
|
|
624
617
|
|
|
625
|
-
const inputTokens =
|
|
626
|
-
const outputTokens =
|
|
618
|
+
const inputTokens = estimateTokens(prompt, { model: response.model });
|
|
619
|
+
const outputTokens = estimateTokens(aiMarkdown, { model: response.model });
|
|
627
620
|
const modelCost = maybeEstimateModelCost({
|
|
628
621
|
modelId: response.model,
|
|
629
622
|
inputTokens,
|