@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.
Files changed (228) 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/command-registry.d.ts +28 -0
  32. package/dist/command-registry.d.ts.map +1 -1
  33. package/dist/command-registry.js +91 -1
  34. package/dist/commands/ai-status.command.d.ts +19 -0
  35. package/dist/commands/ai-status.command.d.ts.map +1 -0
  36. package/dist/commands/ai-status.command.js +94 -0
  37. package/dist/commands/api-diff.command.d.ts +11 -0
  38. package/dist/commands/api-diff.command.d.ts.map +1 -0
  39. package/dist/commands/api-diff.command.js +144 -0
  40. package/dist/commands/apply.command.d.ts.map +1 -1
  41. package/dist/commands/apply.command.js +10 -2
  42. package/dist/commands/arch.command.d.ts +9 -0
  43. package/dist/commands/arch.command.d.ts.map +1 -0
  44. package/dist/commands/arch.command.js +186 -0
  45. package/dist/commands/ask.command.d.ts.map +1 -1
  46. package/dist/commands/ask.command.js +10 -9
  47. package/dist/commands/cache-align.command.d.ts +12 -0
  48. package/dist/commands/cache-align.command.d.ts.map +1 -0
  49. package/dist/commands/cache-align.command.js +78 -0
  50. package/dist/commands/check.command.d.ts.map +1 -1
  51. package/dist/commands/check.command.js +26 -2
  52. package/dist/commands/code-intel.command.d.ts +18 -0
  53. package/dist/commands/code-intel.command.d.ts.map +1 -0
  54. package/dist/commands/code-intel.command.js +146 -0
  55. package/dist/commands/codemod.command.d.ts.map +1 -1
  56. package/dist/commands/codemod.command.js +27 -6
  57. package/dist/commands/command-catalog.d.ts +15 -3
  58. package/dist/commands/command-catalog.d.ts.map +1 -1
  59. package/dist/commands/command-catalog.js +407 -34
  60. package/dist/commands/commands.command.d.ts.map +1 -1
  61. package/dist/commands/commands.command.js +4 -4
  62. package/dist/commands/completion.command.d.ts +10 -0
  63. package/dist/commands/completion.command.d.ts.map +1 -0
  64. package/dist/commands/completion.command.js +121 -0
  65. package/dist/commands/compress.command.d.ts +8 -0
  66. package/dist/commands/compress.command.d.ts.map +1 -0
  67. package/dist/commands/compress.command.js +147 -0
  68. package/dist/commands/constructs.command.d.ts.map +1 -1
  69. package/dist/commands/constructs.command.js +89 -23
  70. package/dist/commands/context.command.d.ts.map +1 -1
  71. package/dist/commands/context.command.js +121 -1
  72. package/dist/commands/contract-gate.command.d.ts.map +1 -1
  73. package/dist/commands/contract-gate.command.js +5 -1
  74. package/dist/commands/delegate.command.d.ts +65 -0
  75. package/dist/commands/delegate.command.d.ts.map +1 -0
  76. package/dist/commands/delegate.command.js +657 -0
  77. package/dist/commands/deps-audit.command.d.ts +23 -0
  78. package/dist/commands/deps-audit.command.d.ts.map +1 -0
  79. package/dist/commands/deps-audit.command.js +270 -0
  80. package/dist/commands/dev.command.d.ts.map +1 -1
  81. package/dist/commands/dev.command.js +5 -2
  82. package/dist/commands/diff-check.command.d.ts +30 -0
  83. package/dist/commands/diff-check.command.d.ts.map +1 -0
  84. package/dist/commands/diff-check.command.js +210 -0
  85. package/dist/commands/doctor.command.d.ts.map +1 -1
  86. package/dist/commands/doctor.command.js +162 -10
  87. package/dist/commands/export.command.d.ts.map +1 -1
  88. package/dist/commands/export.command.js +76 -3
  89. package/dist/commands/framework.command.d.ts +12 -0
  90. package/dist/commands/framework.command.d.ts.map +1 -0
  91. package/dist/commands/framework.command.js +180 -0
  92. package/dist/commands/gate.command.d.ts +15 -0
  93. package/dist/commands/gate.command.d.ts.map +1 -0
  94. package/dist/commands/gate.command.js +300 -0
  95. package/dist/commands/gen.command.d.ts.map +1 -1
  96. package/dist/commands/gen.command.js +13 -1
  97. package/dist/commands/graph-code-subverbs.d.ts +33 -0
  98. package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
  99. package/dist/commands/graph-code-subverbs.js +1385 -0
  100. package/dist/commands/graph.command.d.ts.map +1 -1
  101. package/dist/commands/graph.command.js +31 -2
  102. package/dist/commands/help.command.d.ts +4 -3
  103. package/dist/commands/help.command.d.ts.map +1 -1
  104. package/dist/commands/help.command.js +86 -18
  105. package/dist/commands/helper.command.js +1 -1
  106. package/dist/commands/impact.command.d.ts.map +1 -1
  107. package/dist/commands/impact.command.js +171 -1
  108. package/dist/commands/import.command.d.ts.map +1 -1
  109. package/dist/commands/import.command.js +121 -5
  110. package/dist/commands/ingest.command.d.ts.map +1 -1
  111. package/dist/commands/ingest.command.js +5 -1
  112. package/dist/commands/init.command.d.ts.map +1 -1
  113. package/dist/commands/init.command.js +174 -7
  114. package/dist/commands/knowledge-author.command.d.ts.map +1 -1
  115. package/dist/commands/knowledge-author.command.js +9 -0
  116. package/dist/commands/knowledge-propose.command.d.ts.map +1 -1
  117. package/dist/commands/knowledge-propose.command.js +4 -2
  118. package/dist/commands/knowledge.command.d.ts.map +1 -1
  119. package/dist/commands/knowledge.command.js +26 -3
  120. package/dist/commands/migrate.command.d.ts +13 -0
  121. package/dist/commands/migrate.command.d.ts.map +1 -0
  122. package/dist/commands/migrate.command.js +152 -0
  123. package/dist/commands/move-plan.command.d.ts +23 -0
  124. package/dist/commands/move-plan.command.d.ts.map +1 -0
  125. package/dist/commands/move-plan.command.js +360 -0
  126. package/dist/commands/packs-new.d.ts +1 -1
  127. package/dist/commands/packs-new.d.ts.map +1 -1
  128. package/dist/commands/packs-new.js +5 -36
  129. package/dist/commands/packs.command.d.ts.map +1 -1
  130. package/dist/commands/packs.command.js +2 -10
  131. package/dist/commands/plan-context.command.d.ts +11 -0
  132. package/dist/commands/plan-context.command.d.ts.map +1 -0
  133. package/dist/commands/plan-context.command.js +85 -0
  134. package/dist/commands/preflight.command.d.ts.map +1 -1
  135. package/dist/commands/preflight.command.js +15 -0
  136. package/dist/commands/profiles.command.js +4 -4
  137. package/dist/commands/recommend.command.d.ts +6 -0
  138. package/dist/commands/recommend.command.d.ts.map +1 -1
  139. package/dist/commands/recommend.command.js +119 -5
  140. package/dist/commands/release.command.js +13 -13
  141. package/dist/commands/rule-graph-subverbs.d.ts +3 -0
  142. package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
  143. package/dist/commands/rule-graph-subverbs.js +132 -0
  144. package/dist/commands/rules.command.d.ts.map +1 -1
  145. package/dist/commands/rules.command.js +20 -3
  146. package/dist/commands/scaffold-validate.command.d.ts +22 -0
  147. package/dist/commands/scaffold-validate.command.d.ts.map +1 -0
  148. package/dist/commands/scaffold-validate.command.js +215 -0
  149. package/dist/commands/search-structural.command.d.ts +18 -0
  150. package/dist/commands/search-structural.command.d.ts.map +1 -0
  151. package/dist/commands/search-structural.command.js +376 -0
  152. package/dist/commands/search.command.js +1 -1
  153. package/dist/commands/smart-context.command.d.ts +67 -0
  154. package/dist/commands/smart-context.command.d.ts.map +1 -0
  155. package/dist/commands/smart-context.command.js +4728 -0
  156. package/dist/commands/spike.command.d.ts +22 -0
  157. package/dist/commands/spike.command.d.ts.map +1 -0
  158. package/dist/commands/spike.command.js +235 -0
  159. package/dist/commands/surface.command.d.ts +1 -0
  160. package/dist/commands/surface.command.d.ts.map +1 -1
  161. package/dist/commands/surface.command.js +10 -3
  162. package/dist/commands/task-context.command.d.ts.map +1 -1
  163. package/dist/commands/task-context.command.js +5 -17
  164. package/dist/commands/task.command.d.ts.map +1 -1
  165. package/dist/commands/task.command.js +8 -2
  166. package/dist/commands/template-quality.command.d.ts.map +1 -1
  167. package/dist/commands/template-quality.command.js +39 -3
  168. package/dist/commands/templates.command.d.ts.map +1 -1
  169. package/dist/commands/templates.command.js +37 -2
  170. package/dist/commands/tests.command.d.ts.map +1 -1
  171. package/dist/commands/tests.command.js +13 -2
  172. package/dist/commands/watch.command.d.ts +26 -0
  173. package/dist/commands/watch.command.d.ts.map +1 -0
  174. package/dist/commands/watch.command.js +456 -0
  175. package/dist/dashboard/code-intelligence-data.d.ts +33 -0
  176. package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
  177. package/dist/dashboard/code-intelligence-data.js +329 -0
  178. package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
  179. package/dist/dashboard/dashboard-api-server.js +256 -2
  180. package/dist/dashboard/knowledge-ask.d.ts +4 -0
  181. package/dist/dashboard/knowledge-ask.d.ts.map +1 -0
  182. package/dist/dashboard/knowledge-ask.js +112 -0
  183. package/dist/env/load-dotenv.d.ts +15 -0
  184. package/dist/env/load-dotenv.d.ts.map +1 -0
  185. package/dist/env/load-dotenv.js +70 -0
  186. package/dist/export/claude-commands-export.d.ts +60 -0
  187. package/dist/export/claude-commands-export.d.ts.map +1 -0
  188. package/dist/export/claude-commands-export.js +276 -0
  189. package/dist/export/export-formats.d.ts +1 -1
  190. package/dist/export/export-formats.d.ts.map +1 -1
  191. package/dist/export/export-formats.js +139 -12
  192. package/dist/index.d.ts +3 -0
  193. package/dist/index.d.ts.map +1 -1
  194. package/dist/index.js +3 -0
  195. package/dist/init/init-templates.d.ts.map +1 -1
  196. package/dist/init/init-templates.js +133 -113
  197. package/dist/init/paths-advisory.d.ts +20 -0
  198. package/dist/init/paths-advisory.d.ts.map +1 -0
  199. package/dist/init/paths-advisory.js +88 -0
  200. package/dist/main.d.ts.map +1 -1
  201. package/dist/main.js +331 -17
  202. package/dist/output/ccr-store-config.d.ts +18 -0
  203. package/dist/output/ccr-store-config.d.ts.map +1 -0
  204. package/dist/output/ccr-store-config.js +41 -0
  205. package/dist/output/format-output.d.ts.map +1 -1
  206. package/dist/output/format-output.js +6 -1
  207. package/dist/output/output-compression.d.ts +15 -0
  208. package/dist/output/output-compression.d.ts.map +1 -0
  209. package/dist/output/output-compression.js +60 -0
  210. package/dist/output/resolve-compress-type.d.ts +22 -0
  211. package/dist/output/resolve-compress-type.d.ts.map +1 -0
  212. package/dist/output/resolve-compress-type.js +21 -0
  213. package/dist/output/watch-loop.d.ts +9 -1
  214. package/dist/output/watch-loop.d.ts.map +1 -1
  215. package/dist/output/watch-loop.js +13 -3
  216. package/dist/schemas/json-schemas.d.ts +384 -36
  217. package/dist/schemas/json-schemas.d.ts.map +1 -1
  218. package/dist/schemas/json-schemas.js +247 -36
  219. package/dist/surface/profiles.d.ts.map +1 -1
  220. package/dist/surface/profiles.js +54 -10
  221. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  222. package/dist/surface/surface-config-writer.js +23 -11
  223. package/dist/validation/run-validation-loop.d.ts.map +1 -1
  224. package/dist/validation/run-validation-loop.js +5 -1
  225. package/package.json +35 -21
  226. package/dist/commands/plugin.command.d.ts +0 -11
  227. package/dist/commands/plugin.command.d.ts.map +0 -1
  228. package/dist/commands/plugin.command.js +0 -394
