@shrkcrft/cli 0.1.0-alpha.2 → 0.1.0-alpha.21
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/dist/audit/knowledge-audit-llm.d.ts +19 -0
- package/dist/audit/knowledge-audit-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-audit-llm.js +164 -0
- package/dist/audit/knowledge-audit.d.ts +61 -0
- package/dist/audit/knowledge-audit.d.ts.map +1 -0
- package/dist/audit/knowledge-audit.js +203 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts +11 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan-llm.js +141 -0
- package/dist/audit/knowledge-fix-plan.d.ts +41 -0
- package/dist/audit/knowledge-fix-plan.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan.js +125 -0
- package/dist/audit/pipeline-audit-llm.d.ts +11 -0
- package/dist/audit/pipeline-audit-llm.d.ts.map +1 -0
- package/dist/audit/pipeline-audit-llm.js +134 -0
- package/dist/audit/pipeline-audit.d.ts +69 -0
- package/dist/audit/pipeline-audit.d.ts.map +1 -0
- package/dist/audit/pipeline-audit.js +166 -0
- package/dist/audit/templates-audit-llm.d.ts +19 -0
- package/dist/audit/templates-audit-llm.d.ts.map +1 -0
- package/dist/audit/templates-audit-llm.js +207 -0
- package/dist/audit/templates-audit.d.ts +63 -0
- package/dist/audit/templates-audit.d.ts.map +1 -0
- package/dist/audit/templates-audit.js +171 -0
- package/dist/audit/templates-fix-plan-llm.d.ts +19 -0
- package/dist/audit/templates-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan-llm.js +162 -0
- package/dist/audit/templates-fix-plan.d.ts +37 -0
- package/dist/audit/templates-fix-plan.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan.js +174 -0
- package/dist/command-registry.d.ts +28 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +91 -1
- package/dist/commands/ai-status.command.d.ts +19 -0
- package/dist/commands/ai-status.command.d.ts.map +1 -0
- package/dist/commands/ai-status.command.js +94 -0
- package/dist/commands/api-diff.command.d.ts +11 -0
- package/dist/commands/api-diff.command.d.ts.map +1 -0
- package/dist/commands/api-diff.command.js +144 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +10 -2
- package/dist/commands/arch.command.d.ts +9 -0
- package/dist/commands/arch.command.d.ts.map +1 -0
- package/dist/commands/arch.command.js +186 -0
- package/dist/commands/ask.command.d.ts.map +1 -1
- package/dist/commands/ask.command.js +10 -9
- package/dist/commands/cache-align.command.d.ts +12 -0
- package/dist/commands/cache-align.command.d.ts.map +1 -0
- package/dist/commands/cache-align.command.js +78 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +26 -2
- package/dist/commands/code-intel.command.d.ts +18 -0
- package/dist/commands/code-intel.command.d.ts.map +1 -0
- package/dist/commands/code-intel.command.js +146 -0
- package/dist/commands/codemod.command.d.ts.map +1 -1
- package/dist/commands/codemod.command.js +27 -6
- package/dist/commands/command-catalog.d.ts +15 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +407 -34
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +4 -4
- package/dist/commands/completion.command.d.ts +10 -0
- package/dist/commands/completion.command.d.ts.map +1 -0
- package/dist/commands/completion.command.js +121 -0
- package/dist/commands/compress.command.d.ts +8 -0
- package/dist/commands/compress.command.d.ts.map +1 -0
- package/dist/commands/compress.command.js +147 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +89 -23
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +121 -1
- package/dist/commands/contract-gate.command.d.ts.map +1 -1
- package/dist/commands/contract-gate.command.js +5 -1
- package/dist/commands/delegate.command.d.ts +65 -0
- package/dist/commands/delegate.command.d.ts.map +1 -0
- package/dist/commands/delegate.command.js +657 -0
- package/dist/commands/deps-audit.command.d.ts +23 -0
- package/dist/commands/deps-audit.command.d.ts.map +1 -0
- package/dist/commands/deps-audit.command.js +270 -0
- package/dist/commands/dev.command.d.ts.map +1 -1
- package/dist/commands/dev.command.js +5 -2
- package/dist/commands/diff-check.command.d.ts +30 -0
- package/dist/commands/diff-check.command.d.ts.map +1 -0
- package/dist/commands/diff-check.command.js +210 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +162 -10
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +76 -3
- package/dist/commands/framework.command.d.ts +12 -0
- package/dist/commands/framework.command.d.ts.map +1 -0
- package/dist/commands/framework.command.js +180 -0
- package/dist/commands/gate.command.d.ts +15 -0
- package/dist/commands/gate.command.d.ts.map +1 -0
- package/dist/commands/gate.command.js +300 -0
- package/dist/commands/gen.command.d.ts.map +1 -1
- package/dist/commands/gen.command.js +13 -1
- package/dist/commands/graph-code-subverbs.d.ts +33 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
- package/dist/commands/graph-code-subverbs.js +1385 -0
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +31 -2
- package/dist/commands/help.command.d.ts +4 -3
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +86 -18
- package/dist/commands/helper.command.js +1 -1
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +171 -1
- package/dist/commands/import.command.d.ts.map +1 -1
- package/dist/commands/import.command.js +121 -5
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +5 -1
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +174 -7
- package/dist/commands/knowledge-author.command.d.ts.map +1 -1
- package/dist/commands/knowledge-author.command.js +9 -0
- package/dist/commands/knowledge-propose.command.d.ts.map +1 -1
- package/dist/commands/knowledge-propose.command.js +4 -2
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +26 -3
- package/dist/commands/migrate.command.d.ts +13 -0
- package/dist/commands/migrate.command.d.ts.map +1 -0
- package/dist/commands/migrate.command.js +152 -0
- package/dist/commands/move-plan.command.d.ts +23 -0
- package/dist/commands/move-plan.command.d.ts.map +1 -0
- package/dist/commands/move-plan.command.js +360 -0
- package/dist/commands/packs-new.d.ts +1 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +5 -36
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +2 -10
- package/dist/commands/plan-context.command.d.ts +11 -0
- package/dist/commands/plan-context.command.d.ts.map +1 -0
- package/dist/commands/plan-context.command.js +85 -0
- package/dist/commands/preflight.command.d.ts.map +1 -1
- package/dist/commands/preflight.command.js +15 -0
- package/dist/commands/profiles.command.js +4 -4
- package/dist/commands/recommend.command.d.ts +6 -0
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +119 -5
- package/dist/commands/release.command.js +13 -13
- package/dist/commands/rule-graph-subverbs.d.ts +3 -0
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
- package/dist/commands/rule-graph-subverbs.js +132 -0
- package/dist/commands/rules.command.d.ts.map +1 -1
- package/dist/commands/rules.command.js +20 -3
- package/dist/commands/scaffold-validate.command.d.ts +22 -0
- package/dist/commands/scaffold-validate.command.d.ts.map +1 -0
- package/dist/commands/scaffold-validate.command.js +215 -0
- package/dist/commands/search-structural.command.d.ts +18 -0
- package/dist/commands/search-structural.command.d.ts.map +1 -0
- package/dist/commands/search-structural.command.js +376 -0
- package/dist/commands/search.command.js +1 -1
- package/dist/commands/smart-context.command.d.ts +67 -0
- package/dist/commands/smart-context.command.d.ts.map +1 -0
- package/dist/commands/smart-context.command.js +4728 -0
- package/dist/commands/spike.command.d.ts +22 -0
- package/dist/commands/spike.command.d.ts.map +1 -0
- package/dist/commands/spike.command.js +235 -0
- package/dist/commands/surface.command.d.ts +1 -0
- package/dist/commands/surface.command.d.ts.map +1 -1
- package/dist/commands/surface.command.js +10 -3
- package/dist/commands/task-context.command.d.ts.map +1 -1
- package/dist/commands/task-context.command.js +5 -17
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +8 -2
- package/dist/commands/template-quality.command.d.ts.map +1 -1
- package/dist/commands/template-quality.command.js +39 -3
- package/dist/commands/templates.command.d.ts.map +1 -1
- package/dist/commands/templates.command.js +37 -2
- package/dist/commands/tests.command.d.ts.map +1 -1
- package/dist/commands/tests.command.js +13 -2
- package/dist/commands/watch.command.d.ts +26 -0
- package/dist/commands/watch.command.d.ts.map +1 -0
- package/dist/commands/watch.command.js +456 -0
- package/dist/dashboard/code-intelligence-data.d.ts +33 -0
- package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
- package/dist/dashboard/code-intelligence-data.js +329 -0
- package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
- package/dist/dashboard/dashboard-api-server.js +256 -2
- package/dist/dashboard/knowledge-ask.d.ts +4 -0
- package/dist/dashboard/knowledge-ask.d.ts.map +1 -0
- package/dist/dashboard/knowledge-ask.js +112 -0
- package/dist/env/load-dotenv.d.ts +15 -0
- package/dist/env/load-dotenv.d.ts.map +1 -0
- package/dist/env/load-dotenv.js +70 -0
- package/dist/export/claude-commands-export.d.ts +60 -0
- package/dist/export/claude-commands-export.d.ts.map +1 -0
- package/dist/export/claude-commands-export.js +276 -0
- package/dist/export/export-formats.d.ts +1 -1
- package/dist/export/export-formats.d.ts.map +1 -1
- package/dist/export/export-formats.js +139 -12
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/init/init-templates.d.ts.map +1 -1
- package/dist/init/init-templates.js +133 -113
- package/dist/init/paths-advisory.d.ts +20 -0
- package/dist/init/paths-advisory.d.ts.map +1 -0
- package/dist/init/paths-advisory.js +88 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +331 -17
- package/dist/output/ccr-store-config.d.ts +18 -0
- package/dist/output/ccr-store-config.d.ts.map +1 -0
- package/dist/output/ccr-store-config.js +41 -0
- package/dist/output/format-output.d.ts.map +1 -1
- package/dist/output/format-output.js +6 -1
- package/dist/output/output-compression.d.ts +15 -0
- package/dist/output/output-compression.d.ts.map +1 -0
- package/dist/output/output-compression.js +60 -0
- package/dist/output/resolve-compress-type.d.ts +22 -0
- package/dist/output/resolve-compress-type.d.ts.map +1 -0
- package/dist/output/resolve-compress-type.js +21 -0
- package/dist/output/watch-loop.d.ts +9 -1
- package/dist/output/watch-loop.d.ts.map +1 -1
- package/dist/output/watch-loop.js +13 -3
- package/dist/schemas/json-schemas.d.ts +384 -36
- package/dist/schemas/json-schemas.d.ts.map +1 -1
- package/dist/schemas/json-schemas.js +247 -36
- package/dist/surface/profiles.d.ts.map +1 -1
- package/dist/surface/profiles.js +54 -10
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +23 -11
- package/dist/validation/run-validation-loop.d.ts.map +1 -1
- package/dist/validation/run-validation-loop.js +5 -1
- package/package.json +35 -21
- package/dist/commands/plugin.command.d.ts +0 -11
- package/dist/commands/plugin.command.d.ts.map +0 -1
- package/dist/commands/plugin.command.js +0 -394
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type IAiMessage, type IAiProvider } from '@shrkcrft/ai';
|
|
2
|
+
import type { IKnowledgeEntry } from '@shrkcrft/knowledge';
|
|
3
|
+
import type { ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
4
|
+
import type { IKnowledgeAuditEntry, IKnowledgeAuditReport, ILlmKnowledgeAuditFinding } from './knowledge-audit.js';
|
|
5
|
+
export interface IEnrichKnowledgeAuditOptions {
|
|
6
|
+
provider: IAiProvider;
|
|
7
|
+
inspection: ISharkcraftInspection;
|
|
8
|
+
maxTokensPerEntry?: number;
|
|
9
|
+
onPerEntryError?: (entryId: string, error: Error) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function enrichKnowledgeAuditWithLlm(report: IKnowledgeAuditReport, options: IEnrichKnowledgeAuditOptions): Promise<IKnowledgeAuditReport>;
|
|
12
|
+
declare function buildEnrichmentMessages(entry: IKnowledgeEntry, peers: readonly IKnowledgeEntry[], auditEntry: IKnowledgeAuditEntry): IAiMessage[];
|
|
13
|
+
declare function parseLlmFindings(raw: string): readonly ILlmKnowledgeAuditFinding[];
|
|
14
|
+
export declare const __internals: {
|
|
15
|
+
buildEnrichmentMessages: typeof buildEnrichmentMessages;
|
|
16
|
+
parseLlmFindings: typeof parseLlmFindings;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=knowledge-audit-llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-audit-llm.d.ts","sourceRoot":"","sources":["../../src/audit/knowledge-audit-llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EAE1B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,qBAAqB,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,qBAAqB,CAAC,CAoChC;AAED,iBAAS,uBAAuB,CAC9B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,UAAU,EAAE,oBAAoB,GAC/B,UAAU,EAAE,CAyEd;AAOD,iBAAS,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,yBAAyB,EAAE,CAoC3E;AAQD,eAAO,MAAM,WAAW;;;CAAgD,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { AiMessageRole } from '@shrkcrft/ai';
|
|
2
|
+
export async function enrichKnowledgeAuditWithLlm(report, options) {
|
|
3
|
+
const enriched = [];
|
|
4
|
+
for (const entry of report.entries) {
|
|
5
|
+
const ke = options.inspection.knowledgeEntries.find((e) => e.id === entry.entryId);
|
|
6
|
+
if (!ke) {
|
|
7
|
+
enriched.push(entry);
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const peers = options.inspection.knowledgeEntries
|
|
11
|
+
.filter((e) => e.id !== ke.id && String(e.type) === String(ke.type))
|
|
12
|
+
.slice(0, 4);
|
|
13
|
+
const messages = buildEnrichmentMessages(ke, peers, entry);
|
|
14
|
+
try {
|
|
15
|
+
const res = await options.provider.send({
|
|
16
|
+
messages,
|
|
17
|
+
maxTokens: options.maxTokensPerEntry ?? 1024,
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
options.onPerEntryError?.(entry.entryId, res.error);
|
|
21
|
+
enriched.push(entry);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const parsed = parseLlmFindings(res.value.content);
|
|
25
|
+
enriched.push({ ...entry, llmFindings: parsed });
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
options.onPerEntryError?.(entry.entryId, e);
|
|
29
|
+
enriched.push(entry);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...report,
|
|
34
|
+
llmEnriched: true,
|
|
35
|
+
llmProviderId: options.provider.id,
|
|
36
|
+
entries: enriched,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function buildEnrichmentMessages(entry, peers, auditEntry) {
|
|
40
|
+
const system = {
|
|
41
|
+
role: AiMessageRole.System,
|
|
42
|
+
content: [
|
|
43
|
+
'You are a critic auditing a SharkCraft knowledge entry for staleness, content quality, and silent drift from sibling entries.',
|
|
44
|
+
'A knowledge entry is a structured piece of project knowledge (a rule, a path convention, a generic knowledge fact) that AI coding agents read when working on the project.',
|
|
45
|
+
'',
|
|
46
|
+
'CRITICAL DISTINCTION — read carefully before flagging anything:',
|
|
47
|
+
'- You are auditing the ENTRY itself, NOT the domain it describes. An entry about "deprecated APIs" is fine even if the APIs it discusses are deprecated; you would only flag it if the entry now teaches the wrong thing about those APIs.',
|
|
48
|
+
'- "References stale" means a file/symbol named in the entry no longer exists. Do not invent stale references — only flag what you can ground in the entry text or supplied deterministic findings.',
|
|
49
|
+
'- A short summary is not automatically a bug. Only flag thin wording when the surrounding context makes the brevity misleading.',
|
|
50
|
+
'',
|
|
51
|
+
'STRICT silence bias: emit a finding only if a senior maintainer would change something on the strength of it. If the deterministic layer already caught it, do not restate. Better silence than ceremony.',
|
|
52
|
+
'',
|
|
53
|
+
'Your job: surface issues the deterministic layer (knowledge lint + knowledge stale-reference check) CANNOT see. Look explicitly for:',
|
|
54
|
+
' 1. **content-drift** — the entry teaches a pattern that the rest of the corpus has moved past.',
|
|
55
|
+
' 2. **internal-contradiction**— the entry contradicts a peer entry in the same scope.',
|
|
56
|
+
' 3. **doc-content-mismatch** — `summary`/`description` and the long-form `body` say different things.',
|
|
57
|
+
' 4. **vague-claim** — the entry uses "always/never/should" without a concrete why or example.',
|
|
58
|
+
' 5. **missing-action-hint** — actionable rule with no command or MCP-tool reference, where similar peers have them.',
|
|
59
|
+
' 6. **stale-phrasing** — references a tool/command/workflow that has been renamed or removed.',
|
|
60
|
+
' 7. **other** — only if none of the above fit.',
|
|
61
|
+
'',
|
|
62
|
+
'Return ONLY a JSON object with this exact shape, no preface, no fences:',
|
|
63
|
+
'{',
|
|
64
|
+
' "findings": [',
|
|
65
|
+
' {',
|
|
66
|
+
' "severity": "info" | "warn" | "error",',
|
|
67
|
+
' "category": "content-drift" | "internal-contradiction" | "doc-content-mismatch" | "vague-claim" | "missing-action-hint" | "stale-phrasing" | "other",',
|
|
68
|
+
' "message": "<one sentence — name specific symbols, commands, or peers when possible>",',
|
|
69
|
+
' "confidence": 0.0',
|
|
70
|
+
' }',
|
|
71
|
+
' ]',
|
|
72
|
+
'}',
|
|
73
|
+
'If nothing new is worth flagging, return {"findings": []}. Never invent symbols, paths, or peer ids not present in the supplied data.',
|
|
74
|
+
].join('\n'),
|
|
75
|
+
};
|
|
76
|
+
const user = {
|
|
77
|
+
role: AiMessageRole.User,
|
|
78
|
+
content: [
|
|
79
|
+
`# Knowledge entry under audit`,
|
|
80
|
+
`id: ${entry.id}`,
|
|
81
|
+
`type: ${String(entry.type)}`,
|
|
82
|
+
`title: ${entry.title ?? '(none)'}`,
|
|
83
|
+
`summary: ${entry.summary ?? '(none)'}`,
|
|
84
|
+
`tags: ${(entry.tags ?? []).join(', ') || '(none)'}`,
|
|
85
|
+
``,
|
|
86
|
+
`## Content (truncated to ~4 KB)`,
|
|
87
|
+
'```',
|
|
88
|
+
truncate(String(entry.content ?? '(no content)'), 4096),
|
|
89
|
+
'```',
|
|
90
|
+
``,
|
|
91
|
+
`## Deterministic findings already produced`,
|
|
92
|
+
auditEntry.deterministicFindings.length === 0
|
|
93
|
+
? '(none)'
|
|
94
|
+
: auditEntry.deterministicFindings
|
|
95
|
+
.map((f) => `- [${f.severity}] ${f.category} (${f.field}): ${f.message} (sources: ${f.sources.join(', ')})`)
|
|
96
|
+
.join('\n'),
|
|
97
|
+
``,
|
|
98
|
+
`## Sibling entries (same type)`,
|
|
99
|
+
peers.length === 0
|
|
100
|
+
? '(no peers in this workspace)'
|
|
101
|
+
: peers
|
|
102
|
+
.map((p) => `- ${p.id}: ${p.title ?? '(no title)'} — ${p.summary ?? '(no summary)'}`)
|
|
103
|
+
.join('\n'),
|
|
104
|
+
].join('\n'),
|
|
105
|
+
};
|
|
106
|
+
return [system, user];
|
|
107
|
+
}
|
|
108
|
+
function truncate(s, max) {
|
|
109
|
+
if (s.length <= max)
|
|
110
|
+
return s;
|
|
111
|
+
return `${s.slice(0, max)}\n… [truncated ${s.length - max} chars]`;
|
|
112
|
+
}
|
|
113
|
+
function parseLlmFindings(raw) {
|
|
114
|
+
const trimmed = raw.trim();
|
|
115
|
+
let jsonText = trimmed;
|
|
116
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
117
|
+
if (fenced)
|
|
118
|
+
jsonText = fenced[1].trim();
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(jsonText);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
const first = jsonText.indexOf('{');
|
|
125
|
+
const last = jsonText.lastIndexOf('}');
|
|
126
|
+
if (first < 0 || last <= first)
|
|
127
|
+
return [];
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(jsonText.slice(first, last + 1));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!parsed || typeof parsed !== 'object')
|
|
136
|
+
return [];
|
|
137
|
+
const list = parsed.findings;
|
|
138
|
+
if (!Array.isArray(list))
|
|
139
|
+
return [];
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const item of list) {
|
|
142
|
+
if (!item || typeof item !== 'object')
|
|
143
|
+
continue;
|
|
144
|
+
const obj = item;
|
|
145
|
+
const severity = coerceSeverity(obj.severity);
|
|
146
|
+
const category = typeof obj.category === 'string' && obj.category.trim() ? obj.category.trim() : 'other';
|
|
147
|
+
const message = typeof obj.message === 'string' ? obj.message.trim() : '';
|
|
148
|
+
const confidence = typeof obj.confidence === 'number' && obj.confidence >= 0 && obj.confidence <= 1
|
|
149
|
+
? obj.confidence
|
|
150
|
+
: 0.5;
|
|
151
|
+
if (!message)
|
|
152
|
+
continue;
|
|
153
|
+
out.push({ severity, category, message, confidence });
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function coerceSeverity(value) {
|
|
158
|
+
if (value === 'error' || value === 'warn' || value === 'info')
|
|
159
|
+
return value;
|
|
160
|
+
if (value === 'warning')
|
|
161
|
+
return 'warn';
|
|
162
|
+
return 'info';
|
|
163
|
+
}
|
|
164
|
+
export const __internals = { buildEnrichmentMessages, parseLlmFindings };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
2
|
+
import type { IAiBlock } from '@shrkcrft/ai';
|
|
3
|
+
export type KnowledgeAuditFindingSeverity = 'info' | 'warn' | 'error';
|
|
4
|
+
export interface IKnowledgeAuditFinding {
|
|
5
|
+
severity: KnowledgeAuditFindingSeverity;
|
|
6
|
+
category: string;
|
|
7
|
+
field: string;
|
|
8
|
+
message: string;
|
|
9
|
+
sources: readonly string[];
|
|
10
|
+
stubSuggestion?: string;
|
|
11
|
+
fixSuggestion?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ILlmKnowledgeAuditFinding {
|
|
14
|
+
severity: KnowledgeAuditFindingSeverity;
|
|
15
|
+
category: string;
|
|
16
|
+
message: string;
|
|
17
|
+
confidence: number;
|
|
18
|
+
}
|
|
19
|
+
export type KnowledgeAuditVerdict = 'ok' | 'minor' | 'stale' | 'broken';
|
|
20
|
+
export interface IKnowledgeAuditSuggestedAction {
|
|
21
|
+
kind: 'edit' | 'rewrite' | 'retire' | 'investigate';
|
|
22
|
+
target: string;
|
|
23
|
+
note: string;
|
|
24
|
+
}
|
|
25
|
+
export interface IKnowledgeAuditEntry {
|
|
26
|
+
entryId: string;
|
|
27
|
+
entryType: string;
|
|
28
|
+
title: string;
|
|
29
|
+
verdict: KnowledgeAuditVerdict;
|
|
30
|
+
deterministicFindings: readonly IKnowledgeAuditFinding[];
|
|
31
|
+
llmFindings: readonly ILlmKnowledgeAuditFinding[];
|
|
32
|
+
suggestedActions: readonly IKnowledgeAuditSuggestedAction[];
|
|
33
|
+
}
|
|
34
|
+
export interface IKnowledgeAuditSkipped {
|
|
35
|
+
entryId: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
}
|
|
38
|
+
export interface IKnowledgeAuditReport {
|
|
39
|
+
auditId: string;
|
|
40
|
+
generatedAt: string;
|
|
41
|
+
llmEnriched: boolean;
|
|
42
|
+
llmProviderId: string | null;
|
|
43
|
+
entries: readonly IKnowledgeAuditEntry[];
|
|
44
|
+
skipped: readonly IKnowledgeAuditSkipped[];
|
|
45
|
+
summary: {
|
|
46
|
+
ok: number;
|
|
47
|
+
minor: number;
|
|
48
|
+
stale: number;
|
|
49
|
+
broken: number;
|
|
50
|
+
total: number;
|
|
51
|
+
};
|
|
52
|
+
ai?: IAiBlock;
|
|
53
|
+
}
|
|
54
|
+
export interface IBuildKnowledgeAuditOptions {
|
|
55
|
+
/** Restrict to one entry id. */
|
|
56
|
+
entryId?: string;
|
|
57
|
+
/** Skip the stale-reference walk (slower; touches the filesystem). Defaults false. */
|
|
58
|
+
skipStaleCheck?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export declare function buildKnowledgeAudit(inspection: ISharkcraftInspection, options?: IBuildKnowledgeAuditOptions): IKnowledgeAuditReport;
|
|
61
|
+
//# sourceMappingURL=knowledge-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-audit.d.ts","sourceRoot":"","sources":["../../src/audit/knowledge-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,MAAM,6BAA6B,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,6BAA6B,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,6BAA6B,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,qBAAqB,CAAC;IAC/B,qBAAqB,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzD,WAAW,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAClD,gBAAgB,EAAE,SAAS,8BAA8B,EAAE,CAAC;CAC7D;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACzC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC3C,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,2BAA2B;IAC1C,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,2BAAgC,GACxC,qBAAqB,CAyGvB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { buildKnowledgeStaleReport, KnowledgeLintSeverity, lintKnowledge, ReferenceCheckOutcome, } from '@shrkcrft/inspector';
|
|
2
|
+
export function buildKnowledgeAudit(inspection, options = {}) {
|
|
3
|
+
const all = inspection.knowledgeEntries;
|
|
4
|
+
// User-source only: skip pack-contributed entries (they're signed; not our place to audit).
|
|
5
|
+
const userEntries = all.filter((e) => {
|
|
6
|
+
const src = inspection.entrySources?.get(e.id);
|
|
7
|
+
return !src || src.type === 'local';
|
|
8
|
+
});
|
|
9
|
+
const skipped = all
|
|
10
|
+
.filter((e) => !userEntries.includes(e))
|
|
11
|
+
.map((e) => ({
|
|
12
|
+
entryId: e.id,
|
|
13
|
+
reason: 'pack-contributed (out of scope for v1 audit)',
|
|
14
|
+
}));
|
|
15
|
+
const targets = options.entryId
|
|
16
|
+
? userEntries.filter((e) => e.id === options.entryId)
|
|
17
|
+
: userEntries;
|
|
18
|
+
const targetIds = new Set(targets.map((t) => t.id));
|
|
19
|
+
const lint = lintKnowledge(targets, {});
|
|
20
|
+
const stale = options.skipStaleCheck ? null : safeBuildStaleReport(inspection);
|
|
21
|
+
// Group lint findings by entryId.
|
|
22
|
+
const lintByEntry = new Map();
|
|
23
|
+
for (const f of lint.findings) {
|
|
24
|
+
if (!targetIds.has(f.entryId))
|
|
25
|
+
continue;
|
|
26
|
+
const list = lintByEntry.get(f.entryId) ?? [];
|
|
27
|
+
list.push(f);
|
|
28
|
+
lintByEntry.set(f.entryId, list);
|
|
29
|
+
}
|
|
30
|
+
// Group stale reference findings by entryId. Only stale/missing outcomes
|
|
31
|
+
// (the deterministic engine already filters `ok`); promote to findings.
|
|
32
|
+
const staleByEntry = new Map();
|
|
33
|
+
for (const check of stale?.referenceChecks ?? []) {
|
|
34
|
+
if (!targetIds.has(check.entryId))
|
|
35
|
+
continue;
|
|
36
|
+
if (check.outcome === ReferenceCheckOutcome.Ok ||
|
|
37
|
+
check.outcome === ReferenceCheckOutcome.Unknown) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const list = staleByEntry.get(check.entryId) ?? [];
|
|
41
|
+
list.push(check);
|
|
42
|
+
staleByEntry.set(check.entryId, list);
|
|
43
|
+
}
|
|
44
|
+
const entries = [];
|
|
45
|
+
for (const e of targets) {
|
|
46
|
+
const lintFindings = lintByEntry.get(e.id) ?? [];
|
|
47
|
+
const staleFindings = staleByEntry.get(e.id) ?? [];
|
|
48
|
+
const raw = [];
|
|
49
|
+
for (const f of lintFindings) {
|
|
50
|
+
raw.push({
|
|
51
|
+
severity: normaliseLintSeverity(f.severity),
|
|
52
|
+
category: f.code,
|
|
53
|
+
field: f.field,
|
|
54
|
+
message: f.message,
|
|
55
|
+
sources: ['knowledge lint'],
|
|
56
|
+
...(f.stubSuggestion ? { stubSuggestion: f.stubSuggestion } : {}),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (const f of staleFindings) {
|
|
60
|
+
raw.push({
|
|
61
|
+
severity: outcomeToSeverity(f.outcome),
|
|
62
|
+
category: `knowledge-stale.${f.outcome}`,
|
|
63
|
+
field: f.reference.kind ?? 'reference',
|
|
64
|
+
message: f.message,
|
|
65
|
+
sources: ['knowledge stale'],
|
|
66
|
+
...(f.suggestion ? { fixSuggestion: f.suggestion } : {}),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const deterministicFindings = dedupeFindings(raw);
|
|
70
|
+
entries.push({
|
|
71
|
+
entryId: e.id,
|
|
72
|
+
entryType: String(e.type),
|
|
73
|
+
title: e.title ?? e.id,
|
|
74
|
+
verdict: deriveVerdict(deterministicFindings),
|
|
75
|
+
deterministicFindings,
|
|
76
|
+
llmFindings: [],
|
|
77
|
+
suggestedActions: deriveSuggestedActions(e.id, deterministicFindings),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const summary = entries.reduce((acc, e) => {
|
|
81
|
+
acc[e.verdict] += 1;
|
|
82
|
+
acc.total += 1;
|
|
83
|
+
return acc;
|
|
84
|
+
}, { ok: 0, minor: 0, stale: 0, broken: 0, total: 0 });
|
|
85
|
+
const generatedAt = new Date().toISOString();
|
|
86
|
+
return {
|
|
87
|
+
auditId: `audit-${generatedAt.replace(/[:.]/g, '-')}`,
|
|
88
|
+
generatedAt,
|
|
89
|
+
llmEnriched: false,
|
|
90
|
+
llmProviderId: null,
|
|
91
|
+
entries,
|
|
92
|
+
skipped,
|
|
93
|
+
summary,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function safeBuildStaleReport(inspection) {
|
|
97
|
+
try {
|
|
98
|
+
return buildKnowledgeStaleReport(inspection);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// The stale walker touches the filesystem; degrade to no-stale rather than
|
|
102
|
+
// failing the audit when index access throws.
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function normaliseLintSeverity(s) {
|
|
107
|
+
if (s === KnowledgeLintSeverity.Warning)
|
|
108
|
+
return 'warn';
|
|
109
|
+
return 'info';
|
|
110
|
+
}
|
|
111
|
+
function outcomeToSeverity(o) {
|
|
112
|
+
if (o === ReferenceCheckOutcome.Missing)
|
|
113
|
+
return 'error';
|
|
114
|
+
if (o === ReferenceCheckOutcome.Stale)
|
|
115
|
+
return 'warn';
|
|
116
|
+
return 'info';
|
|
117
|
+
}
|
|
118
|
+
function dedupeFindings(raw) {
|
|
119
|
+
const byKey = new Map();
|
|
120
|
+
for (const f of raw) {
|
|
121
|
+
const key = `${f.category}::${f.field}::${f.message}`;
|
|
122
|
+
const existing = byKey.get(key);
|
|
123
|
+
if (!existing) {
|
|
124
|
+
byKey.set(key, { ...f, sources: [...f.sources] });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const sources = Array.from(new Set([...existing.sources, ...f.sources]));
|
|
128
|
+
byKey.set(key, {
|
|
129
|
+
...existing,
|
|
130
|
+
sources,
|
|
131
|
+
severity: maxSeverity(existing.severity, f.severity),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return Array.from(byKey.values()).sort((a, b) => {
|
|
135
|
+
const order = { error: 0, warn: 1, info: 2 };
|
|
136
|
+
return order[a.severity] - order[b.severity] || a.category.localeCompare(b.category);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function maxSeverity(a, b) {
|
|
140
|
+
const rank = { info: 0, warn: 1, error: 2 };
|
|
141
|
+
return rank[a] >= rank[b] ? a : b;
|
|
142
|
+
}
|
|
143
|
+
function deriveVerdict(findings) {
|
|
144
|
+
if (findings.some((f) => f.severity === 'error'))
|
|
145
|
+
return 'broken';
|
|
146
|
+
if (findings.some((f) => f.severity === 'warn'))
|
|
147
|
+
return 'stale';
|
|
148
|
+
if (findings.some((f) => f.severity === 'info'))
|
|
149
|
+
return 'minor';
|
|
150
|
+
return 'ok';
|
|
151
|
+
}
|
|
152
|
+
function deriveSuggestedActions(entryId, findings) {
|
|
153
|
+
const actions = [];
|
|
154
|
+
const missingRef = findings.find((f) => f.category === `knowledge-stale.${ReferenceCheckOutcome.Missing}`);
|
|
155
|
+
const staleRef = findings.find((f) => f.category === `knowledge-stale.${ReferenceCheckOutcome.Stale}`);
|
|
156
|
+
const obsolete = findings.find((f) => f.category.includes('obsolete-entry'));
|
|
157
|
+
const summaryMissing = findings.find((f) => f.category === 'knowledge.summary-missing');
|
|
158
|
+
const hasError = findings.some((f) => f.severity === 'error');
|
|
159
|
+
const hasWarn = findings.some((f) => f.severity === 'warn');
|
|
160
|
+
if (missingRef) {
|
|
161
|
+
actions.push({
|
|
162
|
+
kind: 'edit',
|
|
163
|
+
target: entryId,
|
|
164
|
+
note: 'A referenced symbol/file no longer exists — update the reference or remove it.',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (staleRef) {
|
|
168
|
+
actions.push({
|
|
169
|
+
kind: 'edit',
|
|
170
|
+
target: entryId,
|
|
171
|
+
note: 'A referenced symbol moved or was renamed — update the reference to point at the new location.',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (obsolete) {
|
|
175
|
+
actions.push({
|
|
176
|
+
kind: 'retire',
|
|
177
|
+
target: entryId,
|
|
178
|
+
note: 'Entry classified as obsolete — confirm and remove, or refresh and re-tag.',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (summaryMissing) {
|
|
182
|
+
actions.push({
|
|
183
|
+
kind: 'edit',
|
|
184
|
+
target: entryId,
|
|
185
|
+
note: 'Add a one-sentence summary so consumers can grok the entry without opening it.',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (hasError && actions.length === 0) {
|
|
189
|
+
actions.push({
|
|
190
|
+
kind: 'investigate',
|
|
191
|
+
target: entryId,
|
|
192
|
+
note: 'Entry has error-level findings; review and fix before next consumption.',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (!hasError && hasWarn && actions.length === 0) {
|
|
196
|
+
actions.push({
|
|
197
|
+
kind: 'edit',
|
|
198
|
+
target: entryId,
|
|
199
|
+
note: 'Address warning-level findings to keep the entry aligned with current code.',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return actions;
|
|
203
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type IAiProvider } from '@shrkcrft/ai';
|
|
2
|
+
import type { ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
3
|
+
import type { IKnowledgeFixPlan } from './knowledge-fix-plan.js';
|
|
4
|
+
export interface IEnrichKnowledgeFixPlanOptions {
|
|
5
|
+
provider: IAiProvider;
|
|
6
|
+
inspection: ISharkcraftInspection;
|
|
7
|
+
maxTokensPerEntry?: number;
|
|
8
|
+
onPerEntryError?: (entryId: string, error: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function enrichKnowledgeFixPlanWithLlm(plan: IKnowledgeFixPlan, options: IEnrichKnowledgeFixPlanOptions): Promise<IKnowledgeFixPlan>;
|
|
11
|
+
//# sourceMappingURL=knowledge-fix-plan-llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-fix-plan-llm.d.ts","sourceRoot":"","sources":["../../src/audit/knowledge-fix-plan-llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAA4B,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE3F,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,qBAAqB,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,iBAAiB,CAAC,CAuC5B"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { AiMessageRole } from '@shrkcrft/ai';
|
|
2
|
+
export async function enrichKnowledgeFixPlanWithLlm(plan, options) {
|
|
3
|
+
const byEntry = new Map();
|
|
4
|
+
for (const fix of plan.fixes) {
|
|
5
|
+
const list = byEntry.get(fix.entryId) ?? [];
|
|
6
|
+
list.push(fix);
|
|
7
|
+
byEntry.set(fix.entryId, list);
|
|
8
|
+
}
|
|
9
|
+
const enriched = [];
|
|
10
|
+
for (const [entryId, fixes] of byEntry.entries()) {
|
|
11
|
+
const entry = options.inspection.knowledgeEntries.find((e) => e.id === entryId);
|
|
12
|
+
if (!entry) {
|
|
13
|
+
enriched.push(...fixes);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const messages = buildFixSuggestionMessages(entry, fixes);
|
|
18
|
+
const res = await options.provider.send({
|
|
19
|
+
messages,
|
|
20
|
+
maxTokens: options.maxTokensPerEntry ?? 1024,
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
options.onPerEntryError?.(entryId, res.error);
|
|
24
|
+
enriched.push(...fixes);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const suggestions = parseSuggestionMap(res.value.content);
|
|
28
|
+
for (const fix of fixes) {
|
|
29
|
+
const key = suggestionKey(fix.findingCategory, fix.finding);
|
|
30
|
+
const suggestion = suggestions.get(key) ?? suggestions.get(fix.findingCategory) ?? null;
|
|
31
|
+
enriched.push(suggestion ? { ...fix, llmSuggestion: suggestion } : fix);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
options.onPerEntryError?.(entryId, e);
|
|
36
|
+
enriched.push(...fixes);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { ...plan, fixes: enriched };
|
|
40
|
+
}
|
|
41
|
+
function suggestionKey(category, finding) {
|
|
42
|
+
return `${category}::${finding.slice(0, 80)}`;
|
|
43
|
+
}
|
|
44
|
+
function buildFixSuggestionMessages(entry, fixes) {
|
|
45
|
+
const system = {
|
|
46
|
+
role: AiMessageRole.System,
|
|
47
|
+
content: [
|
|
48
|
+
'You are a senior maintainer sharpening fix recommendations for a SharkCraft knowledge entry.',
|
|
49
|
+
'For each finding listed, the deterministic layer has already produced a sound agent prompt with a generic placeholder.',
|
|
50
|
+
'Replace those placeholders with a CONCRETE recommendation grounded in the supplied entry.',
|
|
51
|
+
'',
|
|
52
|
+
'Per finding category, prefer to emit:',
|
|
53
|
+
' - knowledge.summary-missing → a concrete one-sentence summary in single quotes.',
|
|
54
|
+
' - knowledge.tags-missing → 1–3 concrete kebab-case tags as an array.',
|
|
55
|
+
' - knowledge.title-missing → a concrete short title in single quotes.',
|
|
56
|
+
' - knowledge-stale.stale → name the likely replacement symbol/path if you can ground it.',
|
|
57
|
+
' - knowledge-stale.missing → confirm removal or name a likely replacement.',
|
|
58
|
+
' - any other category → a one-sentence concrete suggestion.',
|
|
59
|
+
'',
|
|
60
|
+
'Return ONLY a JSON object with this exact shape, no preface, no fences:',
|
|
61
|
+
'{',
|
|
62
|
+
' "suggestions": [',
|
|
63
|
+
' {',
|
|
64
|
+
' "findingCategory": "<exact category>",',
|
|
65
|
+
' "finding": "<first 80 chars of the original finding message>",',
|
|
66
|
+
' "suggestion": "<one to three sentences, concrete>"',
|
|
67
|
+
' }',
|
|
68
|
+
' ]',
|
|
69
|
+
'}',
|
|
70
|
+
'Omit entries you cannot meaningfully sharpen — better silence than ceremony.',
|
|
71
|
+
].join('\n'),
|
|
72
|
+
};
|
|
73
|
+
const user = {
|
|
74
|
+
role: AiMessageRole.User,
|
|
75
|
+
content: [
|
|
76
|
+
`# Knowledge entry: ${entry.id}`,
|
|
77
|
+
`type: ${String(entry.type)}`,
|
|
78
|
+
`title: ${entry.title ?? '(none)'}`,
|
|
79
|
+
`summary: ${entry.summary ?? '(none)'}`,
|
|
80
|
+
`tags: ${(entry.tags ?? []).join(', ') || '(none)'}`,
|
|
81
|
+
``,
|
|
82
|
+
`## Content (truncated)`,
|
|
83
|
+
'```',
|
|
84
|
+
truncate(String(entry.content ?? '(no content)'), 3072),
|
|
85
|
+
'```',
|
|
86
|
+
``,
|
|
87
|
+
`## Fixes needing sharpening`,
|
|
88
|
+
fixes
|
|
89
|
+
.map((f, i) => `### Fix ${i + 1}: ${f.findingCategory}\nfinding: ${f.finding}\ncurrent agent prompt:\n${f.agentPrompt}`)
|
|
90
|
+
.join('\n\n'),
|
|
91
|
+
].join('\n'),
|
|
92
|
+
};
|
|
93
|
+
return [system, user];
|
|
94
|
+
}
|
|
95
|
+
function truncate(s, max) {
|
|
96
|
+
if (s.length <= max)
|
|
97
|
+
return s;
|
|
98
|
+
return `${s.slice(0, max)}\n… [truncated ${s.length - max} chars]`;
|
|
99
|
+
}
|
|
100
|
+
function parseSuggestionMap(raw) {
|
|
101
|
+
const out = new Map();
|
|
102
|
+
const trimmed = raw.trim();
|
|
103
|
+
let jsonText = trimmed;
|
|
104
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
105
|
+
if (fenced)
|
|
106
|
+
jsonText = fenced[1].trim();
|
|
107
|
+
let parsed;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(jsonText);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
const first = jsonText.indexOf('{');
|
|
113
|
+
const last = jsonText.lastIndexOf('}');
|
|
114
|
+
if (first < 0 || last <= first)
|
|
115
|
+
return out;
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(jsonText.slice(first, last + 1));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!parsed || typeof parsed !== 'object')
|
|
124
|
+
return out;
|
|
125
|
+
const list = parsed.suggestions;
|
|
126
|
+
if (!Array.isArray(list))
|
|
127
|
+
return out;
|
|
128
|
+
for (const item of list) {
|
|
129
|
+
if (!item || typeof item !== 'object')
|
|
130
|
+
continue;
|
|
131
|
+
const obj = item;
|
|
132
|
+
const category = typeof obj.findingCategory === 'string' ? obj.findingCategory.trim() : '';
|
|
133
|
+
const finding = typeof obj.finding === 'string' ? obj.finding.trim() : '';
|
|
134
|
+
const suggestion = typeof obj.suggestion === 'string' ? obj.suggestion.trim() : '';
|
|
135
|
+
if (!category || !suggestion)
|
|
136
|
+
continue;
|
|
137
|
+
const key = finding ? suggestionKey(category, finding) : category;
|
|
138
|
+
out.set(key, suggestion);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IKnowledgeAuditReport, KnowledgeAuditFindingSeverity } from './knowledge-audit.js';
|
|
2
|
+
export type KnowledgeFixConfidence = 'high' | 'medium' | 'low';
|
|
3
|
+
export interface IKnowledgeFixInstruction {
|
|
4
|
+
entryId: string;
|
|
5
|
+
findingCategory: string;
|
|
6
|
+
finding: string;
|
|
7
|
+
severity: KnowledgeAuditFindingSeverity;
|
|
8
|
+
intent: string;
|
|
9
|
+
agentPrompt: string;
|
|
10
|
+
confidence: KnowledgeFixConfidence;
|
|
11
|
+
source: 'deterministic' | 'llm';
|
|
12
|
+
llmSuggestion?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IKnowledgeSkippedFinding {
|
|
15
|
+
entryId: string;
|
|
16
|
+
findingCategory: string;
|
|
17
|
+
finding: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
export interface IKnowledgeFixPlan {
|
|
21
|
+
fixPlanId: string;
|
|
22
|
+
generatedAt: string;
|
|
23
|
+
auditId: string;
|
|
24
|
+
/**
|
|
25
|
+
* Knowledge entries are sourced from many files (config-driven). The plan
|
|
26
|
+
* points the agent to the registered config so they can find each entry's
|
|
27
|
+
* literal source by id.
|
|
28
|
+
*/
|
|
29
|
+
sourceHint: string;
|
|
30
|
+
fixes: readonly IKnowledgeFixInstruction[];
|
|
31
|
+
skipped: readonly IKnowledgeSkippedFinding[];
|
|
32
|
+
summary: {
|
|
33
|
+
fixCount: number;
|
|
34
|
+
highConfidence: number;
|
|
35
|
+
mediumConfidence: number;
|
|
36
|
+
lowConfidence: number;
|
|
37
|
+
skipped: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export declare function buildKnowledgeFixPlan(report: IKnowledgeAuditReport): IKnowledgeFixPlan;
|
|
41
|
+
//# sourceMappingURL=knowledge-fix-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-fix-plan.d.ts","sourceRoot":"","sources":["../../src/audit/knowledge-fix-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,qBAAqB,EAErB,6BAA6B,EAC9B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,6BAA6B,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,eAAe,GAAG,KAAK,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC3C,OAAO,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC7C,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAKD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,iBAAiB,CAiCtF"}
|