sentinelayer-cli 0.8.11 → 0.9.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/package.json +10 -5
- package/src/agents/devtestbot/config/definition.js +100 -0
- package/src/agents/devtestbot/config/system-prompt.js +92 -0
- package/src/agents/devtestbot/index.js +9 -0
- package/src/agents/devtestbot/runner.js +769 -0
- package/src/agents/devtestbot/tool.js +707 -0
- package/src/agents/jules/stream.js +2 -12
- package/src/audit/orchestrator.js +471 -114
- package/src/audit/persona-loop.js +1342 -0
- package/src/audit/registry.js +58 -2
- package/src/commands/audit.js +42 -1
- package/src/commands/legacy-args.js +32 -1
- package/src/commands/omargate.js +4 -0
- package/src/commands/session.js +417 -89
- package/src/commands/swarm.js +11 -2
- package/src/cost/history.js +41 -21
- package/src/events/schema.js +27 -1
- package/src/guide/generator.js +14 -0
- package/src/legacy-cli.js +110 -18
- package/src/prompt/generator.js +4 -16
- package/src/review/ai-review.js +95 -6
- package/src/review/dd-report-email-client.js +148 -0
- package/src/review/investor-dd-devtestbot.js +599 -0
- package/src/review/investor-dd-orchestrator.js +135 -3
- package/src/review/omargate-cache.js +285 -0
- package/src/review/omargate-orchestrator.js +605 -4
- package/src/review/persona-prompts.js +34 -1
- package/src/review/report.js +189 -4
- package/src/session/coordination-guidance.js +48 -0
- package/src/session/daemon.js +3 -2
- package/src/session/listener.js +236 -0
- package/src/session/senti-naming.js +36 -0
- package/src/session/setup-guides.js +3 -15
- package/src/session/store.js +54 -5
- package/src/session/sync.js +23 -0
- package/src/spec/generator.js +8 -10
- package/src/swarm/registry.js +20 -0
- package/src/swarm/runtime.js +139 -1
package/src/cost/history.js
CHANGED
|
@@ -7,6 +7,7 @@ import { rollupUsage } from "./tracker.js";
|
|
|
7
7
|
|
|
8
8
|
const HISTORY_VERSION = 1;
|
|
9
9
|
const HISTORY_FILE_NAME = "cost-history.json";
|
|
10
|
+
const costHistoryWriteQueues = new Map();
|
|
10
11
|
|
|
11
12
|
function normalizeNumber(value, field) {
|
|
12
13
|
const normalized = Number(value || 0);
|
|
@@ -76,6 +77,14 @@ export async function resolveCostHistoryPath({
|
|
|
76
77
|
|
|
77
78
|
export async function loadCostHistory(options = {}) {
|
|
78
79
|
const filePath = await resolveCostHistoryPath(options);
|
|
80
|
+
const history = await readCostHistoryFile(filePath);
|
|
81
|
+
return {
|
|
82
|
+
filePath,
|
|
83
|
+
history,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function readCostHistoryFile(filePath) {
|
|
79
88
|
try {
|
|
80
89
|
const raw = await fsp.readFile(filePath, "utf-8");
|
|
81
90
|
const parsed = JSON.parse(raw);
|
|
@@ -83,20 +92,14 @@ export async function loadCostHistory(options = {}) {
|
|
|
83
92
|
throw new Error("Invalid cost history payload.");
|
|
84
93
|
}
|
|
85
94
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
version: Number(parsed.version || HISTORY_VERSION),
|
|
89
|
-
entries: parsed.entries,
|
|
90
|
-
},
|
|
95
|
+
version: Number(parsed.version || HISTORY_VERSION),
|
|
96
|
+
entries: parsed.entries,
|
|
91
97
|
};
|
|
92
98
|
} catch (error) {
|
|
93
99
|
if (error && typeof error === "object" && error.code === "ENOENT") {
|
|
94
100
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
version: HISTORY_VERSION,
|
|
98
|
-
entries: [],
|
|
99
|
-
},
|
|
101
|
+
version: HISTORY_VERSION,
|
|
102
|
+
entries: [],
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
throw error;
|
|
@@ -114,17 +117,34 @@ export async function saveCostHistory({ filePath, history }) {
|
|
|
114
117
|
|
|
115
118
|
export async function appendCostEntry(options = {}, entry = {}) {
|
|
116
119
|
const normalizedEntry = normalizeEntry(entry);
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
filePath,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
const filePath = await resolveCostHistoryPath(options);
|
|
121
|
+
return withCostHistoryWriteQueue(filePath, async () => {
|
|
122
|
+
const history = await readCostHistoryFile(filePath);
|
|
123
|
+
const nextHistory = {
|
|
124
|
+
version: HISTORY_VERSION,
|
|
125
|
+
entries: [...history.entries, normalizedEntry],
|
|
126
|
+
};
|
|
127
|
+
await saveCostHistory({ filePath, history: nextHistory });
|
|
128
|
+
return {
|
|
129
|
+
filePath,
|
|
130
|
+
entry: normalizedEntry,
|
|
131
|
+
history: nextHistory,
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function withCostHistoryWriteQueue(filePath, fn) {
|
|
137
|
+
const previous = costHistoryWriteQueues.get(filePath) || Promise.resolve();
|
|
138
|
+
const next = previous.catch(() => {}).then(fn);
|
|
139
|
+
const queued = next.catch(() => {});
|
|
140
|
+
costHistoryWriteQueues.set(filePath, queued);
|
|
141
|
+
try {
|
|
142
|
+
return await next;
|
|
143
|
+
} finally {
|
|
144
|
+
if (costHistoryWriteQueues.get(filePath) === queued) {
|
|
145
|
+
costHistoryWriteQueues.delete(filePath);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
128
148
|
}
|
|
129
149
|
|
|
130
150
|
function summarizeSessionEntries(entries) {
|
package/src/events/schema.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
const AGENT_EVENT_STREAM = "sl_event";
|
|
2
2
|
const LEGACY_AGENT_ID = "legacy-emitter";
|
|
3
|
+
const AGENT_EVENT_TYPES = Object.freeze([
|
|
4
|
+
"agent_start",
|
|
5
|
+
"agent_complete",
|
|
6
|
+
"agent_abort",
|
|
7
|
+
"agent_error",
|
|
8
|
+
"progress",
|
|
9
|
+
"heartbeat",
|
|
10
|
+
"tool_call",
|
|
11
|
+
"tool_result",
|
|
12
|
+
"finding",
|
|
13
|
+
"reasoning",
|
|
14
|
+
"budget_warning",
|
|
15
|
+
"budget_stop",
|
|
16
|
+
"swarm_start",
|
|
17
|
+
"swarm_complete",
|
|
18
|
+
"phase_start",
|
|
19
|
+
"phase_complete",
|
|
20
|
+
"orchestrator_start",
|
|
21
|
+
"dispatch",
|
|
22
|
+
"reconcile_start",
|
|
23
|
+
"reconcile_complete",
|
|
24
|
+
"orchestrator_complete",
|
|
25
|
+
"convergence_expansion",
|
|
26
|
+
"coverage_gap",
|
|
27
|
+
"llm_error",
|
|
28
|
+
]);
|
|
3
29
|
|
|
4
30
|
function isPlainObject(value) {
|
|
5
31
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -187,4 +213,4 @@ export function validateAgentEvent(evt, options = {}) {
|
|
|
187
213
|
return Boolean(normalizeAgentEvent(evt, options));
|
|
188
214
|
}
|
|
189
215
|
|
|
190
|
-
export { AGENT_EVENT_STREAM };
|
|
216
|
+
export { AGENT_EVENT_STREAM, AGENT_EVENT_TYPES };
|
package/src/guide/generator.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCoordinationEtiquetteItems,
|
|
3
|
+
renderCoordinationMarkdownSection,
|
|
4
|
+
renderCoordinationTicketBlock,
|
|
5
|
+
} from "../session/coordination-guidance.js";
|
|
6
|
+
|
|
1
7
|
export const SUPPORTED_GUIDE_EXPORT_FORMATS = Object.freeze([
|
|
2
8
|
"jira",
|
|
3
9
|
"linear",
|
|
@@ -167,6 +173,8 @@ function buildTicket(phase, index) {
|
|
|
167
173
|
"",
|
|
168
174
|
"Acceptance criteria:",
|
|
169
175
|
acceptanceBlock || "1. Phase outcomes are verified by deterministic checks.",
|
|
176
|
+
"",
|
|
177
|
+
renderCoordinationTicketBlock(),
|
|
170
178
|
].join("\n"),
|
|
171
179
|
};
|
|
172
180
|
}
|
|
@@ -235,6 +243,8 @@ ${goal}
|
|
|
235
243
|
## Phase Execution Plan
|
|
236
244
|
${phaseMarkdown}
|
|
237
245
|
|
|
246
|
+
${renderCoordinationMarkdownSection()}
|
|
247
|
+
|
|
238
248
|
## Suggested PR Sequence
|
|
239
249
|
${resolvedPhases
|
|
240
250
|
.map((phase, index) => `${index + 1}. ${phase.title} (${phase.effort.label})`)
|
|
@@ -246,6 +256,7 @@ ${resolvedPhases
|
|
|
246
256
|
goal,
|
|
247
257
|
phases: resolvedPhases,
|
|
248
258
|
tickets,
|
|
259
|
+
coordinationRules: getCoordinationEtiquetteItems(),
|
|
249
260
|
markdown,
|
|
250
261
|
};
|
|
251
262
|
}
|
|
@@ -256,12 +267,14 @@ export function renderGuideExport({ format, guide }) {
|
|
|
256
267
|
project: guide.projectName,
|
|
257
268
|
generated_at: new Date().toISOString(),
|
|
258
269
|
issues: guide.tickets,
|
|
270
|
+
coordination_rules: Array.isArray(guide.coordinationRules) ? guide.coordinationRules : [],
|
|
259
271
|
};
|
|
260
272
|
|
|
261
273
|
if (normalized === "jira") {
|
|
262
274
|
return JSON.stringify(
|
|
263
275
|
{
|
|
264
276
|
format: "jira",
|
|
277
|
+
coordination_rules: payload.coordination_rules,
|
|
265
278
|
issues: payload.issues.map((issue) => ({
|
|
266
279
|
summary: issue.title,
|
|
267
280
|
description: issue.description,
|
|
@@ -279,6 +292,7 @@ export function renderGuideExport({ format, guide }) {
|
|
|
279
292
|
return JSON.stringify(
|
|
280
293
|
{
|
|
281
294
|
format: "linear",
|
|
295
|
+
coordination_rules: payload.coordination_rules,
|
|
282
296
|
issues: payload.issues.map((issue, index) => ({
|
|
283
297
|
title: issue.title,
|
|
284
298
|
description: issue.description,
|
package/src/legacy-cli.js
CHANGED
|
@@ -25,6 +25,10 @@ import { normalizeAgentEvent } from "./events/schema.js";
|
|
|
25
25
|
import { collectCodebaseIngest, formatIngestSummary } from "./ingest/engine.js";
|
|
26
26
|
import { getExpressTemplate, getPackageJsonTemplate, buildReadmeContent } from "./scaffold/templates.js";
|
|
27
27
|
import { generateScaffold } from "./scaffold/generator.js";
|
|
28
|
+
import {
|
|
29
|
+
getCoordinationEtiquetteItems,
|
|
30
|
+
renderCoordinationNumberedList,
|
|
31
|
+
} from "./session/coordination-guidance.js";
|
|
28
32
|
|
|
29
33
|
let DEFAULT_API_URL = process.env.SENTINELAYER_API_URL || "https://api.sentinelayer.com";
|
|
30
34
|
let DEFAULT_WEB_URL = process.env.SENTINELAYER_WEB_URL || "https://sentinelayer.com";
|
|
@@ -1121,6 +1125,10 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1121
1125
|
const maxParallel =
|
|
1122
1126
|
parseInt(getCommandOptionValue(args, "--max-parallel") || "3", 10) || 3;
|
|
1123
1127
|
const streamEnabled = hasCommandOption(args, "--stream");
|
|
1128
|
+
const devTestBotEnabled = !hasCommandOption(args, "--no-devtestbot");
|
|
1129
|
+
const devTestBotBaseUrl = getCommandOptionValue(args, "--devtestbot-base-url") || "";
|
|
1130
|
+
const devTestBotScope = getCommandOptionValue(args, "--devtestbot-scope") || "";
|
|
1131
|
+
const emailOnComplete = getCommandOptionValue(args, "--email-on-complete") || "";
|
|
1124
1132
|
|
|
1125
1133
|
const targetPath = path.resolve(process.cwd(), pathArg);
|
|
1126
1134
|
if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
|
|
@@ -1128,6 +1136,9 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1128
1136
|
}
|
|
1129
1137
|
|
|
1130
1138
|
const { runInvestorDd } = await import("./review/investor-dd-orchestrator.js");
|
|
1139
|
+
const reportEmailClient = emailOnComplete
|
|
1140
|
+
? await import("./review/dd-report-email-client.js")
|
|
1141
|
+
: null;
|
|
1131
1142
|
if (!asJson) {
|
|
1132
1143
|
printSection("Investor-DD Audit");
|
|
1133
1144
|
printInfo(`Target: ${targetPath}`);
|
|
@@ -1141,6 +1152,24 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1141
1152
|
outputDir: outputDirArg,
|
|
1142
1153
|
budgetOptions: { maxCostUsd, maxRuntimeMinutes, maxParallel },
|
|
1143
1154
|
dryRun,
|
|
1155
|
+
devTestBot: {
|
|
1156
|
+
enabled: devTestBotEnabled,
|
|
1157
|
+
baseUrl: devTestBotBaseUrl,
|
|
1158
|
+
scope: devTestBotScope,
|
|
1159
|
+
},
|
|
1160
|
+
reportEmail: emailOnComplete
|
|
1161
|
+
? {
|
|
1162
|
+
to: emailOnComplete,
|
|
1163
|
+
client: {
|
|
1164
|
+
send: ({ runId, to }) => reportEmailClient.sendDdReportEmail({
|
|
1165
|
+
runId,
|
|
1166
|
+
to,
|
|
1167
|
+
cwd: targetPath,
|
|
1168
|
+
env: process.env,
|
|
1169
|
+
}),
|
|
1170
|
+
},
|
|
1171
|
+
}
|
|
1172
|
+
: null,
|
|
1144
1173
|
onEvent: streamEnabled
|
|
1145
1174
|
? (event) => process.stdout.write(`${JSON.stringify(event)}\n`)
|
|
1146
1175
|
: () => {},
|
|
@@ -1237,7 +1266,10 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1237
1266
|
deterministic: {
|
|
1238
1267
|
summary: detSummary,
|
|
1239
1268
|
findings: detFindings,
|
|
1269
|
+
scope: deterministic.scope || {},
|
|
1270
|
+
layers: deterministic.layers || {},
|
|
1240
1271
|
metadata: deterministic.metadata || {},
|
|
1272
|
+
artifacts: deterministic.artifacts || {},
|
|
1241
1273
|
},
|
|
1242
1274
|
onEvent: streamHandler,
|
|
1243
1275
|
includeOnly: includeOnly.length > 0 ? includeOnly : null,
|
|
@@ -1331,6 +1363,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1331
1363
|
const combinedP1 = combinedSummary.P1 || 0;
|
|
1332
1364
|
const combinedP2 = combinedSummary.P2 || 0;
|
|
1333
1365
|
const combinedP3 = combinedSummary.P3 || 0;
|
|
1366
|
+
const omargateRunId = orchestratorResult?.runId || deterministic.runId;
|
|
1334
1367
|
|
|
1335
1368
|
// Write per-phase artifacts alongside REVIEW_DETERMINISTIC so post-mortems
|
|
1336
1369
|
// can inspect exactly what each layer contributed.
|
|
@@ -1376,6 +1409,7 @@ async function runLocalOmarGateCommand(args) {
|
|
|
1376
1409
|
const report = `# Local Omar Gate Deep Scan
|
|
1377
1410
|
|
|
1378
1411
|
Generated: ${nowIso()}
|
|
1412
|
+
Run ID: ${omargateRunId}
|
|
1379
1413
|
Target: ${targetPath}
|
|
1380
1414
|
Elapsed: ${totalElapsed}
|
|
1381
1415
|
|
|
@@ -1395,12 +1429,24 @@ ${formatFindingsMarkdown(allFindings)}
|
|
|
1395
1429
|
const reportPath = await writeLocalCommandReport(targetPath, "omargate-deep", report, {
|
|
1396
1430
|
outputDir: outputDirArg,
|
|
1397
1431
|
});
|
|
1432
|
+
const { writeOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
|
|
1433
|
+
const deterministicCache = await writeOmarGateDeterministicCache({
|
|
1434
|
+
targetPath,
|
|
1435
|
+
outputDir: outputDirArg,
|
|
1436
|
+
runId: omargateRunId,
|
|
1437
|
+
deterministic,
|
|
1438
|
+
reportPath,
|
|
1439
|
+
});
|
|
1440
|
+
artifactPaths.deterministicCache = deterministicCache.artifactPath;
|
|
1441
|
+
artifactPaths.latestOmarGate = deterministicCache.latestPath;
|
|
1442
|
+
|
|
1398
1443
|
if (asJson) {
|
|
1399
1444
|
console.log(
|
|
1400
1445
|
JSON.stringify(
|
|
1401
1446
|
{
|
|
1402
1447
|
command: "/omargate deep",
|
|
1403
1448
|
targetPath,
|
|
1449
|
+
runId: omargateRunId,
|
|
1404
1450
|
reportPath,
|
|
1405
1451
|
scannedFiles,
|
|
1406
1452
|
p0: combinedP0,
|
|
@@ -1457,6 +1503,7 @@ async function runLocalAuditCommand(args) {
|
|
|
1457
1503
|
const asJson = hasCommandOption(args, "--json");
|
|
1458
1504
|
const pathArg = getCommandOptionValue(args, "--path") || ".";
|
|
1459
1505
|
const outputDirArg = getCommandOptionValue(args, "--output-dir") || "";
|
|
1506
|
+
const reuseOmarGate = getCommandOptionValue(args, "--reuse-omargate") || "";
|
|
1460
1507
|
const targetPath = path.resolve(process.cwd(), pathArg);
|
|
1461
1508
|
if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
|
|
1462
1509
|
throw new Error(`Invalid --path target: ${targetPath}`);
|
|
@@ -1467,6 +1514,16 @@ async function runLocalAuditCommand(args) {
|
|
|
1467
1514
|
printInfo(`Target: ${targetPath}`);
|
|
1468
1515
|
}
|
|
1469
1516
|
|
|
1517
|
+
const buildScanFromOmarGateCache = (cache) => {
|
|
1518
|
+
const findings = Array.isArray(cache?.findings) ? cache.findings : [];
|
|
1519
|
+
return {
|
|
1520
|
+
scannedFiles: Number(cache?.scope?.scannedFiles || findings.length || 0),
|
|
1521
|
+
findings,
|
|
1522
|
+
p1: findings.filter((item) => item.severity === "P1").length,
|
|
1523
|
+
p2: findings.filter((item) => item.severity === "P2").length,
|
|
1524
|
+
};
|
|
1525
|
+
};
|
|
1526
|
+
|
|
1470
1527
|
const requiredChecks = [
|
|
1471
1528
|
{
|
|
1472
1529
|
key: ".github/workflows/omar-gate.yml",
|
|
@@ -1488,7 +1545,44 @@ async function runLocalAuditCommand(args) {
|
|
|
1488
1545
|
},
|
|
1489
1546
|
];
|
|
1490
1547
|
|
|
1491
|
-
|
|
1548
|
+
let omargateReuse = {
|
|
1549
|
+
requested: reuseOmarGate || "",
|
|
1550
|
+
used: false,
|
|
1551
|
+
runId: "",
|
|
1552
|
+
artifactPath: "",
|
|
1553
|
+
reason: reuseOmarGate ? "not_found" : "not_requested",
|
|
1554
|
+
};
|
|
1555
|
+
let scan = null;
|
|
1556
|
+
if (reuseOmarGate) {
|
|
1557
|
+
const { loadOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
|
|
1558
|
+
const reused = await loadOmarGateDeterministicCache({
|
|
1559
|
+
targetPath,
|
|
1560
|
+
outputDir: outputDirArg,
|
|
1561
|
+
runIdOrLatest: reuseOmarGate,
|
|
1562
|
+
});
|
|
1563
|
+
if (reused.found) {
|
|
1564
|
+
scan = buildScanFromOmarGateCache(reused.cache);
|
|
1565
|
+
omargateReuse = {
|
|
1566
|
+
requested: reuseOmarGate,
|
|
1567
|
+
used: true,
|
|
1568
|
+
runId: reused.runId,
|
|
1569
|
+
deterministicRunId: reused.cache?.deterministicRunId || "",
|
|
1570
|
+
artifactPath: reused.artifactPath,
|
|
1571
|
+
reason: "",
|
|
1572
|
+
};
|
|
1573
|
+
} else {
|
|
1574
|
+
omargateReuse = {
|
|
1575
|
+
requested: reuseOmarGate,
|
|
1576
|
+
used: false,
|
|
1577
|
+
runId: "",
|
|
1578
|
+
artifactPath: "",
|
|
1579
|
+
reason: reused.reason || "not_found",
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
if (!scan) {
|
|
1584
|
+
scan = await runCredentialScan(targetPath);
|
|
1585
|
+
}
|
|
1492
1586
|
const failedP1Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P1").length;
|
|
1493
1587
|
const failedP2Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P2").length;
|
|
1494
1588
|
const totalP1 = scan.p1 + failedP1Checks;
|
|
@@ -1506,11 +1600,13 @@ async function runLocalAuditCommand(args) {
|
|
|
1506
1600
|
Generated: ${nowIso()}
|
|
1507
1601
|
Target: ${targetPath}
|
|
1508
1602
|
Overall status: ${overallStatus}
|
|
1603
|
+
OmarGate reuse: ${omargateReuse.used ? `yes (${omargateReuse.runId})` : omargateReuse.requested ? `requested ${omargateReuse.requested} (${omargateReuse.reason})` : "no"}
|
|
1509
1604
|
|
|
1510
1605
|
Readiness checks:
|
|
1511
1606
|
${checkText}
|
|
1512
1607
|
|
|
1513
1608
|
Scan summary:
|
|
1609
|
+
- Reused OmarGate run: ${omargateReuse.used ? omargateReuse.runId : "n/a"}
|
|
1514
1610
|
- Files scanned: ${scan.scannedFiles}
|
|
1515
1611
|
- P1 findings: ${scan.p1}
|
|
1516
1612
|
- P2 findings: ${scan.p2}
|
|
@@ -1536,6 +1632,9 @@ ${formatFindingsMarkdown(scan.findings)}
|
|
|
1536
1632
|
p1Total: totalP1,
|
|
1537
1633
|
p2Total: totalP2,
|
|
1538
1634
|
blocking: totalP1 > 0,
|
|
1635
|
+
omargateReuse,
|
|
1636
|
+
reusedOmarGateRunId: omargateReuse.used ? omargateReuse.runId : "",
|
|
1637
|
+
reusedOmarGateDeterministicPath: omargateReuse.used ? omargateReuse.artifactPath : "",
|
|
1539
1638
|
},
|
|
1540
1639
|
null,
|
|
1541
1640
|
2
|
|
@@ -1543,6 +1642,11 @@ ${formatFindingsMarkdown(scan.findings)}
|
|
|
1543
1642
|
);
|
|
1544
1643
|
} else {
|
|
1545
1644
|
console.log(pc.cyan(`Report: ${reportPath}`));
|
|
1645
|
+
if (omargateReuse.used) {
|
|
1646
|
+
console.log(pc.gray(`Reused OmarGate run: ${omargateReuse.runId}`));
|
|
1647
|
+
} else if (omargateReuse.requested) {
|
|
1648
|
+
console.log(pc.gray(`OmarGate reuse unavailable: ${omargateReuse.reason}`));
|
|
1649
|
+
}
|
|
1546
1650
|
console.log(`Overall status: ${overallStatus}`);
|
|
1547
1651
|
console.log(`P1 total: ${totalP1}`);
|
|
1548
1652
|
console.log(`P2 total: ${totalP2}`);
|
|
@@ -1875,6 +1979,7 @@ function buildCodingAgentConfigTemplate({ agentProfile, projectName }) {
|
|
|
1875
1979
|
"Read docs/spec.md, docs/build-guide.md, tasks/todo.md, and AGENT_HANDOFF_PROMPT.md in order.",
|
|
1876
1980
|
"Work one PR scope at a time and keep changes deterministic.",
|
|
1877
1981
|
"Run local checks before push: /omargate deep and /audit.",
|
|
1982
|
+
...getCoordinationEtiquetteItems(),
|
|
1878
1983
|
];
|
|
1879
1984
|
|
|
1880
1985
|
if (agentProfile.id === "aider") {
|
|
@@ -1885,7 +1990,7 @@ read:
|
|
|
1885
1990
|
- tasks/todo.md
|
|
1886
1991
|
- AGENT_HANDOFF_PROMPT.md
|
|
1887
1992
|
notes:
|
|
1888
|
-
- ${commonChecklist.join("\n - ")}
|
|
1993
|
+
- ${commonChecklist.map((item) => JSON.stringify(item)).join("\n - ")}
|
|
1889
1994
|
`;
|
|
1890
1995
|
}
|
|
1891
1996
|
|
|
@@ -2111,16 +2216,7 @@ Repo context:
|
|
|
2111
2216
|
|
|
2112
2217
|
## Multi-Agent Coordination (if session active)
|
|
2113
2218
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
1. **On start:** Run \`sl session join <id> --name <your-name> --role coder\`
|
|
2117
|
-
2. **Periodically:** Run \`sl session say <id> "status: working on <file>"\` so others know what you're doing
|
|
2118
|
-
3. **On unexpected file change:** Instead of stopping, run:
|
|
2119
|
-
\`sl session say <id> "help: unexpected change in <file> - who modified this?"\`
|
|
2120
|
-
Then check \`sl session read <id> --tail 5\` for the answer and continue.
|
|
2121
|
-
4. **On findings:** Run \`sl session say <id> "finding: [P2] <title> in <file>:<line>"\`
|
|
2122
|
-
5. **On completion:** Run \`sl session leave <id>\`
|
|
2123
|
-
6. **Before modifying a file:** Check \`sl session read <id> --tail 10\` for recent activity on that file
|
|
2219
|
+
${renderCoordinationNumberedList()}
|
|
2124
2220
|
|
|
2125
2221
|
Start now and continue autonomously.
|
|
2126
2222
|
`;
|
|
@@ -2129,12 +2225,8 @@ Start now and continue autonomously.
|
|
|
2129
2225
|
export function buildAgentsSessionGuideContent() {
|
|
2130
2226
|
return `# SentinelLayer Session Guide for AI Agents
|
|
2131
2227
|
|
|
2132
|
-
##
|
|
2133
|
-
|
|
2134
|
-
2. Join: \`sl session join <id> --name <your-short-name> --role <coder|reviewer|tester>\`
|
|
2135
|
-
3. Read context: \`sl session read <id> --tail 20\` — see what others are doing
|
|
2136
|
-
4. Work: emit status every 5 min, post findings, ask for help instead of stopping
|
|
2137
|
-
5. Leave: \`sl session leave <id>\` when done
|
|
2228
|
+
## Required Etiquette
|
|
2229
|
+
${renderCoordinationNumberedList()}
|
|
2138
2230
|
|
|
2139
2231
|
## Why This Matters
|
|
2140
2232
|
- Other agents can see what you're working on and avoid file conflicts
|
package/src/prompt/generator.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getCoordinationEtiquetteItems } from "../session/coordination-guidance.js";
|
|
2
|
+
|
|
1
3
|
export const SUPPORTED_PROMPT_TARGETS = Object.freeze([
|
|
2
4
|
"claude",
|
|
3
5
|
"cursor",
|
|
@@ -34,11 +36,7 @@ const TARGET_GUIDANCE = Object.freeze({
|
|
|
34
36
|
],
|
|
35
37
|
});
|
|
36
38
|
|
|
37
|
-
const SESSION_COORDINATION_GUIDANCE = Object.freeze(
|
|
38
|
-
"Multi-agent coordination: use `sl session` commands to communicate with other agents.",
|
|
39
|
-
"Always update the session chat room with your current activity so joining agents have context.",
|
|
40
|
-
"Never break your autonomous loop on unexpected file changes; ask in the session first.",
|
|
41
|
-
]);
|
|
39
|
+
const SESSION_COORDINATION_GUIDANCE = Object.freeze(getCoordinationEtiquetteItems());
|
|
42
40
|
|
|
43
41
|
function normalizeTarget(target) {
|
|
44
42
|
const normalized = String(target || "generic").trim().toLowerCase();
|
|
@@ -61,14 +59,6 @@ function buildAgentHeader(target) {
|
|
|
61
59
|
return headers[target] || headers.generic;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
function shouldAppendSessionGuidance(specMarkdown) {
|
|
65
|
-
const normalized = String(specMarkdown || "").toLowerCase();
|
|
66
|
-
if (!normalized) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
return normalized.includes("coordination protocol") || normalized.includes("session");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
62
|
export function resolvePromptTarget(target) {
|
|
73
63
|
return normalizeTarget(target);
|
|
74
64
|
}
|
|
@@ -96,9 +86,7 @@ export function generateExecutionPrompt({
|
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
const operatingRules = [...guidance];
|
|
99
|
-
|
|
100
|
-
operatingRules.push(...SESSION_COORDINATION_GUIDANCE);
|
|
101
|
-
}
|
|
89
|
+
operatingRules.push(...SESSION_COORDINATION_GUIDANCE);
|
|
102
90
|
const guidanceMarkdown = operatingRules.map((item, index) => `${index + 1}. ${item}`).join("\n");
|
|
103
91
|
|
|
104
92
|
const hasAidenId = specText.toLowerCase().includes("aidenid");
|