@shrkcrft/cli 0.1.0-alpha.12 → 0.1.0-alpha.14
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/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/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +10 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +40 -2
- package/dist/commands/smart-context.command.d.ts +28 -0
- package/dist/commands/smart-context.command.d.ts.map +1 -1
- package/dist/commands/smart-context.command.js +762 -1
- 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/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/main.d.ts.map +1 -1
- package/dist/main.js +40 -18
- package/package.json +32 -32
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const TEMPLATES_FILE = 'sharkcraft/templates.ts';
|
|
2
|
+
export function buildFixPlan(report) {
|
|
3
|
+
const fixes = [];
|
|
4
|
+
const skipped = [];
|
|
5
|
+
for (const entry of report.templates) {
|
|
6
|
+
for (const f of entry.deterministicFindings) {
|
|
7
|
+
const out = dispatchDeterministic(entry, f);
|
|
8
|
+
if (out.kind === 'fix')
|
|
9
|
+
fixes.push(out.fix);
|
|
10
|
+
else
|
|
11
|
+
skipped.push(out.skip);
|
|
12
|
+
}
|
|
13
|
+
for (const f of entry.llmFindings) {
|
|
14
|
+
fixes.push(makeLlmFix(entry, f));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const summary = {
|
|
18
|
+
fixCount: fixes.length,
|
|
19
|
+
highConfidence: fixes.filter((f) => f.confidence === 'high').length,
|
|
20
|
+
mediumConfidence: fixes.filter((f) => f.confidence === 'medium').length,
|
|
21
|
+
lowConfidence: fixes.filter((f) => f.confidence === 'low').length,
|
|
22
|
+
skipped: skipped.length,
|
|
23
|
+
};
|
|
24
|
+
const generatedAt = new Date().toISOString();
|
|
25
|
+
return {
|
|
26
|
+
fixPlanId: `fix-${generatedAt.replace(/[:.]/g, '-')}`,
|
|
27
|
+
generatedAt,
|
|
28
|
+
auditId: report.auditId,
|
|
29
|
+
sourceFiles: [TEMPLATES_FILE],
|
|
30
|
+
fixes,
|
|
31
|
+
skipped,
|
|
32
|
+
summary,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function dispatchDeterministic(entry, f) {
|
|
36
|
+
switch (f.category) {
|
|
37
|
+
case 'unsafe-target':
|
|
38
|
+
return {
|
|
39
|
+
kind: 'skip',
|
|
40
|
+
skip: {
|
|
41
|
+
templateId: entry.templateId,
|
|
42
|
+
findingCategory: f.category,
|
|
43
|
+
finding: f.message,
|
|
44
|
+
reason: 'security-sensitive — requires human review',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
case 'missing-name':
|
|
48
|
+
return makeFix(entry, f, 'high', 'Add a `name` field to this template.', [
|
|
49
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
50
|
+
`Add a \`name: '<human-readable name>'\` field next to \`id\`.`,
|
|
51
|
+
`The name should briefly describe what the template scaffolds (e.g. "CLI command (shrk subcommand)").`,
|
|
52
|
+
`Do not change other fields. Verify the file still parses.`,
|
|
53
|
+
].join('\n'));
|
|
54
|
+
case 'missing-description':
|
|
55
|
+
return makeFix(entry, f, 'high', 'Add a `description` field to this template.', [
|
|
56
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
57
|
+
`Add a \`description: '<one-sentence description>'\` field. The description appears in \`shrk templates list\` and should explain what the template generates.`,
|
|
58
|
+
`Do not change other fields. Verify the file still parses.`,
|
|
59
|
+
].join('\n'));
|
|
60
|
+
case 'related-id-unresolved': {
|
|
61
|
+
const id = extractQuoted(f.message);
|
|
62
|
+
if (!id) {
|
|
63
|
+
return makeFix(entry, f, 'medium', 'Remove an unresolved related id (id could not be auto-extracted from the message).', [
|
|
64
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
65
|
+
`The finding is: "${f.message}"`,
|
|
66
|
+
`Remove the unresolved related id from the \`related\` array. Do not invent a replacement; if no real id applies, leave the array empty or omit the field.`,
|
|
67
|
+
`Verify the file still parses.`,
|
|
68
|
+
].join('\n'));
|
|
69
|
+
}
|
|
70
|
+
return makeFix(entry, f, 'high', `Remove "${id}" from the \`related\` array.`, [
|
|
71
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
72
|
+
`In its \`related\` array, remove the string "${id}". Do not change any other entries.`,
|
|
73
|
+
`Verify the file still parses and that "${id}" is not referenced elsewhere in the file.`,
|
|
74
|
+
].join('\n'));
|
|
75
|
+
}
|
|
76
|
+
case 'undocumented-var': {
|
|
77
|
+
const varName = extractQuoted(f.message);
|
|
78
|
+
const where = varName ? `the variable named "${varName}"` : 'the variable referenced in the finding';
|
|
79
|
+
return makeFix(entry, f, 'medium', varName
|
|
80
|
+
? `Add a description to variable "${varName}".`
|
|
81
|
+
: 'Add a description to an undocumented variable.', [
|
|
82
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
83
|
+
`Locate ${where} inside the \`variables\` array.`,
|
|
84
|
+
`Add a \`description: '<short explanation>'\` field. The description should explain what the variable controls and where its value ends up in the generated output.`,
|
|
85
|
+
`Verify the file still parses.`,
|
|
86
|
+
].join('\n'));
|
|
87
|
+
}
|
|
88
|
+
case 'required-var-no-example': {
|
|
89
|
+
const varName = extractQuoted(f.message);
|
|
90
|
+
const where = varName ? `the variable named "${varName}"` : 'the required variable referenced in the finding';
|
|
91
|
+
return makeFix(entry, f, 'medium', varName
|
|
92
|
+
? `Add an example value to required variable "${varName}".`
|
|
93
|
+
: 'Add an example value to a required variable that lacks one.', [
|
|
94
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
95
|
+
`Locate ${where} inside the \`variables\` array.`,
|
|
96
|
+
`Add an \`examples: ['<sample value>']\` field. Choose a sample that's representative of real usage — readable by humans, valid against any \`pattern\` that may also be defined.`,
|
|
97
|
+
`Verify the file still parses.`,
|
|
98
|
+
].join('\n'));
|
|
99
|
+
}
|
|
100
|
+
case 'undeclared-var': {
|
|
101
|
+
const varName = extractPlaceholder(f.message);
|
|
102
|
+
const where = varName ? `\`{{${varName}}}\`` : 'the undeclared placeholder named in the finding';
|
|
103
|
+
return makeFix(entry, f, 'medium', varName
|
|
104
|
+
? `Resolve undeclared placeholder ${where}.`
|
|
105
|
+
: 'Resolve an undeclared placeholder in the template body.', [
|
|
106
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
107
|
+
`The template body uses placeholder ${where} that isn't declared in \`variables[]\`.`,
|
|
108
|
+
`Pick one:`,
|
|
109
|
+
` (a) Add a corresponding entry to \`variables[]\` (with \`name\`, \`required\`, and ideally a \`description\` + \`examples\`). This is right if the placeholder represents intended user input.`,
|
|
110
|
+
` (b) Remove the placeholder from the template body. This is right if it was a typo or leftover.`,
|
|
111
|
+
`Choose based on what the surrounding body and template description imply. Verify the file still parses.`,
|
|
112
|
+
].join('\n'));
|
|
113
|
+
}
|
|
114
|
+
case 'path-no-convention': {
|
|
115
|
+
const samplePath = extractQuoted(f.message);
|
|
116
|
+
return makeFix(entry, f, 'low', samplePath
|
|
117
|
+
? `Align template targetPath with path conventions (sample: "${samplePath}").`
|
|
118
|
+
: 'Align template targetPath with path conventions.', [
|
|
119
|
+
`The sample target path${samplePath ? ` "${samplePath}"` : ''} doesn't match any entry in \`sharkcraft/paths.ts\`.`,
|
|
120
|
+
`Decide between two fixes:`,
|
|
121
|
+
` (a) Update the template's \`targetPath\` so its rendered output matches an existing path convention. Edit ${TEMPLATES_FILE}, find template "${entry.templateId}", and adjust its \`targetPath\` function/string.`,
|
|
122
|
+
` (b) Add a matching entry to \`sharkcraft/paths.ts\` if this template's output really does belong in a new location.`,
|
|
123
|
+
`Prefer (a) unless the template represents a genuinely new file-shape for the project. Verify both files parse.`,
|
|
124
|
+
].join('\n'));
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
return makeFix(entry, f, 'low', `Address finding "${f.category}".`, [
|
|
128
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
129
|
+
`The audit reported: ${f.message}`,
|
|
130
|
+
f.suggestion ? `Suggested fix from the inspector: ${f.suggestion}` : 'No specific suggestion was supplied — use judgment.',
|
|
131
|
+
`Apply a minimal change that resolves the finding without touching unrelated fields. Verify the file still parses.`,
|
|
132
|
+
].filter(Boolean).join('\n'));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function makeFix(entry, f, confidence, intent, agentPrompt) {
|
|
136
|
+
return {
|
|
137
|
+
kind: 'fix',
|
|
138
|
+
fix: {
|
|
139
|
+
templateId: entry.templateId,
|
|
140
|
+
findingCategory: f.category,
|
|
141
|
+
finding: f.message,
|
|
142
|
+
severity: f.severity,
|
|
143
|
+
intent,
|
|
144
|
+
agentPrompt,
|
|
145
|
+
confidence,
|
|
146
|
+
source: 'deterministic',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function makeLlmFix(entry, f) {
|
|
151
|
+
return {
|
|
152
|
+
templateId: entry.templateId,
|
|
153
|
+
findingCategory: f.category,
|
|
154
|
+
finding: f.message,
|
|
155
|
+
severity: f.severity,
|
|
156
|
+
intent: `Review the LLM-flagged "${f.category}" finding and decide whether to act.`,
|
|
157
|
+
agentPrompt: [
|
|
158
|
+
`Open ${TEMPLATES_FILE}. Find the template literal with id "${entry.templateId}".`,
|
|
159
|
+
`An LLM critique flagged (confidence ${f.confidence.toFixed(2)}): ${f.message}`,
|
|
160
|
+
`LLM findings are advisory — verify against the template body and sibling templates before acting.`,
|
|
161
|
+
`If you choose to act, keep the change minimal and scoped to the finding. If you don't, that's also a valid outcome — record the decision in your response.`,
|
|
162
|
+
].join('\n'),
|
|
163
|
+
confidence: 'low',
|
|
164
|
+
source: 'llm',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function extractQuoted(message) {
|
|
168
|
+
const m = message.match(/"([^"]+)"/);
|
|
169
|
+
return m ? m[1] : null;
|
|
170
|
+
}
|
|
171
|
+
function extractPlaceholder(message) {
|
|
172
|
+
const m = message.match(/\{\{\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*\}\}/);
|
|
173
|
+
return m ? m[1] : null;
|
|
174
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* `shrk ai-status` — one-shot self-check of the LLM wiring.
|
|
4
|
+
*
|
|
5
|
+
* Designed for agents (like Claude) that want to know whether shrk's
|
|
6
|
+
* LLM enrichment is going to do anything useful, without having to
|
|
7
|
+
* run a full audit. Three pieces of information:
|
|
8
|
+
* 1. Which provider, if any, the local-first walk would pick.
|
|
9
|
+
* 2. The structured `ai` block (the same one every audit emits) so
|
|
10
|
+
* the agent can act on the hints.
|
|
11
|
+
* 3. Optional `--ping`: a tiny live request that proves the
|
|
12
|
+
* provider really responds (the resolver only checks env vars).
|
|
13
|
+
*
|
|
14
|
+
* The deterministic baseline contract: works without LLM. With no
|
|
15
|
+
* provider reachable, the output is still useful — it carries the
|
|
16
|
+
* setup hints.
|
|
17
|
+
*/
|
|
18
|
+
export declare const aiStatusCommand: ICommandHandler;
|
|
19
|
+
//# sourceMappingURL=ai-status.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-status.command.d.ts","sourceRoot":"","sources":["../../src/commands/ai-status.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,EAAE,eAkD7B,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AiMessageRole, buildAiBlock, renderAiBlockMarkdown, selectAiProvider, } from '@shrkcrft/ai';
|
|
2
|
+
import { flagBool, flagNumber, flagString, } from "../command-registry.js";
|
|
3
|
+
import { asJson, header, kv } from "../output/format-output.js";
|
|
4
|
+
/**
|
|
5
|
+
* `shrk ai-status` — one-shot self-check of the LLM wiring.
|
|
6
|
+
*
|
|
7
|
+
* Designed for agents (like Claude) that want to know whether shrk's
|
|
8
|
+
* LLM enrichment is going to do anything useful, without having to
|
|
9
|
+
* run a full audit. Three pieces of information:
|
|
10
|
+
* 1. Which provider, if any, the local-first walk would pick.
|
|
11
|
+
* 2. The structured `ai` block (the same one every audit emits) so
|
|
12
|
+
* the agent can act on the hints.
|
|
13
|
+
* 3. Optional `--ping`: a tiny live request that proves the
|
|
14
|
+
* provider really responds (the resolver only checks env vars).
|
|
15
|
+
*
|
|
16
|
+
* The deterministic baseline contract: works without LLM. With no
|
|
17
|
+
* provider reachable, the output is still useful — it carries the
|
|
18
|
+
* setup hints.
|
|
19
|
+
*/
|
|
20
|
+
export const aiStatusCommand = {
|
|
21
|
+
name: 'ai-status',
|
|
22
|
+
description: 'Report which AI provider shrk would use right now, with setup or upgrade hints. `--ping` verifies the provider actually responds. Read-only.',
|
|
23
|
+
usage: 'shrk ai-status [--provider auto|ollama|llamacpp|claude|gemini] [--ping] [--ping-timeout <ms>] [--json]',
|
|
24
|
+
async run(args) {
|
|
25
|
+
const providerKind = flagString(args, 'provider');
|
|
26
|
+
const json = flagBool(args, 'json');
|
|
27
|
+
const wantPing = flagBool(args, 'ping');
|
|
28
|
+
const pingTimeoutMs = flagNumber(args, 'ping-timeout') ?? 8000;
|
|
29
|
+
const selection = selectAiProvider(providerKind);
|
|
30
|
+
const ai = buildAiBlock({ selection, userOptedOut: false });
|
|
31
|
+
let ping = null;
|
|
32
|
+
if (wantPing) {
|
|
33
|
+
ping = selection.provider
|
|
34
|
+
? await pingProvider(selection.provider, pingTimeoutMs)
|
|
35
|
+
: { ok: false, reason: 'no provider reachable — nothing to ping', elapsedMs: 0 };
|
|
36
|
+
}
|
|
37
|
+
if (json) {
|
|
38
|
+
process.stdout.write(asJson({
|
|
39
|
+
ai,
|
|
40
|
+
...(ping ? { ping } : {}),
|
|
41
|
+
}) + '\n');
|
|
42
|
+
return ai.reachable && (!ping || ping.ok) ? 0 : 1;
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write(header('AI status'));
|
|
45
|
+
process.stdout.write(kv('reachable', ai.reachable ? 'yes' : 'no') + '\n');
|
|
46
|
+
process.stdout.write(kv('requested provider', ai.requestedProvider) + '\n');
|
|
47
|
+
process.stdout.write(kv('resolved provider', ai.providerId ?? '(none)') + '\n');
|
|
48
|
+
if (ping) {
|
|
49
|
+
process.stdout.write(kv('ping', ping.ok
|
|
50
|
+
? `ok in ${ping.elapsedMs}ms` + (ping.model ? ` (model: ${ping.model})` : '')
|
|
51
|
+
: `failed: ${ping.reason}`) + '\n');
|
|
52
|
+
}
|
|
53
|
+
process.stdout.write('\n');
|
|
54
|
+
process.stdout.write(renderAiBlockMarkdown(ai));
|
|
55
|
+
return ai.reachable && (!ping || ping.ok) ? 0 : 1;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
async function pingProvider(provider, timeoutMs) {
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
// Tiny prompt with bounded output — we just want to prove the round-trip works.
|
|
61
|
+
const requestPromise = provider.send({
|
|
62
|
+
messages: [
|
|
63
|
+
{ role: AiMessageRole.System, content: 'Respond with the single word: ok' },
|
|
64
|
+
{ role: AiMessageRole.User, content: 'ping' },
|
|
65
|
+
],
|
|
66
|
+
maxTokens: 8,
|
|
67
|
+
});
|
|
68
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
69
|
+
setTimeout(() => resolve({ ok: false, error: new Error(`timed out after ${timeoutMs}ms`) }), timeoutMs);
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
const res = (await Promise.race([requestPromise, timeoutPromise]));
|
|
73
|
+
const elapsedMs = Date.now() - start;
|
|
74
|
+
if (!('value' in res) || !res.ok) {
|
|
75
|
+
const reason = 'error' in res
|
|
76
|
+
? res.error.message ?? String(res.error)
|
|
77
|
+
: 'no response';
|
|
78
|
+
return { ok: false, reason, elapsedMs };
|
|
79
|
+
}
|
|
80
|
+
const value = res.value;
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
elapsedMs,
|
|
84
|
+
...(value.model ? { model: value.model } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: e.message,
|
|
91
|
+
elapsedMs: Date.now() - start,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,QAAQ,cAAc;IACtB,iBAAiB,mBAAmB;IACpC,gBAAgB,kBAAkB;IAClC,YAAY,kBAAkB;IAC9B,SAAS,eAAe;IACxB,cAAc,oBAAoB;CACnC;AAED;;;;;;GAMG;AACH,oBAAY,cAAc;IACxB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,8BAA8B;AAC9B,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,EAAE,OAAO;IACT,UAAU,gBAAgB;IAC1B,UAAU,eAAe;CAC1B;AAED;;;;;;GAMG;AACH,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,MAAM,WAAW;CAClB;AAED;;;;;;GAMG;AACH,oBAAY,gBAAgB;IAC1B,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,OAAO,YAAY;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,YAAY,iBAAiB;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,kCAAkC;IAClC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,oBAAoB,
|
|
1
|
+
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -115,6 +115,16 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
115
115
|
intendedAudience: [CommandAudience.Human, CommandAudience.Agent, CommandAudience.Ci],
|
|
116
116
|
taskRole: CommandTaskRole.Diagnose,
|
|
117
117
|
}),
|
|
118
|
+
entry({
|
|
119
|
+
command: 'ai-status',
|
|
120
|
+
description: 'Report which AI provider shrk would use right now, with setup or upgrade hints. `--ping` verifies the provider actually responds.',
|
|
121
|
+
category: 'core',
|
|
122
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
123
|
+
mcpAvailable: false,
|
|
124
|
+
surface: CommandSurface.Common,
|
|
125
|
+
intendedAudience: [CommandAudience.Human, CommandAudience.Agent],
|
|
126
|
+
taskRole: CommandTaskRole.Diagnose,
|
|
127
|
+
}),
|
|
118
128
|
entry({
|
|
119
129
|
command: 'context',
|
|
120
130
|
description: 'Focused context for a task (rules / paths / templates). For "what should I do?" prefer `shrk recommend "<task>"`.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA8OhC,eAAO,MAAM,aAAa,EAAE,eAW3B,CAAC;AA4eF,eAAO,MAAM,qBAAqB,EAAE,eAmCnC,CAAC;AAuDF,eAAO,MAAM,yBAAyB,EAAE,eAavC,CAAC;AAIF,eAAO,MAAM,wBAAwB,EAAE,eA2CtC,CAAC;AAgCF,eAAO,MAAM,6BAA6B,EAAE,eAa3C,CAAC"}
|
|
@@ -10,6 +10,7 @@ import { asJson, header, kv } from "../output/format-output.js";
|
|
|
10
10
|
import { maybeRunInWatchMode } from "../output/watch-loop.js";
|
|
11
11
|
import { doctorHints, renderFailureHints } from "../output/failure-hints.js";
|
|
12
12
|
import { foldDoctorChecks, renderFoldedSummary, DoctorState, } from "../doctor/doctor-tags.js";
|
|
13
|
+
import { enrichWithLlmRecommendations, renderRecommendationsMarkdown, } from '@shrkcrft/ai';
|
|
13
14
|
const SEVERITY_LABEL = {
|
|
14
15
|
[DoctorSeverity.Ok]: 'OK ',
|
|
15
16
|
[DoctorSeverity.Info]: 'INFO ',
|
|
@@ -200,8 +201,8 @@ function renderSemanticIndexCheck(report) {
|
|
|
200
201
|
}
|
|
201
202
|
export const doctorCommand = {
|
|
202
203
|
name: 'doctor',
|
|
203
|
-
description: 'Validate the local SharkCraft setup (config, knowledge, templates, project). `--focus errors|warnings-new|info`, `--hide <category,...>`, `--quiet-known` filter the headline view using `sharkcraft/doctor.suppressions.json`. `--watch`/`--once`/`--debounce` for live mode. `--explain-quality` shows the per-warning "why this matters" line so warnings stop being permanent yellow noise. `--blockers` shows only must-fix findings (errors + warning-category in {config-invalid, pack-signature-invalid, plan-signature-divergent, asset-load-failed}); exit code is non-zero iff a blocker remains. Subcommands: `suppress`, `suppressions list|check`, `watch`.',
|
|
204
|
-
usage: 'shrk [--cwd <dir>] doctor [--no-config] [--json] [--strict[=errors|warnings|all]] [--blockers] [--show-advisory] [--min-score <0-100>] [--focus errors,warnings-new,info] [--hide action-hint-quality,...] [--quiet-known] [--explain-quality] [--watch [--once] [--debounce N]]',
|
|
204
|
+
description: 'Validate the local SharkCraft setup (config, knowledge, templates, project). `--focus errors|warnings-new|info`, `--hide <category,...>`, `--quiet-known` filter the headline view using `sharkcraft/doctor.suppressions.json`. `--watch`/`--once`/`--debounce` for live mode. `--explain-quality` shows the per-warning "why this matters" line so warnings stop being permanent yellow noise. `--blockers` shows only must-fix findings (errors + warning-category in {config-invalid, pack-signature-invalid, plan-signature-divergent, asset-load-failed}); exit code is non-zero iff a blocker remains. `--llm-recommendations` layers a local-LLM-derived list of concrete next-steps onto the deterministic output (no-op when no provider is reachable). Subcommands: `suppress`, `suppressions list|check`, `watch`.',
|
|
205
|
+
usage: 'shrk [--cwd <dir>] doctor [--no-config] [--json] [--strict[=errors|warnings|all]] [--blockers] [--show-advisory] [--min-score <0-100>] [--focus errors,warnings-new,info] [--hide action-hint-quality,...] [--quiet-known] [--explain-quality] [--llm-recommendations] [--provider auto|ollama|llamacpp] [--watch [--once] [--debounce N]]',
|
|
205
206
|
async run(args) {
|
|
206
207
|
const watchExit = await maybeRunInWatchMode(args, runDoctorOnce);
|
|
207
208
|
if (watchExit !== null)
|
|
@@ -296,6 +297,19 @@ async function doctorCommandImpl(args) {
|
|
|
296
297
|
return !isSharkcraftMissing;
|
|
297
298
|
});
|
|
298
299
|
}
|
|
300
|
+
// Optional LLM enrichment: never alters the deterministic emission below;
|
|
301
|
+
// only appended at the end. No-op when the flag is off or no provider is
|
|
302
|
+
// reachable — keeps the deterministic baseline byte-stable.
|
|
303
|
+
const wantLlmRecs = flagBool(args, 'llm-recommendations');
|
|
304
|
+
const llmEnvelope = wantLlmRecs
|
|
305
|
+
? await enrichWithLlmRecommendations({
|
|
306
|
+
surface: 'doctor',
|
|
307
|
+
deterministicSummary: summariseDoctorChecks(visibleChecks),
|
|
308
|
+
providerKind: flagString(args, 'provider') ?? undefined,
|
|
309
|
+
ask: 'For each warning or error, propose ONE concrete next-step the user can execute from a shell — name the `shrk` subcommand, file path, or config key. If a finding has no useful next-step, skip it.',
|
|
310
|
+
maxTokens: 1024,
|
|
311
|
+
})
|
|
312
|
+
: null;
|
|
299
313
|
const ackExpired = ackSummary.expired.length > 0 && failOnExpiredAcknowledgement;
|
|
300
314
|
// Under --no-config + missing sharkcraft, treat the run as advisory: do not
|
|
301
315
|
// red-fail on the inspector's "no sharkcraft" errors / warnings.
|
|
@@ -388,6 +402,7 @@ async function doctorCommandImpl(args) {
|
|
|
388
402
|
})),
|
|
389
403
|
...result,
|
|
390
404
|
...(filtered ? { filtered } : {}),
|
|
405
|
+
...(llmEnvelope ? { llmRecommendations: llmEnvelope } : {}),
|
|
391
406
|
}) + '\n');
|
|
392
407
|
return overallExitCode;
|
|
393
408
|
}
|
|
@@ -590,8 +605,31 @@ async function doctorCommandImpl(args) {
|
|
|
590
605
|
if (previewEligible) {
|
|
591
606
|
process.stdout.write('\nDraft patch available — run `shrk fix preview` for a preview-only patch under `.sharkcraft/fixes/`.\n');
|
|
592
607
|
}
|
|
608
|
+
if (llmEnvelope) {
|
|
609
|
+
process.stdout.write('\n');
|
|
610
|
+
process.stdout.write(renderRecommendationsMarkdown(llmEnvelope));
|
|
611
|
+
}
|
|
593
612
|
return overallExitCode;
|
|
594
613
|
}
|
|
614
|
+
function summariseDoctorChecks(checks) {
|
|
615
|
+
const lines = [];
|
|
616
|
+
const order = [DoctorSeverity.Error, DoctorSeverity.Warning, DoctorSeverity.Info, DoctorSeverity.Ok];
|
|
617
|
+
for (const sev of order) {
|
|
618
|
+
const grouped = checks.filter((c) => c.severity === sev);
|
|
619
|
+
if (grouped.length === 0)
|
|
620
|
+
continue;
|
|
621
|
+
const label = SEVERITY_LABEL[sev].trim();
|
|
622
|
+
lines.push(`## ${label} (${grouped.length})`);
|
|
623
|
+
for (const c of grouped) {
|
|
624
|
+
const fix = c.recommendedFix ?? c.fix;
|
|
625
|
+
lines.push(`- **${c.title}**${c.category ? ` (${c.category})` : ''}: ${c.message}${fix ? ` — suggested fix: ${fix}` : ''}`);
|
|
626
|
+
}
|
|
627
|
+
lines.push('');
|
|
628
|
+
}
|
|
629
|
+
if (lines.length === 0)
|
|
630
|
+
lines.push('(no findings — all checks passed)');
|
|
631
|
+
return lines.join('\n');
|
|
632
|
+
}
|
|
595
633
|
export const doctorSuppressCommand = {
|
|
596
634
|
name: 'suppress',
|
|
597
635
|
description: 'Add a doctor finding to sharkcraft/doctor.suppressions.json. Requires --reason.',
|
|
@@ -23,6 +23,34 @@ export declare const smartContextPlanAheadCommand: ICommandHandler;
|
|
|
23
23
|
export declare const smartContextListCommand: ICommandHandler;
|
|
24
24
|
/** `shrk smart-context show <slug>` — print a saved entry. */
|
|
25
25
|
export declare const smartContextShowCommand: ICommandHandler;
|
|
26
|
+
/**
|
|
27
|
+
* `shrk smart-context audit-templates` — local-LLM template audit.
|
|
28
|
+
*
|
|
29
|
+
* Orchestrates the existing deterministic template inspectors
|
|
30
|
+
* (`templates lint` + `templates drift`), dedupes their overlap by
|
|
31
|
+
* (category + message), and — when a local provider is reachable —
|
|
32
|
+
* runs an LLM critique pass per template. Always report-only: no edits
|
|
33
|
+
* to template sources, no plan emission. See
|
|
34
|
+
* docs/smart-context-audit-templates.md for the report contract.
|
|
35
|
+
*/
|
|
36
|
+
export declare const smartContextAuditTemplatesCommand: ICommandHandler;
|
|
37
|
+
/**
|
|
38
|
+
* `shrk smart-context audit-knowledge` — local-LLM knowledge audit.
|
|
39
|
+
*
|
|
40
|
+
* Wraps `lintKnowledge` + `buildKnowledgeStaleReport` from `@shrkcrft/inspector`,
|
|
41
|
+
* then layers LLM critique (when a provider is reachable) and emits a
|
|
42
|
+
* Claude-targetable fix plan. Report-only — no writes to knowledge sources.
|
|
43
|
+
* See docs/smart-context-audit-templates.md for the shared report contract.
|
|
44
|
+
*/
|
|
45
|
+
export declare const smartContextAuditKnowledgeCommand: ICommandHandler;
|
|
46
|
+
/**
|
|
47
|
+
* `shrk smart-context audit-pipelines` — local-LLM pipeline audit.
|
|
48
|
+
*
|
|
49
|
+
* Wraps `lintPipelines` from `@shrkcrft/inspector`, optionally layers
|
|
50
|
+
* LLM critique, and emits a Claude-targetable fix plan. Same report-only
|
|
51
|
+
* contract as audit-templates / audit-knowledge.
|
|
52
|
+
*/
|
|
53
|
+
export declare const smartContextAuditPipelinesCommand: ICommandHandler;
|
|
26
54
|
/** `shrk smart-context embeddings build` — (re)build the semantic index. */
|
|
27
55
|
export declare const smartContextEmbeddingsBuildCommand: ICommandHandler;
|
|
28
56
|
/** `shrk smart-context embeddings-status` — freshness report (no model load). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"AAuBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"AAuBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAyDhC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,EAAE,eA+JjC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,EAAE,eAmF1C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,uBAAuB,EAAE,eAwBrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,EAAE,eAkCrC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAmH/C,CAAC;AA2JF;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAoH/C,CAAC;AA2JF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAgG/C,CAAC;AAiMF,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,EAAE,eAuHhD,CAAC;AAMF,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,EAAE,eAsCjD,CAAC"}
|