@@ -0,0 +1,22 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ /**
3
+ * `shrk spike <slug>` — turn a saved smart-context plan into starter
4
+ * files for the recommended MVP.
5
+ *
6
+ * Reads `.sharkcraft/smart-context/<slug>.plan.json` (written by
7
+ * smart-context when --save + focused/ai-plan succeeded), grabs
8
+ * `firstSpike.proposedFiles[]`, and writes a placeholder file at each
9
+ * proposed path that does not already exist. The placeholders carry a
10
+ * short header derived from `purpose` plus the plan's
11
+ * `schemaOutline` when present, so the human opening the file knows
12
+ * why it exists and what shape to fill in.
13
+ *
14
+ * Safety:
15
+ * - Never overwrites an existing file.
16
+ * - Refuses to write outside the workspace.
17
+ * - `--dry-run` prints what would happen.
18
+ * - `--force` is intentionally absent — a re-spike should mean
19
+ * "delete then rerun" so the user gets a fresh prompt about it.
20
+ */
21
+ export declare const spikeCommand: ICommandHandler;
22
+ //# sourceMappingURL=spike.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spike.command.d.ts","sourceRoot":"","sources":["../../src/commands/spike.command.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAuBhC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,EAAE,eAsI1B,CAAC"}
@@ -0,0 +1,235 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { flagBool, resolveCwd, } from "../command-registry.js";
4
+ import { asJson, header, kv } from "../output/format-output.js";
5
+ const SMART_CONTEXT_DIR = nodePath.join('.sharkcraft', 'smart-context');
6
+ /**
7
+ * `shrk spike <slug>` — turn a saved smart-context plan into starter
8
+ * files for the recommended MVP.
9
+ *
10
+ * Reads `.sharkcraft/smart-context/<slug>.plan.json` (written by
11
+ * smart-context when --save + focused/ai-plan succeeded), grabs
12
+ * `firstSpike.proposedFiles[]`, and writes a placeholder file at each
13
+ * proposed path that does not already exist. The placeholders carry a
14
+ * short header derived from `purpose` plus the plan's
15
+ * `schemaOutline` when present, so the human opening the file knows
16
+ * why it exists and what shape to fill in.
17
+ *
18
+ * Safety:
19
+ * - Never overwrites an existing file.
20
+ * - Refuses to write outside the workspace.
21
+ * - `--dry-run` prints what would happen.
22
+ * - `--force` is intentionally absent — a re-spike should mean
23
+ * "delete then rerun" so the user gets a fresh prompt about it.
24
+ */
25
+ export const spikeCommand = {
26
+ name: 'spike',
27
+ description: 'Scaffold starter files for a saved smart-context plan\'s recommended MVP. Reads .sharkcraft/smart-context/<slug>.plan.json.',
28
+ usage: 'shrk spike <slug> [--dry-run] [--json]',
29
+ async run(args) {
30
+ const slug = args.positional[0]?.trim();
31
+ if (!slug) {
32
+ process.stderr.write('Usage: shrk spike <slug> [--dry-run] [--json]\n');
33
+ process.stderr.write(' (slug comes from `shrk smart-context list`)\n');
34
+ return 2;
35
+ }
36
+ const cwd = resolveCwd(args);
37
+ const json = flagBool(args, 'json');
38
+ const dryRun = flagBool(args, 'dry-run');
39
+ const planPath = resolvePlanPath(cwd, slug);
40
+ if (!planPath) {
41
+ process.stderr.write(`No structured plan found for slug "${slug}". Looked under .sharkcraft/smart-context/.\n` +
42
+ `Tip: run smart-context with --save and a structured-output mode (--ai-plan or --focused --plan) first.\n`);
43
+ return 1;
44
+ }
45
+ let plan;
46
+ try {
47
+ plan = JSON.parse(readFileSync(planPath, 'utf8'));
48
+ }
49
+ catch (e) {
50
+ process.stderr.write(`Failed to parse ${planPath}: ${e.message}\n`);
51
+ return 1;
52
+ }
53
+ const proposed = plan.firstSpike?.proposedFiles ?? [];
54
+ if (proposed.length === 0) {
55
+ process.stderr.write(`Plan at ${planPath} has no firstSpike.proposedFiles[]. Re-run smart-context with --focused --plan (architecture tasks fill this in).\n`);
56
+ return 1;
57
+ }
58
+ const items = [];
59
+ for (const entry of proposed) {
60
+ const raw = entry?.path?.trim();
61
+ if (!raw || raw.length === 0)
62
+ continue;
63
+ // Refuse placeholder-y paths like `.../<timestamp>.json`.
64
+ if (/[<>{}]/.test(raw)) {
65
+ items.push({ path: raw, absolute: '', action: 'skip-unsafe', reason: 'placeholder syntax' });
66
+ continue;
67
+ }
68
+ const safe = safeJoin(cwd, raw);
69
+ if (!safe) {
70
+ items.push({ path: raw, absolute: '', action: 'skip-unsafe', reason: 'escapes workspace' });
71
+ continue;
72
+ }
73
+ if (existsSync(safe)) {
74
+ items.push({ path: raw, absolute: safe, action: 'skip-exists' });
75
+ continue;
76
+ }
77
+ items.push({ path: raw, absolute: safe, action: 'create' });
78
+ }
79
+ const willCreate = items.filter((i) => i.action === 'create');
80
+ const skipExists = items.filter((i) => i.action === 'skip-exists');
81
+ const skipUnsafe = items.filter((i) => i.action === 'skip-unsafe');
82
+ if (!dryRun) {
83
+ for (const item of willCreate) {
84
+ const purpose = proposed.find((p) => p.path === item.path)?.purpose ?? '';
85
+ const body = renderStarter({
86
+ path: item.path,
87
+ purpose,
88
+ plan,
89
+ });
90
+ mkdirSync(nodePath.dirname(item.absolute), { recursive: true });
91
+ writeFileSync(item.absolute, body, 'utf8');
92
+ }
93
+ }
94
+ if (json) {
95
+ process.stdout.write(asJson({
96
+ slug,
97
+ planPath,
98
+ dryRun,
99
+ created: willCreate.map((i) => i.path),
100
+ skippedExisting: skipExists.map((i) => i.path),
101
+ skippedUnsafe: skipUnsafe.map((i) => ({ path: i.path, reason: i.reason })),
102
+ proposedCommand: plan.firstSpike?.proposedCommand ?? null,
103
+ }) + '\n');
104
+ return 0;
105
+ }
106
+ process.stdout.write(header(`Spike for: ${slug}`));
107
+ process.stdout.write(kv('plan', planPath) + '\n');
108
+ if (plan.recommendedMvp?.architectureName) {
109
+ process.stdout.write(kv('mvp', plan.recommendedMvp.architectureName) + '\n');
110
+ }
111
+ process.stdout.write('\n');
112
+ if (willCreate.length > 0) {
113
+ process.stdout.write(`Files ${dryRun ? 'that would be created' : 'created'} (${willCreate.length}):\n`);
114
+ for (const item of willCreate) {
115
+ const purpose = proposed.find((p) => p.path === item.path)?.purpose ?? '';
116
+ process.stdout.write(` + ${item.path}${purpose ? ` — ${purpose}` : ''}\n`);
117
+ }
118
+ }
119
+ else {
120
+ process.stdout.write('Nothing new to create.\n');
121
+ }
122
+ if (skipExists.length > 0) {
123
+ process.stdout.write(`\nSkipped (already exist):\n`);
124
+ for (const item of skipExists)
125
+ process.stdout.write(` · ${item.path}\n`);
126
+ }
127
+ if (skipUnsafe.length > 0) {
128
+ process.stdout.write(`\nSkipped (unsafe):\n`);
129
+ for (const item of skipUnsafe) {
130
+ process.stdout.write(` ! ${item.path} — ${item.reason ?? 'unsafe'}\n`);
131
+ }
132
+ }
133
+ if (plan.firstSpike?.proposedCommand) {
134
+ process.stdout.write(`\nNext: run \`${plan.firstSpike.proposedCommand}\`\n`);
135
+ }
136
+ if (plan.firstSpike?.successCriteria && plan.firstSpike.successCriteria.length > 0) {
137
+ process.stdout.write(`\nSuccess criteria:\n`);
138
+ for (const c of plan.firstSpike.successCriteria)
139
+ process.stdout.write(` - ${c}\n`);
140
+ }
141
+ return 0;
142
+ },
143
+ };
144
+ function resolvePlanPath(cwd, slug) {
145
+ const dir = nodePath.join(cwd, SMART_CONTEXT_DIR);
146
+ if (!existsSync(dir))
147
+ return null;
148
+ // Exact match first.
149
+ const exact = nodePath.join(dir, `${slug}.plan.json`);
150
+ if (existsSync(exact))
151
+ return exact;
152
+ // Otherwise tolerate <slug>-plan.plan.json and similar suffix patterns.
153
+ try {
154
+ for (const name of readdirSync(dir)) {
155
+ if (!name.endsWith('.plan.json'))
156
+ continue;
157
+ const base = name.replace(/\.plan\.json$/, '');
158
+ if (base === slug || base.startsWith(slug)) {
159
+ return nodePath.join(dir, name);
160
+ }
161
+ }
162
+ }
163
+ catch {
164
+ // ignore
165
+ }
166
+ return null;
167
+ }
168
+ function safeJoin(cwd, candidate) {
169
+ const normalised = candidate.replace(/\\/g, '/').replace(/^\.\//, '');
170
+ if (nodePath.isAbsolute(normalised)) {
171
+ // Permit only if inside cwd.
172
+ const resolved = nodePath.resolve(normalised);
173
+ if (!resolved.startsWith(cwd + nodePath.sep) && resolved !== cwd)
174
+ return null;
175
+ return resolved;
176
+ }
177
+ const resolved = nodePath.resolve(cwd, normalised);
178
+ if (!resolved.startsWith(cwd + nodePath.sep))
179
+ return null;
180
+ return resolved;
181
+ }
182
+ function renderStarter(input) {
183
+ const ext = nodePath.extname(input.path).toLowerCase();
184
+ const commentOpen = pickCommentOpen(ext);
185
+ const lines = [];
186
+ const taskOrSummary = input.plan.summary ?? input.plan.task ?? 'smart-context spike';
187
+ lines.push(`${commentOpen} Spike scaffold: ${input.path}`);
188
+ if (input.purpose)
189
+ lines.push(`${commentOpen} Purpose: ${input.purpose}`);
190
+ lines.push(`${commentOpen} Generated by \`shrk spike\` from a smart-context plan.`);
191
+ lines.push(`${commentOpen} Plan summary: ${taskOrSummary}`);
192
+ const mvp = input.plan.recommendedMvp?.architectureName;
193
+ if (mvp)
194
+ lines.push(`${commentOpen} MVP: ${mvp}`);
195
+ lines.push(`${commentOpen} Replace this header with the real implementation.`);
196
+ if (input.plan.firstSpike?.schemaOutline !== undefined && (ext === '.json' || ext === '.md')) {
197
+ lines.push('');
198
+ if (ext === '.json') {
199
+ const outlined = typeof input.plan.firstSpike.schemaOutline === 'string'
200
+ ? input.plan.firstSpike.schemaOutline
201
+ : JSON.stringify(input.plan.firstSpike.schemaOutline, null, 2);
202
+ lines.push(outlined);
203
+ }
204
+ else {
205
+ lines.push('```json');
206
+ lines.push(typeof input.plan.firstSpike.schemaOutline === 'string'
207
+ ? input.plan.firstSpike.schemaOutline
208
+ : JSON.stringify(input.plan.firstSpike.schemaOutline, null, 2));
209
+ lines.push('```');
210
+ }
211
+ }
212
+ // For .ts/.tsx files leave a TODO line so the file is syntactically valid.
213
+ if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx') {
214
+ lines.push('');
215
+ lines.push('// TODO(spike): implement.');
216
+ lines.push('export {};');
217
+ }
218
+ return lines.join('\n') + '\n';
219
+ }
220
+ function pickCommentOpen(ext) {
221
+ switch (ext) {
222
+ case '.json':
223
+ return '//'; // not technically valid JSON; we replace this body below for .json files
224
+ case '.md':
225
+ return '<!--';
226
+ case '.yml':
227
+ case '.yaml':
228
+ return '#';
229
+ case '.sh':
230
+ return '#';
231
+ default:
232
+ return '//';
233
+ }
234
+ }
235
+ void statSync;
@@ -10,6 +10,7 @@ import { type ICommandHandler } from '../command-registry.js';
10
10
  * - unhide <command> reverse hide
