@shahmilsaari/memory-core 1.0.32 → 1.0.35
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 +8 -6
- package/dist/{approval-queue-YBYRGBHP.js → approval-queue-4RK46FNE.js} +2 -1
- package/dist/{ast-analyzer-JM4CIOFY.js → ast-analyzer-WGTJKGAP.js} +1 -0
- package/dist/{check-cache-6NWRTZJD.js → check-cache-ZVFHTA2C.js} +1 -0
- package/dist/{check-logger-5HYSWA3S.js → check-logger-F4DW4LBY.js} +1 -0
- package/dist/chunk-2LNDQGDD.js +37 -0
- package/dist/{chunk-23GUWJ6F.js → chunk-4QWIYO2G.js} +3071 -3271
- package/dist/{chunk-GIPKVQSA.js → chunk-5T2QBBYT.js} +3 -29
- package/dist/chunk-FPRSYCOZ.js +77 -0
- package/dist/chunk-HJCPBM7B.js +238 -0
- package/dist/chunk-K3NQKI34.js +10 -0
- package/dist/chunk-SKR44CRD.js +35 -0
- package/dist/{chunk-W6WEAV3S.js → chunk-UMYJQWIE.js} +13 -11
- package/dist/{chunk-PQBWHAZN.js → chunk-XAE3RY5D.js} +5 -2
- package/dist/{classifier-MZ65R7FK.js → classifier-D5K3X7FQ.js} +1 -0
- package/dist/cli.js +244 -80
- package/dist/{confidence-gate-ZQDAOS6P.js → confidence-gate-AT2MW4NK.js} +8 -6
- package/dist/dashboard/assets/index-BcDOsQwz.css +1 -0
- package/dist/dashboard/assets/index-DGx4sRHq.js +2 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/{dashboard-server-MD6NVL2F.js → dashboard-server-UBZHOPRC.js} +274 -21
- package/dist/{db-FLFZZXG3.js → db-RE23O4DV.js} +3 -1
- package/dist/{deterministic-validator-PP56B46I.js → deterministic-validator-3LB234OG.js} +1 -0
- package/dist/draft-rule-NUXOSRRS.js +11 -0
- package/dist/embedding-VIMV6O2P.js +9 -0
- package/dist/{evidence-HVMSONTT.js → evidence-WFMGWSOP.js} +1 -0
- package/dist/{graph-TFNTB5OK.js → graph-UJJAKNHD.js} +1 -0
- package/dist/{incident-capture-RVPZULS7.js → incident-capture-5YUVJJM3.js} +11 -0
- package/dist/mcp-server-3N7R46BS.js +14958 -0
- package/dist/memory-selection-A5CQ6ZT5.js +29 -0
- package/dist/{deepseek-critique-MALVIYGF.js → model-critique-E2GNZ42U.js} +18 -32
- package/dist/{ollama-judge-D2LFK5PB.js → ollama-judge-6ZEZN4JV.js} +29 -41
- package/dist/{rate-limiter-SLIPCXRF.js → rate-limiter-KIMXTAAU.js} +1 -0
- package/dist/{rules-V3QMN3AR.js → rules-UUVIKEDM.js} +1 -0
- package/dist/{watch-errors-B3FA26N4.js → watch-errors-DZMW3CFN.js} +28 -33
- package/package.json +2 -1
- package/templates/AGENTS.md.hbs +11 -27
- package/templates/AI_RULES.md.hbs +9 -23
- package/templates/ARCHITECTURE.md.hbs +9 -30
- package/templates/CLAUDE.md.hbs +10 -35
- package/templates/DEVIN.md.hbs +7 -16
- package/templates/PROJECT_MEMORY.md.hbs +5 -21
- package/templates/amazonq-guidelines.md.hbs +7 -16
- package/templates/clinerules.hbs +8 -17
- package/templates/copilot-instructions.md.hbs +7 -16
- package/templates/cursor-rule.mdc.hbs +4 -9
- package/templates/cursorrules.hbs +8 -14
- package/templates/gemini-styleguide.md.hbs +7 -16
- package/templates/jetbrains-ai.md.hbs +6 -15
- package/templates/roo-rule.md.hbs +6 -15
- package/templates/windsurfrules.hbs +9 -18
- package/dist/dashboard/assets/index-BFwqVRYO.js +0 -2
- package/dist/dashboard/assets/index-y7eHWJtq.css +0 -1
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildArchRules,
|
|
4
|
-
buildLayersTemplate
|
|
4
|
+
buildLayersTemplate
|
|
5
|
+
} from "./chunk-HJCPBM7B.js";
|
|
6
|
+
import {
|
|
5
7
|
detectProject,
|
|
6
|
-
embed,
|
|
7
8
|
inferProjectArchitectures,
|
|
8
9
|
startWatch
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4QWIYO2G.js";
|
|
11
|
+
import "./chunk-ZZBQEXEO.js";
|
|
10
12
|
import {
|
|
11
|
-
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
embed
|
|
14
|
+
} from "./chunk-SKR44CRD.js";
|
|
13
15
|
import {
|
|
14
|
-
Config,
|
|
15
16
|
closePool,
|
|
16
17
|
countUsers,
|
|
17
18
|
createUser,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
deleteMemory,
|
|
20
21
|
deleteUser,
|
|
21
22
|
getArchProfile,
|
|
23
|
+
getMemory,
|
|
22
24
|
getPool,
|
|
23
25
|
getUserByEmail,
|
|
24
26
|
listArchProfiles,
|
|
@@ -27,13 +29,21 @@ import {
|
|
|
27
29
|
saveArchProfile,
|
|
28
30
|
saveMemory,
|
|
29
31
|
updateLastLogin,
|
|
30
|
-
updateMemory
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
updateMemory,
|
|
33
|
+
upsertMemory
|
|
34
|
+
} from "./chunk-5T2QBBYT.js";
|
|
35
|
+
import {
|
|
36
|
+
Config
|
|
37
|
+
} from "./chunk-2LNDQGDD.js";
|
|
38
|
+
import {
|
|
39
|
+
callChatModel,
|
|
40
|
+
getChatProviderLabel
|
|
41
|
+
} from "./chunk-XAE3RY5D.js";
|
|
42
|
+
import "./chunk-K3NQKI34.js";
|
|
33
43
|
|
|
34
44
|
// src/dashboard-server.ts
|
|
35
45
|
import { createHash } from "crypto";
|
|
36
|
-
import {
|
|
46
|
+
import { execFileSync } from "child_process";
|
|
37
47
|
import { createReadStream, existsSync, mkdirSync, readFileSync, watch, writeFileSync } from "fs";
|
|
38
48
|
import { createServer } from "http";
|
|
39
49
|
import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
|
|
@@ -88,6 +98,8 @@ var watcherStatus = {
|
|
|
88
98
|
};
|
|
89
99
|
var RUNTIME_ENV_KEYS = [
|
|
90
100
|
"DATABASE_URL",
|
|
101
|
+
"EMBEDDING_URL",
|
|
102
|
+
"EMBEDDING_MODEL",
|
|
91
103
|
"OLLAMA_URL",
|
|
92
104
|
"OLLAMA_MODEL",
|
|
93
105
|
"CHAT_PROVIDER",
|
|
@@ -127,7 +139,7 @@ function readViolationHistory() {
|
|
|
127
139
|
}
|
|
128
140
|
function getGitAuthor(file) {
|
|
129
141
|
try {
|
|
130
|
-
return
|
|
142
|
+
return execFileSync("git", ["log", "--format=%an", "-1", "--", file], { cwd: projectRoot, stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString().trim() || void 0;
|
|
131
143
|
} catch {
|
|
132
144
|
return void 0;
|
|
133
145
|
}
|
|
@@ -144,14 +156,65 @@ function appendViolationHistory(file, violations, timestamp) {
|
|
|
144
156
|
} catch {
|
|
145
157
|
}
|
|
146
158
|
}
|
|
159
|
+
function settingsPath() {
|
|
160
|
+
return join(projectRoot, ".archmind", "dashboard-settings.json");
|
|
161
|
+
}
|
|
162
|
+
var DEFAULT_SETTINGS = {
|
|
163
|
+
autoFix: false,
|
|
164
|
+
verbose: false,
|
|
165
|
+
debug: false,
|
|
166
|
+
scanOnStart: false
|
|
167
|
+
};
|
|
168
|
+
var cachedSettings = null;
|
|
169
|
+
function readSettings() {
|
|
170
|
+
if (cachedSettings) return cachedSettings;
|
|
171
|
+
const path = settingsPath();
|
|
172
|
+
if (!existsSync(path)) {
|
|
173
|
+
cachedSettings = { ...DEFAULT_SETTINGS };
|
|
174
|
+
return cachedSettings;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
178
|
+
cachedSettings = { ...DEFAULT_SETTINGS, ...parsed };
|
|
179
|
+
} catch {
|
|
180
|
+
cachedSettings = { ...DEFAULT_SETTINGS };
|
|
181
|
+
}
|
|
182
|
+
return cachedSettings;
|
|
183
|
+
}
|
|
184
|
+
function writeSettings(next) {
|
|
185
|
+
try {
|
|
186
|
+
const archmindDir = join(projectRoot, ".archmind");
|
|
187
|
+
mkdirSync(archmindDir, { recursive: true });
|
|
188
|
+
writeFileSync(settingsPath(), JSON.stringify(next, null, 2), "utf-8");
|
|
189
|
+
cachedSettings = next;
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function markFileResolved(file, timestamp) {
|
|
194
|
+
try {
|
|
195
|
+
const history = readViolationHistory();
|
|
196
|
+
let changed = false;
|
|
197
|
+
for (const entry of history) {
|
|
198
|
+
if (entry.file === file && !entry.resolved) {
|
|
199
|
+
entry.resolved = true;
|
|
200
|
+
entry.resolvedAt = timestamp;
|
|
201
|
+
changed = true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (changed) {
|
|
205
|
+
writeFileSync(historyPath(), JSON.stringify(history, null, 2), "utf-8");
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
}
|
|
147
210
|
function readArchState() {
|
|
148
211
|
const archmindDir = join(projectRoot, ".archmind");
|
|
149
212
|
const queuePath = join(archmindDir, "approval-queue.json");
|
|
150
213
|
const rulesPath = join(archmindDir, "rules.json");
|
|
151
214
|
const layersPath = join(archmindDir, "layers.json");
|
|
152
215
|
try {
|
|
153
|
-
const queue =
|
|
154
|
-
archState.pendingRules = Array.isArray(queue
|
|
216
|
+
const queue = existsSync(queuePath) ? JSON.parse(readFileSync(queuePath, "utf-8")) : [];
|
|
217
|
+
archState.pendingRules = Array.isArray(queue) ? queue.filter((i) => i.status === "pending").length : 0;
|
|
155
218
|
} catch {
|
|
156
219
|
archState.pendingRules = 0;
|
|
157
220
|
}
|
|
@@ -364,8 +427,8 @@ function buildModelStatusCacheKey() {
|
|
|
364
427
|
return [
|
|
365
428
|
Config.chatProvider,
|
|
366
429
|
Config.chatModel,
|
|
367
|
-
Config.
|
|
368
|
-
Config.
|
|
430
|
+
Config.embeddingModel,
|
|
431
|
+
Config.embeddingUrl,
|
|
369
432
|
Config.chatApiKey ? "key:set" : "key:empty"
|
|
370
433
|
].join("|");
|
|
371
434
|
}
|
|
@@ -377,8 +440,8 @@ async function getModelStatus(forceRefresh = false) {
|
|
|
377
440
|
}
|
|
378
441
|
const provider = Config.chatProvider;
|
|
379
442
|
const checkModel = Config.chatModel;
|
|
380
|
-
const embeddingModel = Config.
|
|
381
|
-
const ollamaUrl = Config.
|
|
443
|
+
const embeddingModel = Config.embeddingModel;
|
|
444
|
+
const ollamaUrl = Config.embeddingUrl;
|
|
382
445
|
const status = {
|
|
383
446
|
provider,
|
|
384
447
|
checkModel,
|
|
@@ -668,6 +731,87 @@ async function handleApi(req, res, url) {
|
|
|
668
731
|
scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
|
|
669
732
|
return;
|
|
670
733
|
}
|
|
734
|
+
if (req.method === "POST" && url.pathname.endsWith("/check")) {
|
|
735
|
+
const checkMatch = url.pathname.match(/^\/api\/memories\/(\d+)\/check$/);
|
|
736
|
+
if (!checkMatch) {
|
|
737
|
+
sendJson(res, 400, { error: "Invalid path" });
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const id = Number(checkMatch[1]);
|
|
741
|
+
const memory = await getMemory(id);
|
|
742
|
+
if (!memory) {
|
|
743
|
+
sendJson(res, 404, { error: `No memory found with ID ${id}` });
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const config = readProjectConfig();
|
|
747
|
+
const architectures = inferProjectArchitectures(projectRoot, config);
|
|
748
|
+
const projectInfo = [
|
|
749
|
+
`Project: ${config?.projectName ?? "unknown"}`,
|
|
750
|
+
`Type: ${config?.projectType ?? "unknown"}`,
|
|
751
|
+
config?.backendArchitecture ? `Backend: ${config.backendArchitecture}` : "",
|
|
752
|
+
config?.frontendFramework ? `Frontend: ${config.frontendFramework}` : "",
|
|
753
|
+
config?.language ? `Language: ${config.language}` : ""
|
|
754
|
+
].filter(Boolean).join(", ");
|
|
755
|
+
const systemPrompt = 'Audit memory rule quality. Return JSON: {"status":"valid|stale|duplicate|vague","confidence":0.X,"reasoning":"","suggestion":"","issues":[]}. stale=outdated APIs/patterns. duplicate=says what other rules say. vague=not actionable.';
|
|
756
|
+
const truncatedContent = memory.content.length > 800 ? `${memory.content.slice(0, 800)}\u2026[truncated]` : memory.content;
|
|
757
|
+
const userPrompt = [
|
|
758
|
+
`Project: ${projectInfo}`,
|
|
759
|
+
`Arch: ${architectures.join(",") || "none"}`,
|
|
760
|
+
`Type: ${memory.type} | Scope: ${memory.scope} | Architecture: ${memory.architecture ?? "none"}`,
|
|
761
|
+
`Tags: ${(memory.tags ?? []).join(",") || "none"}`,
|
|
762
|
+
memory.title ? `Title: ${memory.title}` : "",
|
|
763
|
+
"",
|
|
764
|
+
truncatedContent
|
|
765
|
+
].filter(Boolean).join("\n");
|
|
766
|
+
try {
|
|
767
|
+
const result = await callChatModel([
|
|
768
|
+
{ role: "system", content: systemPrompt, cache: true },
|
|
769
|
+
{ role: "user", content: userPrompt }
|
|
770
|
+
], { timeoutMs: 3e4 });
|
|
771
|
+
const trimmed = result.content.trim();
|
|
772
|
+
const jsonMatch = trimmed.match(/(?:```(?:json)?\s*)?(\{[\s\S]*\})(?:\s*```)?/);
|
|
773
|
+
const parsed = jsonMatch ? JSON.parse(jsonMatch[1]) : JSON.parse(trimmed);
|
|
774
|
+
sendJson(res, 200, {
|
|
775
|
+
id: memory.id,
|
|
776
|
+
status: parsed.status ?? "unknown",
|
|
777
|
+
confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0,
|
|
778
|
+
reasoning: parsed.reasoning ?? "",
|
|
779
|
+
suggestion: parsed.suggestion ?? "",
|
|
780
|
+
issues: Array.isArray(parsed.issues) ? parsed.issues : [],
|
|
781
|
+
memory
|
|
782
|
+
});
|
|
783
|
+
} catch (err) {
|
|
784
|
+
sendJson(res, 500, { error: `Model check failed: ${err.message}` });
|
|
785
|
+
}
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (req.method === "POST" && url.pathname.endsWith("/apply-suggestion")) {
|
|
789
|
+
const applyMatch = url.pathname.match(/^\/api\/memories\/(\d+)\/apply-suggestion$/);
|
|
790
|
+
if (!applyMatch) {
|
|
791
|
+
sendJson(res, 400, { error: "Invalid path" });
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const id = Number(applyMatch[1]);
|
|
795
|
+
const body = await readBody(req);
|
|
796
|
+
const content = typeof body.content === "string" ? body.content.trim() : "";
|
|
797
|
+
if (!content) {
|
|
798
|
+
sendJson(res, 400, { error: "content is required" });
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const updated = await updateMemory(id, {
|
|
802
|
+
content,
|
|
803
|
+
reason: typeof body.reason === "string" && body.reason.trim() ? body.reason.trim() : void 0,
|
|
804
|
+
embedding: await embed(content)
|
|
805
|
+
});
|
|
806
|
+
if (!updated) {
|
|
807
|
+
sendJson(res, 404, { error: `No memory found with ID ${id}` });
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
sendJson(res, 200, updated);
|
|
811
|
+
invalidateSnapshotBase();
|
|
812
|
+
scheduleSnapshotBroadcast({ delayMs: 0, forceBaseRefresh: true });
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
671
815
|
if (req.method === "POST" && url.pathname === "/api/config/model") {
|
|
672
816
|
const body = await readBody(req);
|
|
673
817
|
const provider = typeof body.provider === "string" && body.provider.trim() ? body.provider.trim() : null;
|
|
@@ -704,7 +848,7 @@ async function handleApi(req, res, url) {
|
|
|
704
848
|
const warnings = [];
|
|
705
849
|
if (effectiveProvider === "ollama") {
|
|
706
850
|
try {
|
|
707
|
-
const ollamaUrl =
|
|
851
|
+
const ollamaUrl = Config.embeddingUrl;
|
|
708
852
|
const r = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
709
853
|
if (!r.ok) warnings.push(`Ollama returned HTTP ${r.status}`);
|
|
710
854
|
} catch (err) {
|
|
@@ -808,13 +952,30 @@ async function handleApi(req, res, url) {
|
|
|
808
952
|
teamActivity,
|
|
809
953
|
summary: {
|
|
810
954
|
total: history.reduce((s, e) => s + e.violations.length, 0),
|
|
811
|
-
resolved: history.filter((e) => e.resolved).length,
|
|
812
|
-
open: history.filter((e) => !e.resolved).length,
|
|
955
|
+
resolved: history.filter((e) => e.resolved).reduce((s, e) => s + e.violations.length, 0),
|
|
956
|
+
open: history.filter((e) => !e.resolved).reduce((s, e) => s + e.violations.length, 0),
|
|
813
957
|
uniqueFiles: new Set(history.map((e) => e.file)).size
|
|
814
958
|
}
|
|
815
959
|
});
|
|
816
960
|
return;
|
|
817
961
|
}
|
|
962
|
+
if (req.method === "GET" && url.pathname === "/api/settings") {
|
|
963
|
+
sendJson(res, 200, readSettings());
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (req.method === "POST" && url.pathname === "/api/settings") {
|
|
967
|
+
const body = await readBody(req);
|
|
968
|
+
const current = readSettings();
|
|
969
|
+
const next = {
|
|
970
|
+
autoFix: typeof body.autoFix === "boolean" ? body.autoFix : current.autoFix,
|
|
971
|
+
verbose: typeof body.verbose === "boolean" ? body.verbose : current.verbose,
|
|
972
|
+
debug: typeof body.debug === "boolean" ? body.debug : current.debug,
|
|
973
|
+
scanOnStart: typeof body.scanOnStart === "boolean" ? body.scanOnStart : current.scanOnStart
|
|
974
|
+
};
|
|
975
|
+
writeSettings(next);
|
|
976
|
+
sendJson(res, 200, next);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
818
979
|
if (req.method === "DELETE" && url.pathname === "/api/history") {
|
|
819
980
|
try {
|
|
820
981
|
const path = historyPath();
|
|
@@ -1071,7 +1232,7 @@ async function handleApi(req, res, url) {
|
|
|
1071
1232
|
if (req.method === "GET" && url.pathname === "/api/auth/me") {
|
|
1072
1233
|
const payload = requireAuth(req);
|
|
1073
1234
|
if (!payload) {
|
|
1074
|
-
sendJson(res, 401, { error: "Unauthorized" });
|
|
1235
|
+
sendJson(res, 401, { error: "Unauthorized", authEnabled });
|
|
1075
1236
|
return;
|
|
1076
1237
|
}
|
|
1077
1238
|
sendJson(res, 200, { user: { userId: payload.userId, email: payload.email, username: payload.username, role: payload.role, teamName: payload.teamName }, authEnabled });
|
|
@@ -1145,6 +1306,53 @@ async function handleApi(req, res, url) {
|
|
|
1145
1306
|
sendJson(res, deleted ? 200 : 404, deleted ? { ok: true } : { error: "User not found" });
|
|
1146
1307
|
return;
|
|
1147
1308
|
}
|
|
1309
|
+
if (req.method === "GET" && url.pathname === "/api/proposals") {
|
|
1310
|
+
const queuePath = join(projectRoot, ".archmind", "approval-queue.json");
|
|
1311
|
+
const items = existsSync(queuePath) ? JSON.parse(readFileSync(queuePath, "utf-8")) : [];
|
|
1312
|
+
const pending = Array.isArray(items) ? items.filter((i) => i.status === "pending") : [];
|
|
1313
|
+
sendJson(res, 200, { proposals: pending });
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const proposalMatch = url.pathname.match(/^\/api\/proposals\/([^/]+)\/(approve|reject)$/);
|
|
1317
|
+
if (proposalMatch && req.method === "POST") {
|
|
1318
|
+
const [, id, action] = proposalMatch;
|
|
1319
|
+
const queuePath = join(projectRoot, ".archmind", "approval-queue.json");
|
|
1320
|
+
if (!existsSync(queuePath)) {
|
|
1321
|
+
sendJson(res, 404, { error: "No proposals" });
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
const items = JSON.parse(readFileSync(queuePath, "utf-8"));
|
|
1325
|
+
const item = items.find((i) => i.id === id);
|
|
1326
|
+
if (!item) {
|
|
1327
|
+
sendJson(res, 404, { error: "Proposal not found" });
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
item.status = action === "approve" ? "approved" : "rejected";
|
|
1331
|
+
writeFileSync(queuePath, JSON.stringify(items, null, 2));
|
|
1332
|
+
if (action === "approve") {
|
|
1333
|
+
try {
|
|
1334
|
+
const embedding = await embed(item.rule.description);
|
|
1335
|
+
await upsertMemory({
|
|
1336
|
+
type: "rule",
|
|
1337
|
+
scope: "project",
|
|
1338
|
+
title: item.rule.name,
|
|
1339
|
+
content: item.rule.description,
|
|
1340
|
+
reason: `Auto-captured from ${item.source} (confidence: ${item.confidence})`,
|
|
1341
|
+
tags: [item.source, item.rule.severity],
|
|
1342
|
+
embedding
|
|
1343
|
+
});
|
|
1344
|
+
const remaining = items.filter((i) => i.status !== "approved");
|
|
1345
|
+
writeFileSync(queuePath, JSON.stringify(remaining, null, 2));
|
|
1346
|
+
readArchState();
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
sendJson(res, 500, { error: `Saved to queue but DB insert failed: ${err.message}` });
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
readArchState();
|
|
1353
|
+
sendJson(res, 200, { ok: true });
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1148
1356
|
sendJson(res, 404, { error: "Not found" });
|
|
1149
1357
|
} catch (err) {
|
|
1150
1358
|
sendJson(res, 500, { error: err.message });
|
|
@@ -1272,6 +1480,43 @@ function scheduleSnapshotBroadcast(options = {}) {
|
|
|
1272
1480
|
void flushSnapshotBroadcast();
|
|
1273
1481
|
}, Math.max(0, delayMs));
|
|
1274
1482
|
}
|
|
1483
|
+
async function draftProposalsFromViolations(file, violations) {
|
|
1484
|
+
if (violations.length === 0) return;
|
|
1485
|
+
try {
|
|
1486
|
+
const { draftRuleWithAI, draftRuleFallback } = await import("./draft-rule-NUXOSRRS.js");
|
|
1487
|
+
const { ApprovalQueue } = await import("./approval-queue-4RK46FNE.js");
|
|
1488
|
+
const queue = new ApprovalQueue(join(projectRoot, ".archmind"));
|
|
1489
|
+
let queued = 0;
|
|
1490
|
+
for (const v of violations) {
|
|
1491
|
+
const context = `${v.rule}. Issue: ${v.issue}. File: ${file}`;
|
|
1492
|
+
const aiResult = await draftRuleWithAI({ context, source: "watch" });
|
|
1493
|
+
const { rule, confidence } = aiResult ?? draftRuleFallback({ context, source: "watch" });
|
|
1494
|
+
if (confidence === "high") {
|
|
1495
|
+
try {
|
|
1496
|
+
const embedding = await embed(rule.description);
|
|
1497
|
+
await upsertMemory({
|
|
1498
|
+
type: "rule",
|
|
1499
|
+
scope: "project",
|
|
1500
|
+
title: rule.name,
|
|
1501
|
+
content: rule.description,
|
|
1502
|
+
reason: `Auto-captured from watcher violation in ${file} (high confidence)`,
|
|
1503
|
+
tags: ["watcher", rule.severity],
|
|
1504
|
+
embedding
|
|
1505
|
+
});
|
|
1506
|
+
continue;
|
|
1507
|
+
} catch {
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
queue.add(rule, "watch", confidence, { file, evidence: `${v.rule}: ${v.issue}` });
|
|
1511
|
+
queued++;
|
|
1512
|
+
}
|
|
1513
|
+
if (queued > 0) {
|
|
1514
|
+
readArchState();
|
|
1515
|
+
broadcast({ type: "proposals-updated", count: queue.pending().length });
|
|
1516
|
+
}
|
|
1517
|
+
} catch {
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1275
1520
|
function handleWatchEvent(event) {
|
|
1276
1521
|
recentEvents.push(event);
|
|
1277
1522
|
if (recentEvents.length > 100) recentEvents.shift();
|
|
@@ -1298,6 +1543,7 @@ function handleWatchEvent(event) {
|
|
|
1298
1543
|
lastSeen: event.timestamp,
|
|
1299
1544
|
violations: []
|
|
1300
1545
|
});
|
|
1546
|
+
markFileResolved(event.file, event.timestamp);
|
|
1301
1547
|
}
|
|
1302
1548
|
if (event.type === "skipped") {
|
|
1303
1549
|
fileStatuses.set(event.file, {
|
|
@@ -1316,6 +1562,7 @@ function handleWatchEvent(event) {
|
|
|
1316
1562
|
violations: event.violations
|
|
1317
1563
|
});
|
|
1318
1564
|
appendViolationHistory(event.file, event.violations, event.timestamp);
|
|
1565
|
+
void draftProposalsFromViolations(event.file, event.violations);
|
|
1319
1566
|
}
|
|
1320
1567
|
if (event.type === "error") {
|
|
1321
1568
|
watcherStatus.error = event.message;
|
|
@@ -1444,9 +1691,15 @@ async function startDashboard(options = {}) {
|
|
|
1444
1691
|
console.log(chalk.gray(` Project root: ${projectRoot}`));
|
|
1445
1692
|
if (options.watch ?? true) {
|
|
1446
1693
|
watcherStatus.enabled = true;
|
|
1694
|
+
const initial = readSettings();
|
|
1447
1695
|
void startWatch({
|
|
1448
1696
|
projectRoot,
|
|
1449
1697
|
path: ".",
|
|
1698
|
+
autoFix: initial.autoFix,
|
|
1699
|
+
verbose: initial.verbose,
|
|
1700
|
+
debug: initial.debug,
|
|
1701
|
+
scanOnStart: initial.scanOnStart,
|
|
1702
|
+
getAutoFix: () => readSettings().autoFix,
|
|
1450
1703
|
onEvent: handleWatchEvent,
|
|
1451
1704
|
exitOnSetupFailure: false
|
|
1452
1705
|
});
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
draftRuleFallback,
|
|
4
|
+
draftRuleWithAI
|
|
5
|
+
} from "./chunk-FPRSYCOZ.js";
|
|
6
|
+
import "./chunk-XAE3RY5D.js";
|
|
7
|
+
import "./chunk-K3NQKI34.js";
|
|
2
8
|
|
|
3
9
|
// src/automation/incident-capture.ts
|
|
4
10
|
var IncidentCaptureService = class {
|
|
11
|
+
async draftRuleWithAI(incident) {
|
|
12
|
+
const context = `What broke: ${incident.what}. Root cause: ${incident.why}. Location: ${incident.where}`;
|
|
13
|
+
const result = await draftRuleWithAI({ context, source: "incident", hint: incident.why });
|
|
14
|
+
return result ?? draftRuleFallback({ context, source: "incident" });
|
|
15
|
+
}
|
|
5
16
|
draftRule(incident) {
|
|
6
17
|
return {
|
|
7
18
|
id: `auto-incident-${Date.now()}`,
|