@shrkcrft/cli 0.1.0-alpha.12 → 0.1.0-alpha.13

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.
Files changed (50) hide show
  1. package/dist/audit/knowledge-audit-llm.d.ts +19 -0
  2. package/dist/audit/knowledge-audit-llm.d.ts.map +1 -0
  3. package/dist/audit/knowledge-audit-llm.js +164 -0
  4. package/dist/audit/knowledge-audit.d.ts +61 -0
  5. package/dist/audit/knowledge-audit.d.ts.map +1 -0
  6. package/dist/audit/knowledge-audit.js +203 -0
  7. package/dist/audit/knowledge-fix-plan-llm.d.ts +11 -0
  8. package/dist/audit/knowledge-fix-plan-llm.d.ts.map +1 -0
  9. package/dist/audit/knowledge-fix-plan-llm.js +141 -0
  10. package/dist/audit/knowledge-fix-plan.d.ts +41 -0
  11. package/dist/audit/knowledge-fix-plan.d.ts.map +1 -0
  12. package/dist/audit/knowledge-fix-plan.js +125 -0
  13. package/dist/audit/pipeline-audit-llm.d.ts +11 -0
  14. package/dist/audit/pipeline-audit-llm.d.ts.map +1 -0
  15. package/dist/audit/pipeline-audit-llm.js +134 -0
  16. package/dist/audit/pipeline-audit.d.ts +69 -0
  17. package/dist/audit/pipeline-audit.d.ts.map +1 -0
  18. package/dist/audit/pipeline-audit.js +166 -0
  19. package/dist/audit/templates-audit-llm.d.ts +19 -0
  20. package/dist/audit/templates-audit-llm.d.ts.map +1 -0
  21. package/dist/audit/templates-audit-llm.js +207 -0
  22. package/dist/audit/templates-audit.d.ts +63 -0
  23. package/dist/audit/templates-audit.d.ts.map +1 -0
  24. package/dist/audit/templates-audit.js +171 -0
  25. package/dist/audit/templates-fix-plan-llm.d.ts +19 -0
  26. package/dist/audit/templates-fix-plan-llm.d.ts.map +1 -0
  27. package/dist/audit/templates-fix-plan-llm.js +162 -0
  28. package/dist/audit/templates-fix-plan.d.ts +37 -0
  29. package/dist/audit/templates-fix-plan.d.ts.map +1 -0
  30. package/dist/audit/templates-fix-plan.js +174 -0
  31. package/dist/commands/ai-status.command.d.ts +19 -0
  32. package/dist/commands/ai-status.command.d.ts.map +1 -0
  33. package/dist/commands/ai-status.command.js +94 -0
  34. package/dist/commands/command-catalog.d.ts.map +1 -1
  35. package/dist/commands/command-catalog.js +10 -0
  36. package/dist/commands/doctor.command.d.ts.map +1 -1
  37. package/dist/commands/doctor.command.js +40 -2
  38. package/dist/commands/smart-context.command.d.ts +28 -0
  39. package/dist/commands/smart-context.command.d.ts.map +1 -1
  40. package/dist/commands/smart-context.command.js +762 -1
  41. package/dist/commands/surface.command.d.ts +1 -0
  42. package/dist/commands/surface.command.d.ts.map +1 -1
  43. package/dist/commands/surface.command.js +10 -3
  44. package/dist/commands/template-quality.command.d.ts.map +1 -1
  45. package/dist/commands/template-quality.command.js +39 -3
  46. package/dist/commands/templates.command.d.ts.map +1 -1
  47. package/dist/commands/templates.command.js +37 -2
  48. package/dist/main.d.ts.map +1 -1
  49. package/dist/main.js +40 -18
  50. 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,EAo+FzD,CAAC;AAEH,4DAA4D;AAC5D,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAWpD;AA0DD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAgGjE,CAAC;AAEH,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAItE;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,GAAG,SAAS,eAAe,EAAE,CAKnF;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,GAAG,eAAe,GAAG,SAAS,CAEpF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,oBAAoB,GAAG,gBAAgB,CAQ1E;AAqFD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAclE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAwC9D;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,wBAAwB,IAAI,SAAS,uBAAuB,EAAE,CAsB7E;AAED,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,SAAS,uBAAuB,EAAE,GACvC,MAAM,CAaR"}
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;AAyOhC,eAAO,MAAM,aAAa,EAAE,eAW3B,CAAC;AA+bF,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"}
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;AA6BhC;;;;;;;;;;;;;;;;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,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,EAAE,eAgHhD,CAAC;AAMF,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,EAAE,eAsCjD,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"}