11
11
  * - reset clear surface.enabled + surface.hidden
12
12
  * - explain <command> why this command has its current tier
13
+ * - profiles [get <id>] list surface profiles (or show one)
13
14
  */
14
15
  export declare const surfaceCommand: ICommandHandler;
15
16
  //# sourceMappingURL=surface.command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"surface.command.d.ts","sourceRoot":"","sources":["../../src/commands/surface.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiBhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,EAAE,eAmC5B,CAAC"}
1
+ {"version":3,"file":"surface.command.d.ts","sourceRoot":"","sources":["../../src/commands/surface.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiBhC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,EAAE,eAmC5B,CAAC"}
@@ -15,11 +15,12 @@ import { applySurfaceEdit, defaultConfigFile, planSurfaceEdit, } from "../surfac
15
15
  * - unhide <command> reverse hide
16
16
  * - reset clear surface.enabled + surface.hidden
17
17
  * - explain <command> why this command has its current tier
18
+ * - profiles [get <id>] list surface profiles (or show one)
18
19
  */
19
20
  export const surfaceCommand = {
20
21
  name: 'surface',
21
22
  description: 'Inspect or change the adaptive command surface (core / extended / experimental tiers).',
22
- usage: 'shrk surface <list|enable|disable|hide|unhide|reset|explain> [name] [--write] [--json]',
23
+ usage: 'shrk surface <list|enable|disable|hide|unhide|reset|explain|profiles> [name] [--write] [--json]',
23
24
  async run(args) {
24
25
  const [verb, ...rest] = args.positional;
25
26
  const json = flagBool(args, 'json');
@@ -84,7 +85,7 @@ async function runProfiles(opts) {
84
85
  const found = availableProfiles.find((p) => p.id === opts.target);
85
86
  if (!found) {
86
87
  process.stderr.write(`Unknown profile: ${opts.target}\n`);
87
- return 1;
88
+ return 2;
88
89
  }
89
90
  if (opts.json) {
90
91
  process.stdout.write(asJson(found) + '\n');
@@ -171,10 +172,16 @@ async function runExplain({ cwd, json, target }) {
171
172
  process.stderr.write('Usage: shrk surface explain <command>\n');
172
173
  return 2;
173
174
  }
174
- const { context, activeProfile } = await loadSurfaceContext({ cwd });
175
+ const { context, activeProfile, availableProfiles } = await loadSurfaceContext({ cwd });
175
176
  const summary = buildSurfaceSummary(context);
176
177
  const view = findCommandInSummary(summary, target);
177
178
  if (!view) {
179
+ // A common mix-up: `surface explain <profile>`. `explain` is for commands;
180
+ // point the user at the profile-aware verb instead of a bare "unknown".
181
+ if (availableProfiles.some((p) => p.id === target)) {
182
+ process.stderr.write(`'${target}' is a surface profile, not a command. Try: shrk surface profiles get ${target}\n`);
183
+ return 2;
184
+ }
178
185
  process.stderr.write(`Unknown command: ${target}\n`);
179
186
  return 2;
180
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/task-context.command.ts"],"names":[],"mappings":"AAsBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,qBAAqB,EAAE,eAyEnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAmEnC,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,eA2DjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAoBlC,CAAC"}
1
+ {"version":3,"file":"task-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/task-context.command.ts"],"names":[],"mappings":"AAuBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,qBAAqB,EAAE,eAyEnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAsEnC,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,eA2DjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAoBlC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import * as nodePath from 'node:path';
3
+ import { cliCommandNameSet } from "./command-catalog.js";
3
4
  import { analyzeImportGraph, buildAgentBrief, buildContradictionReport, buildGeneratedCodeReport, buildRepositoryKnowledgeModel, buildTaskPacket, buildTaskRiskReport, classifyChangeIntent, detectLanguageProfiles, getChangedFiles, getStatusSummary, inspectSharkcraft, isGitRepo, listConstructs, loadRepositoryMemory, } from '@shrkcrft/inspector';
4
5
  import { flagBool, flagString, flagList, resolveCwd, } from "../command-registry.js";
5
6
  import { asJson, header, kv } from "../output/format-output.js";
@@ -102,7 +103,10 @@ export const validateChangeCommand = {
102
103
  }
103
104
  // Fallback path through getStatusSummary not necessary — getChangedFiles already covers it.
104
105
  void getStatusSummary;
105
- const contradictions = buildContradictionReport({ inspection });
106
+ const contradictions = buildContradictionReport({
107
+ inspection,
108
+ cliCommandNames: cliCommandNameSet(),
109
+ });
106
110
  const generated = buildGeneratedCodeReport({ inspection });
107
111
  const generatedPaths = new Set(generated.generatedFiles.map((f) => f.path));
108
112
  const boundaryHits = changed.filter((f) => looksLikeBoundaryViolation(f));
@@ -399,22 +403,6 @@ async function collectLikelyFilesV2(input) {
399
403
  }
400
404
  }
401
405
  }
402
- // Boost files that sit on registered plugin-lifecycle profile barrels.
403
- try {
404
- const { listPluginLifecycleProfiles } = await import('@shrkcrft/inspector');
405
- const profiles = await listPluginLifecycleProfiles(inspection);
406
- for (const entry of profiles) {
407
- for (const b of entry.profile.barrels ?? []) {
408
- for (const f of [...scoreByPath.keys()]) {
409
- if (f.includes(b.path))
410
- bump(f, 3, `lifecycle profile barrel: ${b.id}`);
411
- }
412
- }
413
- }
414
- }
415
- catch {
416
- // Profile registry unavailable — skip the boost.
417
- }
418
406
  // 10) Tests — files that look like they test the matched files.
419
407
  const tests = [];
420
408
  for (const f of [...scoreByPath.keys()]) {
@@ -1 +1 @@
1
- {"version":3,"file":"task.command.d.ts","sourceRoot":"","sources":["../../src/commands/task.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA2DhC,eAAO,MAAM,WAAW,EAAE,eA8PzB,CAAC"}
1
+ {"version":3,"file":"task.command.d.ts","sourceRoot":"","sources":["../../src/commands/task.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA2DhC,eAAO,MAAM,WAAW,EAAE,eAoQzB,CAAC"}
@@ -58,8 +58,8 @@ function minimalTaskPacket(p) {
58
58
  }
59
59
  export const taskCommand = {
60
60
  name: 'task',
61
- description: 'Build an AI-ready task packet: relevant context, action hints, recommended pipeline, templates, paths, verification commands. Pass `--next` to skip the packet and survey the workspace for the highest-leverage next action.',
62
- usage: 'shrk [--cwd <dir>] task "<task>" [--max-tokens 4000] [--scope x,y] [--explain-ranking] [--json] [--compact] OR shrk task --next [--json]',
61
+ description: 'Build an AI-ready task packet: relevant context, action hints, recommended pipeline, templates, paths, verification commands. Defaults to a compact packet (top-5 rules / top-3 templates / 5 hints per field) to keep agent token cost low. Pass `--full` to get the unrestricted packet, or `--next` to skip the packet and survey the workspace for the highest-leverage next action.',
62
+ usage: 'shrk [--cwd <dir>] task "<task>" [--max-tokens 4000] [--scope x,y] [--explain-ranking] [--full] [--json] OR shrk task --next [--json]',
63
63
  async run(args) {
64
64
  const positional = args.positional;
65
65
  // `--next` short-circuits the packet build. Survey doctor / lint /
@@ -142,10 +142,16 @@ export const taskCommand = {
142
142
  const maxTokens = flagNumber(args, 'max-tokens') ?? 3500;
143
143
  const scope = flagList(args, 'scope');
144
144
  const explainRanking = flagBool(args, 'explain-ranking') || flagBool(args, 'json');
145
+ // `--full` (or `--verbose`, which already affects text rendering) opts
146
+ // out of the compact packet — pass it when an agent genuinely needs
147
+ // the full ranking + uncapped action-hint aggregates. Default is the
148
+ // tight packet (5 rules / 3 templates / 5 hints per field).
149
+ const compact = !flagBool(args, 'full') && !flagBool(args, 'verbose');
145
150
  const packet = buildTaskPacket(inspection, task, {
146
151
  maxTokens,
147
152
  ...(scope.length ? { scope } : {}),
148
153
  explainRanking,
154
+ compact,
149
155
  });
150
156
  // Uncertainty summary always computed; coverage gaps when requested.
151
157
  const uncertainty = buildUncertaintySummary(packet);
@@ -1 +1 @@
1
- {"version":3,"file":"template-quality.command.d.ts","sourceRoot":"","sources":["../../src/commands/template-quality.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,oBAAoB,EAAE,eAkClC,CAAC;AAqDF,eAAO,MAAM,oBAAoB,EAAE,eAmBlC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAuBtC,CAAC"}
1
+ {"version":3,"file":"template-quality.command.d.ts","sourceRoot":"","sources":["../../src/commands/template-quality.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,oBAAoB,EAAE,eAsDlC,CAAC;AA4EF,eAAO,MAAM,oBAAoB,EAAE,eAmBlC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAuBtC,CAAC"}
@@ -3,18 +3,32 @@ import * as nodePath from 'node:path';
3
3
  import { inspectSharkcraft, lintTemplates, testTemplates, } from '@shrkcrft/inspector';
4
4
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
5
5
  import { asJson, header } from "../output/format-output.js";
6
+ import { enrichWithLlmRecommendations, renderRecommendationsMarkdown, } from '@shrkcrft/ai';
6
7
  export const templatesLintCommand = {
7
8
  name: 'lint',
8
- description: 'Lint registered templates (titles, vars, target safety, placeholders). --fix-preview emits a TODO patch per finding under .sharkcraft/fixes/templates-lint/ (preview only — never mutates source).',
9
- usage: 'shrk templates lint [<id>] [--fix-preview] [--json]',
9
+ description: 'Lint registered templates (titles, vars, target safety, placeholders). --fix-preview emits a TODO patch per finding under .sharkcraft/fixes/templates-lint/ (preview only — never mutates source). --llm-recommendations layers concrete next-steps onto the deterministic findings when a local LLM is reachable.',
10
+ usage: 'shrk templates lint [<id>] [--fix-preview] [--json] [--llm-recommendations] [--provider auto|ollama|llamacpp]',
10
11
  async run(args) {
11
12
  const cwd = resolveCwd(args);
12
13
  const inspection = await inspectSharkcraft({ cwd });
13
14
  const ids = args.positional.length > 0 ? args.positional : undefined;
14
15
  const report = lintTemplates(inspection, ids);
15
16
  const fixPreview = flagBool(args, 'fix-preview');
17
+ const wantLlm = flagBool(args, 'llm-recommendations');
18
+ const llmEnvelope = wantLlm
19
+ ? await enrichWithLlmRecommendations({
20
+ surface: 'templates-lint',
21
+ deterministicSummary: summariseLintResults(report.results),
22
+ providerKind: flagString(args, 'provider') ?? undefined,
23
+ ask: 'For each non-passing template, suggest ONE concrete edit in sharkcraft/templates.ts: name the field (description, variables[i].examples, related[i], etc.) and the literal value or removal. Skip templates with only `info`-level issues unless a clear improvement exists.',
24
+ maxTokens: 1024,
25
+ })
26
+ : null;
16
27
  if (flagBool(args, 'json')) {
17
- process.stdout.write(asJson(report) + '\n');
28
+ process.stdout.write(asJson({
29
+ ...report,
30
+ ...(llmEnvelope ? { llmRecommendations: llmEnvelope } : {}),
31
+ }) + '\n');
18
32
  if (fixPreview)
19
33
  writeFixPreviewPatches(cwd, report.results);
20
34
  return report.summary.errors > 0 ? 1 : 0;
@@ -35,9 +49,31 @@ export const templatesLintCommand = {
35
49
  process.stdout.write('\nNo fix-preview patches needed.\n');
36
50
  }
37
51
  }
52
+ if (llmEnvelope) {
53
+ process.stdout.write('\n');
54
+ process.stdout.write(renderRecommendationsMarkdown(llmEnvelope));
55
+ }
38
56
  return report.summary.errors > 0 ? 1 : 0;
39
57
  },
40
58
  };
59
+ function summariseLintResults(results) {
60
+ const lines = [];
61
+ for (const r of results) {
62
+ lines.push(`## ${r.passed ? 'OK' : 'FAIL'} ${r.templateId}`);
63
+ if (r.issues.length === 0) {
64
+ lines.push('(no issues)');
65
+ }
66
+ else {
67
+ for (const i of r.issues) {
68
+ lines.push(`- ${i.severity} \`${i.code}\` — ${i.message}`);
69
+ }
70
+ }
71
+ lines.push('');
72
+ }
73
+ if (lines.length === 0)
74
+ lines.push('(no templates registered)');
75
+ return lines.join('\n');
76
+ }
41
77
  /** Write a per-template TODO patch under `.sharkcraft/fixes/templates-lint/`. */
42
78
  function writeFixPreviewPatches(cwd, results) {
43
79
  const dir = nodePath.join(cwd, '.sharkcraft', 'fixes', 'templates-lint');
@@ -1 +1 @@
1
- {"version":3,"file":"templates.command.d.ts","sourceRoot":"","sources":["../../src/commands/templates.command.ts"],"names":[],"mappings":"AAyBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,oBAAoB,EAAE,eA6DlC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAmBlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA+CjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAgBpC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAiDrC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAWnC,CAAC;AAyKF,eAAO,MAAM,2BAA2B,EAAE,eA+BzC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAOnC,CAAC;AA2GF,eAAO,MAAM,wBAAwB,EAAE,eA+FtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,eAKjC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAyCpC,CAAC;AAwHF,eAAO,MAAM,sBAAsB,EAAE,eAiKpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAkEpC,CAAC"}
1
+ {"version":3,"file":"templates.command.d.ts","sourceRoot":"","sources":["../../src/commands/templates.command.ts"],"names":[],"mappings":"AAyBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAUhC,eAAO,MAAM,oBAAoB,EAAE,eA6DlC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAmBlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA+CjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAgBpC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAiDrC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAWnC,CAAC;AAgNF,eAAO,MAAM,2BAA2B,EAAE,eA+BzC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAOnC,CAAC;AA2GF,eAAO,MAAM,wBAAwB,EAAE,eA+FtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,eAKjC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAyCpC,CAAC;AAwHF,eAAO,MAAM,sBAAsB,EAAE,eAiKpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAkEpC,CAAC"}
@@ -9,6 +9,7 @@ import { flagBool, flagList, flagString, flagVars, resolveCwd, } from "../comman
9
9
  import { asJson, header, kv } from "../output/format-output.js";
10
10
  import { maybeRunInWatchMode } from "../output/watch-loop.js";
11
11
  import { renderFailureHints, templateDriftHints } from "../output/failure-hints.js";
12
+ import { enrichWithLlmRecommendations, renderRecommendationsMarkdown, } from '@shrkcrft/ai';
12
13
  export const templatesVarsCommand = {
13
14
  name: 'vars',
14
15
  description: 'Show the variables a template accepts (required/optional/defaults/examples).',
@@ -209,8 +210,8 @@ export const templatesPreviewCommand = {
209
210
  };
210
211
  export const templatesDriftCommand = {
211
212
  name: 'drift',
212
- description: 'Verify every registered template against the workspace. Severity controls and `--watch [--once] [--debounce N]` supported. Read-only.',
213
- usage: 'shrk templates drift [--template <id>] [--pack <packId>] [--var key=value ...] [--min-severity error|warning|info] [--hide <code>[,<code>...]] [--strict] [--ci] [--format text|markdown|html|json] [--report] [--output <path>] [--json] [--watch [--once] [--debounce N]]',
213
+ description: 'Verify every registered template against the workspace. Severity controls and `--watch [--once] [--debounce N]` supported. `--llm-recommendations` layers a local-LLM-derived list of concrete next-steps on top of the deterministic findings (no-op when no provider reachable). Read-only.',
214
+ usage: 'shrk templates drift [--template <id>] [--pack <packId>] [--var key=value ...] [--min-severity error|warning|info] [--hide <code>[,<code>...]] [--strict] [--ci] [--format text|markdown|html|json] [--report] [--output <path>] [--json] [--llm-recommendations] [--provider auto|ollama|llamacpp] [--watch [--once] [--debounce N]]',
214
215
  async run(args) {
215
216
  const watchExit = await maybeRunInWatchMode(args, templatesDriftImpl);
216
217
  if (watchExit !== null)
@@ -272,6 +273,16 @@ async function templatesDriftImpl(args) {
272
273
  else
273
274
  filteredFail++;
274
275
  }
276
+ const wantLlmRecs = flagBool(args, 'llm-recommendations');
277
+ const llmEnvelope = wantLlmRecs
278
+ ? await enrichWithLlmRecommendations({
279
+ surface: 'templates-drift',
280
+ deterministicSummary: summariseDriftEntries(filteredEntries),
281
+ providerKind: flagString(args, 'provider') ?? undefined,
282
+ ask: 'For each FAIL or WARN entry, propose ONE concrete fix the maintainer can apply — name the specific field in `sharkcraft/templates.ts` (or a peer file) to edit. Skip PASS entries unless something is genuinely worth nudging.',
283
+ maxTokens: 1024,
284
+ })
285
+ : null;
275
286
  const ciPayload = {
276
287
  ...report,
277
288
  entries: filteredEntries,
@@ -284,6 +295,7 @@ async function templatesDriftImpl(args) {
284
295
  minSeverity: minSeverityRaw || 'info',
285
296
  hideCodes: [...hideCodes],
286
297
  },
298
+ ...(llmEnvelope ? { llmRecommendations: llmEnvelope } : {}),
287
299
  };
288
300
  const exitNonZero = ci
289
301
  ? filteredFail > 0 || (strict && filteredWarn > 0)
@@ -330,8 +342,31 @@ async function templatesDriftImpl(args) {
330
342
  if (exitNonZero) {
331
343
  process.stdout.write(renderFailureHints(templateDriftHints()));
332
344
  }
345
+ if (llmEnvelope) {
346
+ process.stdout.write('\n');
347
+ process.stdout.write(renderRecommendationsMarkdown(llmEnvelope));
348
+ }
333
349
  return exitNonZero ? 1 : 0;
334
350
  }
351
+ function summariseDriftEntries(entries) {
352
+ const lines = [];
353
+ for (const e of entries) {
354
+ const tag = e.status === TemplateDriftStatus.Pass ? 'PASS' : e.status === TemplateDriftStatus.Warn ? 'WARN' : 'FAIL';
355
+ lines.push(`## [${tag}] ${e.templateId}${e.templateName ? ` — ${e.templateName}` : ''}`);
356
+ if (e.issues.length === 0) {
357
+ lines.push('(no issues)');
358
+ }
359
+ else {
360
+ for (const i of e.issues) {
361
+ lines.push(`- ${i.severity} \`${i.code}\` — ${i.message}${i.suggestedFix ? ` (suggested: ${i.suggestedFix})` : ''}`);
362
+ }
363
+ }
364
+ lines.push('');
365
+ }
366
+ if (lines.length === 0)
367
+ lines.push('(no templates in this drift report)');
368
+ return lines.join('\n');
369
+ }
335
370
  function renderDriftMarkdown(p) {
336
371
  const out = [];
337
372
  out.push('# Template drift');
@@ -1 +1 @@
1
- {"version":3,"file":"tests.command.d.ts","sourceRoot":"","sources":["../../src/commands/tests.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiChC,eAAO,MAAM,kBAAkB,EAAE,eAyBhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAajC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAgBjC,CAAC"}
1
+ {"version":3,"file":"tests.command.d.ts","sourceRoot":"","sources":["../../src/commands/tests.command.ts"],"names":[],"mappings":"AASA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAwChC,eAAO,MAAM,kBAAkB,EAAE,eAyBhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAajC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAgBjC,CAAC"}
@@ -1,13 +1,24 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import * as nodePath from 'node:path';
3
- import { analyzeTestImpact, inspectSharkcraft, readFeatureBundle, suggestTestPathFor, } from '@shrkcrft/inspector';
3
+ import { analyzeTestImpact, getChangedFiles, inspectSharkcraft, readFeatureBundle, suggestTestPathFor, } from '@shrkcrft/inspector';
4
4
  import { flagBool, flagString, flagList, resolveCwd, } from "../command-registry.js";
5
5
  import { asJson, header } from "../output/format-output.js";
6
6
  function collectFiles(cwd, args) {
7
7
  const files = flagList(args, 'files');
8
8
  const planFile = flagString(args, 'plan');
9
9
  const bundleId = flagString(args, 'bundle');
10
+ const since = flagString(args, 'since');
11
+ const staged = flagBool(args, 'staged');
10
12
  const out = [...files];
13
+ // `--since <ref>` / `--staged`: collect changed files straight from git so
14
+ // the agent's reflex `shrk tests impact --since main` works the same way as
15
+ // `shrk impact --since` (was --files-only, forcing a manual diff).
16
+ if (since)
17
+ for (const f of getChangedFiles(cwd, { since }))
18
+ out.push(f);
19
+ if (staged)
20
+ for (const f of getChangedFiles(cwd, { staged: true }))
21
+ out.push(f);
11
22
  if (planFile) {
12
23
  const path = nodePath.isAbsolute(planFile) ? planFile : nodePath.join(cwd, planFile);
13
24
  if (existsSync(path)) {
@@ -38,7 +49,7 @@ function collectFiles(cwd, args) {
38
49
  export const testsImpactCommand = {
39
50
  name: 'impact',
40
51
  description: 'Test impact analysis for changed files / plan / bundle.',
41
- usage: 'shrk tests impact [--files a,b] [--plan <plan>] [--bundle <id>] "<task>"',
52
+ usage: 'shrk tests impact [--files a,b] [--since <ref>] [--staged] [--plan <plan>] [--bundle <id>] "<task>"',
42
53
  async run(args) {
43
54
  const cwd = resolveCwd(args);
44
55
  const inspection = await inspectSharkcraft({ cwd });
@@ -0,0 +1,26 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ /**
3
+ * `shrk watch "<task>"` — emit a fresh task-focused context bundle on
4
+ * stdout JSONL each time the workspace changes.
5
+ *
6
+ * Designed to run in a terminal *next to* Claude Code so the agent has
7
+ * a continuously-refreshed "feed" of the most relevant code for the
8
+ * current task — no extra LLM calls, just BGE-ranked deltas.
9
+ *
10
+ * Surfaces:
11
+ * - stdout: one JSON line per packet, NDJSON-style.
12
+ * - filesystem: same packet appended to
13
+ * `.sharkcraft/feed/<slug>.jsonl` so other tools (or the user)
14
+ * can tail it from elsewhere.
15
+ *
16
+ * Cost: every emission is O(BGE-search + re-rank) — typically
17
+ * 100–300 ms for ~10 candidate files. We never call a generative LLM.
18
+ */
19
+ export declare const watchCommand: ICommandHandler;
20
+ /** `shrk watch list` — show all active and stale watch manifests. */
21
+ export declare const watchListCommand: ICommandHandler;
22
+ /** `shrk watch stop <slug>` — SIGTERM the matching watcher. */
23
+ export declare const watchStopCommand: ICommandHandler;
24
+ /** `shrk watch prune` — remove stale manifests. */
25
+ export declare const watchPruneCommand: ICommandHandler;
26
+ //# sourceMappingURL=watch.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.command.d.ts","sourceRoot":"","sources":["../../src/commands/watch.command.ts"],"names":[],"mappings":"AAwBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA2ChC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,EAAE,eAkL1B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,EAAE,eAwD9B,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,EAAE,eA4C9B,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,EAAE,eA8B/B,CAAC"}