sentinelayer-cli 0.1.1 → 0.3.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 +996 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +62 -54
- package/src/agents/jules/config/definition.js +209 -209
- package/src/agents/jules/config/system-prompt.js +175 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +377 -377
- package/src/agents/jules/loop.js +367 -367
- package/src/agents/jules/pulse.js +319 -319
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +308 -308
- package/src/agents/jules/tools/auth-audit.js +226 -222
- package/src/agents/jules/tools/dispatch.js +327 -327
- package/src/agents/jules/tools/file-edit.js +180 -180
- package/src/agents/jules/tools/file-read.js +100 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +168 -168
- package/src/agents/jules/tools/grep.js +228 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +161 -161
- package/src/agents/jules/tools/runtime-audit.js +493 -493
- package/src/agents/jules/tools/shell.js +383 -383
- package/src/ai/aidenid.js +972 -945
- package/src/ai/client.js +508 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/http.js +113 -113
- package/src/auth/service.js +891 -848
- package/src/auth/session-store.js +359 -345
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1337
- package/src/commands/ai/provision-governance.js +1272 -1246
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1147
- package/src/commands/auth.js +375 -366
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -404
- package/src/commands/omargate.js +15 -15
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +866 -788
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +510 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/ingest-refresh.js +195 -0
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/interactive/action-menu.js +132 -0
- package/src/interactive/auto-ingest.js +111 -0
- package/src/interactive/index.js +95 -0
- package/src/interactive/workspace.js +92 -0
- package/src/legacy-cli.js +2548 -2435
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -106
- package/src/review/ai-review.js +669 -669
- package/src/review/local-review.js +1284 -1284
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -0
- package/src/scaffold/templates.js +150 -0
- package/src/scan/generator.js +418 -351
- package/src/scan/gh-secrets.js +107 -0
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +118 -0
- package/src/telemetry/sync.js +190 -0
- package/src/ui/markdown.js +220 -220
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { collectCodebaseIngest, generateCodebaseIngest } from "../ingest/engine.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pulse Ingest Refresh — periodic codebase re-index.
|
|
7
|
+
*
|
|
8
|
+
* Detects when the file count changes vs the last ingest.
|
|
9
|
+
* If delta detected: re-runs ingest + AST + domain assignment.
|
|
10
|
+
* Budget-gated: aborts if refresh takes > maxDurationMs.
|
|
11
|
+
*
|
|
12
|
+
* Inspired by src/utils/cronScheduler.ts file watcher pattern.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const DEFAULT_CHECK_INTERVAL_MS = 60_000; // 1 minute
|
|
16
|
+
const DEFAULT_MAX_DURATION_MS = 30_000; // 30 seconds
|
|
17
|
+
const STALE_FILE_COUNT_THRESHOLD = 5; // re-ingest if >= 5 files changed
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if the codebase has changed since last ingest.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} targetPath - Repository root
|
|
23
|
+
* @returns {{ changed: boolean, currentFileCount: number, lastFileCount: number, delta: number }}
|
|
24
|
+
*/
|
|
25
|
+
export function detectIngestDrift(targetPath) {
|
|
26
|
+
const ingestPath = path.join(targetPath, ".sentinelayer", "CODEBASE_INGEST.json");
|
|
27
|
+
|
|
28
|
+
let lastFileCount = 0;
|
|
29
|
+
try {
|
|
30
|
+
const ingest = JSON.parse(fs.readFileSync(ingestPath, "utf-8"));
|
|
31
|
+
lastFileCount = ingest?.summary?.filesScanned || 0;
|
|
32
|
+
} catch {
|
|
33
|
+
return { changed: true, currentFileCount: 0, lastFileCount: 0, delta: 0 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Quick file count via directory scan (fast, no deep analysis)
|
|
37
|
+
let currentFileCount = 0;
|
|
38
|
+
try {
|
|
39
|
+
currentFileCount = countFilesQuick(targetPath);
|
|
40
|
+
} catch {
|
|
41
|
+
return { changed: false, currentFileCount: 0, lastFileCount, delta: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const delta = Math.abs(currentFileCount - lastFileCount);
|
|
45
|
+
return {
|
|
46
|
+
changed: delta >= STALE_FILE_COUNT_THRESHOLD,
|
|
47
|
+
currentFileCount,
|
|
48
|
+
lastFileCount,
|
|
49
|
+
delta,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Run a budget-gated ingest refresh.
|
|
55
|
+
* Aborts if refresh takes longer than maxDurationMs.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} targetPath
|
|
58
|
+
* @param {object} [options]
|
|
59
|
+
* @param {number} [options.maxDurationMs] - Max refresh time (default 30s)
|
|
60
|
+
* @param {function} [options.onEvent] - Event callback
|
|
61
|
+
* @returns {Promise<{ refreshed: boolean, reason: string, durationMs: number }>}
|
|
62
|
+
*/
|
|
63
|
+
export async function refreshIngestIfNeeded(targetPath, options = {}) {
|
|
64
|
+
const maxDurationMs = options.maxDurationMs || DEFAULT_MAX_DURATION_MS;
|
|
65
|
+
const drift = detectIngestDrift(targetPath);
|
|
66
|
+
|
|
67
|
+
if (!drift.changed) {
|
|
68
|
+
return { refreshed: false, reason: "no_drift", durationMs: 0, ...drift };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.onEvent) {
|
|
72
|
+
options.onEvent({
|
|
73
|
+
stream: "sl_event",
|
|
74
|
+
event: "ingest_refresh_start",
|
|
75
|
+
payload: { delta: drift.delta, currentFiles: drift.currentFileCount, lastFiles: drift.lastFileCount },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const startMs = Date.now();
|
|
80
|
+
|
|
81
|
+
// Budget gate: abort if taking too long
|
|
82
|
+
const timeout = setTimeout(() => {
|
|
83
|
+
if (options.onEvent) {
|
|
84
|
+
options.onEvent({
|
|
85
|
+
stream: "sl_event",
|
|
86
|
+
event: "ingest_refresh_timeout",
|
|
87
|
+
payload: { maxDurationMs, elapsed: Date.now() - startMs },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}, maxDurationMs);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
let ingest;
|
|
94
|
+
try {
|
|
95
|
+
ingest = await generateCodebaseIngest({
|
|
96
|
+
rootPath: targetPath,
|
|
97
|
+
outputDir: path.join(targetPath, ".sentinelayer"),
|
|
98
|
+
refresh: true,
|
|
99
|
+
});
|
|
100
|
+
} catch {
|
|
101
|
+
ingest = await collectCodebaseIngest({ rootPath: targetPath });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const durationMs = Date.now() - startMs;
|
|
105
|
+
|
|
106
|
+
if (options.onEvent) {
|
|
107
|
+
options.onEvent({
|
|
108
|
+
stream: "sl_event",
|
|
109
|
+
event: "ingest_refresh_complete",
|
|
110
|
+
payload: {
|
|
111
|
+
durationMs,
|
|
112
|
+
filesScanned: ingest?.summary?.filesScanned || 0,
|
|
113
|
+
delta: drift.delta,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
refreshed: true,
|
|
120
|
+
reason: "drift_detected",
|
|
121
|
+
durationMs,
|
|
122
|
+
filesScanned: ingest?.summary?.filesScanned || 0,
|
|
123
|
+
...drift,
|
|
124
|
+
};
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return {
|
|
127
|
+
refreshed: false,
|
|
128
|
+
reason: "refresh_failed: " + err.message,
|
|
129
|
+
durationMs: Date.now() - startMs,
|
|
130
|
+
...drift,
|
|
131
|
+
};
|
|
132
|
+
} finally {
|
|
133
|
+
clearTimeout(timeout);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Start a periodic ingest refresh watcher.
|
|
139
|
+
* Checks for drift every checkIntervalMs and refreshes if needed.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} targetPath
|
|
142
|
+
* @param {object} [options]
|
|
143
|
+
* @param {number} [options.checkIntervalMs] - Check interval (default 60s)
|
|
144
|
+
* @param {number} [options.maxDurationMs] - Max refresh time (default 30s)
|
|
145
|
+
* @param {function} [options.onEvent] - Event callback
|
|
146
|
+
* @returns {{ stop: function }} - Call stop() to stop watching
|
|
147
|
+
*/
|
|
148
|
+
export function startPeriodicRefresh(targetPath, options = {}) {
|
|
149
|
+
const checkIntervalMs = options.checkIntervalMs || DEFAULT_CHECK_INTERVAL_MS;
|
|
150
|
+
let running = true;
|
|
151
|
+
|
|
152
|
+
const check = async () => {
|
|
153
|
+
if (!running) return;
|
|
154
|
+
await refreshIngestIfNeeded(targetPath, options);
|
|
155
|
+
if (running) {
|
|
156
|
+
setTimeout(check, checkIntervalMs);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Start first check after one interval
|
|
161
|
+
const timer = setTimeout(check, checkIntervalMs);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
stop() {
|
|
165
|
+
running = false;
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Quick file count — counts source files without deep analysis.
|
|
173
|
+
* Much faster than full ingest for drift detection.
|
|
174
|
+
*/
|
|
175
|
+
function countFilesQuick(rootPath) {
|
|
176
|
+
const IGNORE = new Set([
|
|
177
|
+
".git", "node_modules", ".next", "dist", "build", "coverage",
|
|
178
|
+
".turbo", ".venv", "__pycache__", ".cache", ".sentinelayer",
|
|
179
|
+
]);
|
|
180
|
+
let count = 0;
|
|
181
|
+
|
|
182
|
+
function walk(dir) {
|
|
183
|
+
try {
|
|
184
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
185
|
+
for (const entry of entries) {
|
|
186
|
+
if (IGNORE.has(entry.name)) continue;
|
|
187
|
+
if (entry.isDirectory()) walk(path.join(dir, entry.name));
|
|
188
|
+
else if (entry.isFile()) count++;
|
|
189
|
+
}
|
|
190
|
+
} catch { /* skip unreadable */ }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
walk(rootPath);
|
|
194
|
+
return count;
|
|
195
|
+
}
|