@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
@@ -5,10 +5,12 @@ import { buildSurfaceSummary } from "../surface/surface-summary.js";
5
5
  import { renderShapeLine } from "../surface/shape-defaults.js";
6
6
  import { existsSync } from 'node:fs';
7
7
  import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
8
+ import { SemanticIndex, listIndexableFiles } from '@shrkcrft/embeddings';
8
9
  import { asJson, header, kv } from "../output/format-output.js";
9
10
  import { maybeRunInWatchMode } from "../output/watch-loop.js";
10
11
  import { doctorHints, renderFailureHints } from "../output/failure-hints.js";
11
12
  import { foldDoctorChecks, renderFoldedSummary, DoctorState, } from "../doctor/doctor-tags.js";
13
+ import { enrichWithLlmRecommendations, renderRecommendationsMarkdown, } from '@shrkcrft/ai';
12
14
  const SEVERITY_LABEL = {
13
15
  [DoctorSeverity.Ok]: 'OK ',
14
16
  [DoctorSeverity.Info]: 'INFO ',
@@ -38,7 +40,7 @@ function describeStrictMode(mode) {
38
40
  case 'all':
39
41
  return 'strict=all (every warning fails)';
40
42
  case 'warnings':
41
- return 'strict=warnings (structural warnings fail, hint-quality excluded)';
43
+ return 'strict=warnings (structural warnings fail, advisory excluded)';
42
44
  case 'errors':
43
45
  return 'strict=errors (only errors fail)';
44
46
  case 'off':
@@ -59,8 +61,10 @@ function evaluateStrict(mode, checks, errorCount) {
59
61
  for (const c of checks) {
60
62
  if (c.severity !== DoctorSeverity.Warning)
61
63
  continue;
62
- const isHintQuality = c.id.startsWith('actionhints-');
63
- if (mode === 'warnings' && isHintQuality) {
64
+ // `--strict=warnings` excludes anything the inspector flagged as
65
+ // advisory (action-hint quality today, any future advisory category
66
+ // tomorrow). `--strict=all` counts every warning, advisory or not.
67
+ if (mode === 'warnings' && c.advisory === true) {
64
68
  excludedWarnings += 1;
65
69
  }
66
70
  else {
@@ -74,7 +78,7 @@ function evaluateStrict(mode, checks, errorCount) {
74
78
  excludedWarnings,
75
79
  reason: mode === 'all'
76
80
  ? 'any warning'
77
- : 'structural warnings only (hint-quality excluded)',
81
+ : 'structural warnings only (advisory excluded)',
78
82
  };
79
83
  }
80
84
  function buildFilterOptions(args, suppressions) {
@@ -137,10 +141,81 @@ function isBlockerCheck(check) {
137
141
  async function runDoctorOnce(args) {
138
142
  return doctorCommandImpl(args);
139
143
  }
144
+ function augmentWithSemanticIndexCheck(result, cwd) {
145
+ const current = listIndexableFiles(cwd, 5000);
146
+ const report = SemanticIndex.freshnessReport(cwd, current);
147
+ const check = renderSemanticIndexCheck(report);
148
+ const checks = [...result.checks, check];
149
+ const summary = { ...result.summary };
150
+ if (check.severity === DoctorSeverity.Ok)
151
+ summary.ok = (summary.ok ?? 0) + 1;
152
+ else if (check.severity === DoctorSeverity.Info)
153
+ summary.info = (summary.info ?? 0) + 1;
154
+ else if (check.severity === DoctorSeverity.Warning)
155
+ summary.warnings = (summary.warnings ?? 0) + 1;
156
+ else if (check.severity === DoctorSeverity.Error)
157
+ summary.errors = (summary.errors ?? 0) + 1;
158
+ return { ...result, checks, summary };
159
+ }
160
+ function renderSemanticIndexCheck(report) {
161
+ if (!report.hasIndex) {
162
+ return {
163
+ id: 'semantic-index-missing',
164
+ title: 'Semantic embedding index',
165
+ severity: DoctorSeverity.Info,
166
+ message: `No semantic index found — ${report.untracked} indexable files on disk. ` +
167
+ 'Run `shrk smart-context embeddings-build` to enable embedding-backed retrieval in smart-context.',
168
+ category: 'semantic-index',
169
+ };
170
+ }
171
+ if (report.corrupt) {
172
+ return {
173
+ id: 'semantic-index-corrupt',
174
+ title: 'Semantic embedding index',
175
+ severity: DoctorSeverity.Error,
176
+ message: 'Semantic index meta is corrupt.',
177
+ fix: 'shrk smart-context embeddings-build --rebuild',
178
+ category: 'semantic-index',
179
+ };
180
+ }
181
+ const driftCount = report.stale + report.missing + report.untracked;
182
+ if (driftCount === 0) {
183
+ return {
184
+ id: 'semantic-index-fresh',
185
+ title: 'Semantic embedding index',
186
+ severity: DoctorSeverity.Ok,
187
+ message: `Index fresh — ${report.indexed} files (model ${report.model}).`,
188
+ category: 'semantic-index',
189
+ };
190
+ }
191
+ // Severity is driven only by *rot* — indexed entries that changed (stale)
192
+ // or were deleted (missing), as a fraction of the indexed set. Newly added
193
+ // files (`untracked`) are benign growth, not drift: they aren't in the
194
+ // index yet so they can't return wrong hits, and they don't belong in a
195
+ // ratio whose denominator is the indexed set. The old fused "% drift"
196
+ // (stale+missing+untracked over indexed) tripped a yellow WARN purely on
197
+ // repo growth — exactly the "scary number" noise. The index is a derived
198
+ // cache, so this is always `advisory` (folds into the calm "N advisory"
199
+ // line and nudges a refresh) rather than a code-health defect; a deleted
200
+ // file can no longer reach a caller either, because searchFiles prunes
201
+ // on-disk-missing paths at query time.
202
+ const rotPct = report.indexed > 0 ? ((report.stale + report.missing) * 100) / report.indexed : 0;
203
+ const severity = rotPct >= 10 ? DoctorSeverity.Warning : DoctorSeverity.Info;
204
+ return {
205
+ id: 'semantic-index-stale',
206
+ title: 'Semantic embedding index',
207
+ severity,
208
+ advisory: true,
209
+ message: `Index drifted from disk — ${report.untracked} new, ${report.missing} deleted, ${report.stale} changed ` +
210
+ `(${report.indexed} indexed; ${Math.round(rotPct)}% stale/deleted).`,
211
+ fix: 'shrk smart-context embeddings-build',
212
+ category: 'semantic-index',
213
+ };
214
+ }
140
215
  export const doctorCommand = {
141
216
  name: 'doctor',
142
- 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`.',
143
- 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]]',
217
+ 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`.',
218
+ 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]]',
144
219
  async run(args) {
145
220
  const watchExit = await maybeRunInWatchMode(args, runDoctorOnce);
146
221
  if (watchExit !== null)
@@ -161,7 +236,7 @@ async function doctorCommandImpl(args) {
161
236
  inspectOpts.loaderTimeoutMs = loaderTimeout;
162
237
  }
163
238
  const inspection = await inspectSharkcraft(inspectOpts);
164
- const result = runDoctor(inspection);
239
+ const result = augmentWithSemanticIndexCheck(runDoctor(inspection), cwd);
165
240
  const report = buildAiReadinessReport(inspection);
166
241
  if (debug) {
167
242
  process.stderr.write(`[debug] inspection elapsed ${inspection.inspectionElapsedMs}ms cache=${inspection.cacheEnabled ? 'on' : 'off'} loaders=${inspection.loaderDiagnostics.length}\n`);
@@ -235,6 +310,19 @@ async function doctorCommandImpl(args) {
235
310
  return !isSharkcraftMissing;
236
311
  });
237
312
  }
313
+ // Optional LLM enrichment: never alters the deterministic emission below;
314
+ // only appended at the end. No-op when the flag is off or no provider is
315
+ // reachable — keeps the deterministic baseline byte-stable.
316
+ const wantLlmRecs = flagBool(args, 'llm-recommendations');
317
+ const llmEnvelope = wantLlmRecs
318
+ ? await enrichWithLlmRecommendations({
319
+ surface: 'doctor',
320
+ deterministicSummary: summariseDoctorChecks(visibleChecks),
321
+ providerKind: flagString(args, 'provider') ?? undefined,
322
+ 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.',
323
+ maxTokens: 1024,
324
+ })
325
+ : null;
238
326
  const ackExpired = ackSummary.expired.length > 0 && failOnExpiredAcknowledgement;
239
327
  // Under --no-config + missing sharkcraft, treat the run as advisory: do not
240
328
  // red-fail on the inspector's "no sharkcraft" errors / warnings.
@@ -327,6 +415,7 @@ async function doctorCommandImpl(args) {
327
415
  })),
328
416
  ...result,
329
417
  ...(filtered ? { filtered } : {}),
418
+ ...(llmEnvelope ? { llmRecommendations: llmEnvelope } : {}),
330
419
  }) + '\n');
331
420
  return overallExitCode;
332
421
  }
@@ -402,6 +491,11 @@ async function doctorCommandImpl(args) {
402
491
  process.stdout.write('\n' + summary);
403
492
  }
404
493
  process.stdout.write('\n');
494
+ // How many of the warnings are routine index/cache maintenance (advisory
495
+ // staleness) rather than code-health defects. Surfaced as an additive
496
+ // sub-line so the headline count isn't misread as N real problems —
497
+ // these fold out under --show-advisory.
498
+ const maintenanceWarnings = visibleChecks.filter((c) => c.severity === DoctorSeverity.Warning && c.advisory).length;
405
499
  if (filtered) {
406
500
  const s = filtered.summary;
407
501
  process.stdout.write(`Summary: ${s.ok} ok, ${s.info} info, ${s.warnings} active warnings, ${s.errors} errors\n`);
@@ -415,12 +509,15 @@ async function doctorCommandImpl(args) {
415
509
  else {
416
510
  process.stdout.write(`Summary: ${result.summary.ok} ok, ${result.summary.info} info, ${result.summary.warnings} warnings, ${result.summary.errors} errors\n`);
417
511
  }
512
+ if (maintenanceWarnings > 0) {
513
+ process.stdout.write(` (${maintenanceWarnings} ${maintenanceWarnings === 1 ? 'warning is' : 'warnings are'} routine index/cache maintenance, not code-health defects — fold-out via --show-advisory)\n`);
514
+ }
418
515
  void buildSuppressionEntry;
419
516
  void doctorSuppressionsFile;
420
517
  void saveDoctorSuppressions;
421
518
  void existsSync;
422
519
  if (strictMode === 'warnings' && strictEval.excludedWarnings > 0) {
423
- process.stdout.write(` (strict=warnings excluded ${strictEval.excludedWarnings} hint-quality warning(s); use --strict=all to include)\n`);
520
+ process.stdout.write(` (strict=warnings excluded ${strictEval.excludedWarnings} advisory warning(s); use --strict=all to include)\n`);
424
521
  }
425
522
  // Surface acknowledgement state. Bare suppressions don't qualify as
426
523
  // acknowledgements; expiring/expired ones get a callout so authors don't
@@ -458,10 +555,27 @@ async function doctorCommandImpl(args) {
458
555
  if (!inspection.sharkcraftDir) {
459
556
  process.stdout.write('\nNothing here yet — try `shrk init --zero-config` to detect your stack and pick a preset.\n');
460
557
  }
461
- process.stdout.write(`\nAI-readiness: ${report.score} / 100 (${report.grade})\n`);
558
+ // Honest binary verdicts come first — yes/no on the two questions
559
+ // users actually want answered (can I let an agent write here? can
560
+ // I let an agent read here?). The 0..100 score follows, scoped to
561
+ // the workspace shape so libraries don't get dinged for "no
562
+ // pipelines" and apps don't get dinged for "no published API".
563
+ process.stdout.write(`\nShape: ${report.workspaceShape.label}` +
564
+ ` (score counts ${report.dimensions.filter((d) => d.applies === 'core').length} of ${report.dimensions.length} dimensions)\n`);
565
+ process.stdout.write(` ${report.verdicts.readyForAgentReads ? '✓' : '✗'} Ready for agent reads (context / task lookups)\n`);
566
+ process.stdout.write(` ${report.verdicts.readyForAgentWrites ? '✓' : '✗'} Ready for agent writes (apply / generate)\n`);
567
+ if (report.verdicts.blockers.length > 0) {
568
+ process.stdout.write(` Blockers:\n`);
569
+ for (const b of report.verdicts.blockers) {
570
+ process.stdout.write(` • ${b}\n`);
571
+ }
572
+ }
573
+ process.stdout.write(`\nAI-readiness: ${report.score} / 100 (${report.grade}, shape-aware)\n`);
462
574
  if (report.topRecommendations.length) {
463
575
  // Keep the default doctor output short: top 3 recommendations,
464
- // pass `--verbose` for the full list.
576
+ // pass `--verbose` for the full list. Recommendations only fire
577
+ // from `core` dimensions now — n/a-for-shape dimensions stop
578
+ // generating the misleading "add a pipeline" advice for libraries.
465
579
  const verbose = flagBool(args, 'verbose');
466
580
  const visible = verbose ? report.topRecommendations : report.topRecommendations.slice(0, 3);
467
581
  process.stdout.write(`Top recommendations${verbose ? '' : ` (top ${visible.length})`}:\n`);
@@ -471,6 +585,21 @@ async function doctorCommandImpl(args) {
471
585
  process.stdout.write(` … (${report.topRecommendations.length - visible.length} more — pass --verbose to see all)\n`);
472
586
  }
473
587
  }
588
+ // Surface N/A dimensions when --show-na is passed, so users can see
589
+ // what was deliberately skipped and disagree if they want to.
590
+ if (flagBool(args, 'show-na')) {
591
+ const skipped = report.dimensions.filter((d) => d.applies !== 'core');
592
+ if (skipped.length > 0) {
593
+ process.stdout.write(`\nNot counted in score (${skipped.length} dimensions):\n`);
594
+ for (const d of skipped) {
595
+ const tag = d.applies === 'n/a-for-shape' ? 'n/a' : 'advisory';
596
+ process.stdout.write(` [${tag}] ${d.title}: ${d.note}`);
597
+ if (d.appliesReason)
598
+ process.stdout.write(` — ${d.appliesReason}`);
599
+ process.stdout.write('\n');
600
+ }
601
+ }
602
+ }
474
603
  if (strictEval.failed) {
475
604
  process.stdout.write(`\nStrict mode: failing because ${strictEval.countedWarnings} warning(s) + ${result.summary.errors} error(s) exist (${strictEval.reason}).\n`);
476
605
  }
@@ -497,8 +626,31 @@ async function doctorCommandImpl(args) {
497
626
  if (previewEligible) {
498
627
  process.stdout.write('\nDraft patch available — run `shrk fix preview` for a preview-only patch under `.sharkcraft/fixes/`.\n');
499
628
  }
629
+ if (llmEnvelope) {
630
+ process.stdout.write('\n');
631
+ process.stdout.write(renderRecommendationsMarkdown(llmEnvelope));
632
+ }
500
633
  return overallExitCode;
501
634
  }
635
+ function summariseDoctorChecks(checks) {
636
+ const lines = [];
637
+ const order = [DoctorSeverity.Error, DoctorSeverity.Warning, DoctorSeverity.Info, DoctorSeverity.Ok];
638
+ for (const sev of order) {
639
+ const grouped = checks.filter((c) => c.severity === sev);
640
+ if (grouped.length === 0)
641
+ continue;
642
+ const label = SEVERITY_LABEL[sev].trim();
643
+ lines.push(`## ${label} (${grouped.length})`);
644
+ for (const c of grouped) {
645
+ const fix = c.recommendedFix ?? c.fix;
646
+ lines.push(`- **${c.title}**${c.category ? ` (${c.category})` : ''}: ${c.message}${fix ? ` — suggested fix: ${fix}` : ''}`);
647
+ }
648
+ lines.push('');
649
+ }
650
+ if (lines.length === 0)
651
+ lines.push('(no findings — all checks passed)');
652
+ return lines.join('\n');
653
+ }
502
654
  export const doctorSuppressCommand = {
503
655
  name: 'suppress',
504
656
  description: 'Add a doctor finding to sharkcraft/doctor.suppressions.json. Requires --reason.',
@@ -1 +1 @@
1
- {"version":3,"file":"export.command.d.ts","sourceRoot":"","sources":["../../src/commands/export.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgBhC,eAAO,MAAM,aAAa,EAAE,eAkF3B,CAAC"}
1
+ {"version":3,"file":"export.command.d.ts","sourceRoot":"","sources":["../../src/commands/export.command.ts"],"names":[],"mappings":"AASA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgBhC,eAAO,MAAM,aAAa,EAAE,eAuF3B,CAAC"}
@@ -2,8 +2,9 @@ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { inspectSharkcraft } from '@shrkcrft/inspector';
4
4
  import { ALL_EXPORT_FORMATS, isExportFormat, renderExport, } from "../export/export-formats.js";
5
+ import { buildClaudeCommands } from "../export/claude-commands-export.js";
5
6
  import { flagBool, flagNumber, flagString, resolveCwd, } from "../command-registry.js";
6
- import { asJson, header } from "../output/format-output.js";
7
+ import { asJson, bullet, header } from "../output/format-output.js";
7
8
  import { exportBundleCommand, exportSessionCommand, exportQualityCommand, exportReviewCommand, } from "./export-bundle.command.js";
8
9
  const ARCHIVE_SUBCOMMANDS = {
9
10
  bundle: exportBundleCommand,
@@ -13,7 +14,7 @@ const ARCHIVE_SUBCOMMANDS = {
13
14
  };
14
15
  export const exportCommand = {
15
16
  name: 'export',
16
- description: 'Render SharkCraft knowledge as a flat agent-rule file (AGENTS.md / CLAUDE.md / .cursor/rules / copilot-instructions). Dry-run by default; pass --write to save.',
17
+ description: 'Inversion — pull SharkCraft rules into the agent\'s prompt instead of the agent calling back to shrk. Single-file outputs: claude-skill (.claude/skills/<name>/SKILL.md, recommended), agents-md (AGENTS.md), claude-md (CLAUDE.md), cursor-rules (.cursor/rules/*.mdc), copilot-instructions. Multi-file output: claude-commands (.claude/commands/*.md — per-project slash commands like /new-service, /check-changes, /follow-shrk). Dry-run by default; pass --write to save.',
17
18
  usage: 'shrk [--cwd <dir>] export <format> [--write] [--output <path>] [--task "<task>"] [--max-rules N] [--max-paths N] [--json]',
18
19
  async run(args) {
19
20
  const format = args.positional[0];
@@ -27,8 +28,13 @@ export const exportCommand = {
27
28
  const sub = { ...args, positional: args.positional.slice(1) };
28
29
  return archive.run(sub);
29
30
  }
31
+ // Multi-file `claude-commands` dispatches separately — it emits
32
+ // one .md per slash command, not a single rendered file.
33
+ if (format === 'claude-commands') {
34
+ return runClaudeCommandsExport(args);
35
+ }
30
36
  if (!isExportFormat(format)) {
31
- process.stderr.write(`Unknown export format "${format}".\nFormats: ${ALL_EXPORT_FORMATS.join(', ')}, bundle, session, quality, review\n`);
37
+ process.stderr.write(`Unknown export format "${format}".\nFormats: ${ALL_EXPORT_FORMATS.join(', ')}, claude-commands, bundle, session, quality, review\n`);
32
38
  return 2;
33
39
  }
34
40
  const cwd = resolveCwd(args);
@@ -81,3 +87,70 @@ export const exportCommand = {
81
87
  return 0;
82
88
  },
83
89
  };
90
+ /**
91
+ * `shrk export claude-commands` — multi-file generator for Claude
92
+ * Code's native `.claude/commands/` slash-command primitive. Produces
93
+ * one .md per command (static + per-template).
94
+ *
95
+ * Unlike single-file exports (claude-skill / claude-md / etc.) this
96
+ * writes a SET of files. Each file is a complete recipe Claude Code
97
+ * loads when the user types the matching slash command.
98
+ */
99
+ async function runClaudeCommandsExport(args) {
100
+ const cwd = resolveCwd(args);
101
+ const inspection = await inspectSharkcraft({ cwd });
102
+ const result = buildClaudeCommands(inspection);
103
+ const wantJson = flagBool(args, 'json');
104
+ const doWrite = flagBool(args, 'write');
105
+ const force = flagBool(args, 'force');
106
+ if (wantJson) {
107
+ process.stdout.write(asJson({
108
+ format: 'claude-commands',
109
+ write: doWrite,
110
+ files: result.files.map((f) => ({
111
+ path: f.path,
112
+ slash: f.slash,
113
+ source: f.source,
114
+ })),
115
+ }) + '\n');
116
+ return 0;
117
+ }
118
+ if (!doWrite) {
119
+ process.stdout.write(header('Export (claude-commands) — dry-run'));
120
+ process.stdout.write(`Would write ${result.files.length} command file(s):\n\n`);
121
+ for (const f of result.files) {
122
+ process.stdout.write(` ${f.path}\n`);
123
+ process.stdout.write(` → users type \`/${f.slash}\` in Claude Code (${f.source})\n`);
124
+ }
125
+ process.stdout.write('\nRe-run with --write to save.\n');
126
+ return 0;
127
+ }
128
+ const written = [];
129
+ const skipped = [];
130
+ for (const file of result.files) {
131
+ const fullPath = join(cwd, file.path);
132
+ mkdirSync(dirname(fullPath), { recursive: true });
133
+ if (existsSync(fullPath) && !force) {
134
+ skipped.push(file.path);
135
+ continue;
136
+ }
137
+ writeFileSync(fullPath, file.content, 'utf8');
138
+ written.push(file.path);
139
+ }
140
+ process.stdout.write(header('Claude commands exported'));
141
+ if (written.length) {
142
+ process.stdout.write(`Wrote ${written.length} command file(s):\n`);
143
+ for (const p of written) {
144
+ const f = result.files.find((x) => x.path === p);
145
+ process.stdout.write(bullet(`${p} → \`/${f.slash}\``) + '\n');
146
+ }
147
+ }
148
+ if (skipped.length) {
149
+ process.stdout.write(`\nSkipped ${skipped.length} (already exist; use --force to overwrite):\n`);
150
+ for (const p of skipped)
151
+ process.stdout.write(bullet(p) + '\n');
152
+ }
153
+ process.stdout.write('\nClaude Code picks up `.claude/commands/*.md` automatically. ' +
154
+ 'Open the project in Claude Code, type `/` — the slash commands are in the palette.\n');
155
+ return 0;
156
+ }
@@ -0,0 +1,12 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ /**
3
+ * `shrk framework` — run / inspect the framework-aware extractors.
4
+ *
5
+ * Sub-verbs:
6
+ * - shrk framework index run extractors over the project
7
+ * - shrk framework status report store health
8
+ * - shrk framework list [filters] list entities (--framework, --subtype, --file)
9
+ * - shrk framework routes NestJS route table (method, path, handler, file)
10
+ */
11
+ export declare const frameworkCommand: ICommandHandler;
12
+ //# sourceMappingURL=framework.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework.command.d.ts","sourceRoot":"","sources":["../../src/commands/framework.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC"}
@@ -0,0 +1,180 @@
1
+ import { buildRegistryWithPacks, defaultRegistry, FrameworkQueryApi, runExtractors, } from '@shrkcrft/framework-scanners';
2
+ import { inspectSharkcraft } from '@shrkcrft/inspector';
3
+ import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
4
+ import { asJson, header, kv } from "../output/format-output.js";
5
+ /**
6
+ * `shrk framework` — run / inspect the framework-aware extractors.
7
+ *
8
+ * Sub-verbs:
9
+ * - shrk framework index run extractors over the project
10
+ * - shrk framework status report store health
11
+ * - shrk framework list [filters] list entities (--framework, --subtype, --file)
12
+ * - shrk framework routes NestJS route table (method, path, handler, file)
13
+ */
14
+ export const frameworkCommand = {
15
+ name: 'framework',
16
+ description: 'Framework-aware extractors: NestJS (controllers/modules/providers/routes), React (components/hook usages). Output: shrk framework list / routes / status / index.',
17
+ usage: 'shrk framework index [--only nestjs,react] [--json] | shrk framework status [--json] | shrk framework list [--framework <name>] [--subtype <s>] [--file <path>] [--limit N] [--json] | shrk framework routes [--json]',
18
+ async run(args) {
19
+ const sub = args.positional[0];
20
+ if (sub === 'index')
21
+ return runIndex(args);
22
+ if (sub === 'status')
23
+ return runStatus(args);
24
+ if (sub === 'list')
25
+ return runList(args);
26
+ if (sub === 'routes')
27
+ return runRoutes(args);
28
+ process.stderr.write(this.usage + '\n');
29
+ return 2;
30
+ },
31
+ };
32
+ async function runIndex(args) {
33
+ const cwd = resolveCwd(args);
34
+ const wantJson = flagBool(args, 'json');
35
+ const only = flagString(args, 'only');
36
+ const noPacks = flagBool(args, 'no-packs');
37
+ try {
38
+ // Build the registry: built-ins + optional pack-contributed extractors.
39
+ const registry = defaultRegistry();
40
+ let packPackages = [];
41
+ let packDiagnostics = [];
42
+ if (!noPacks) {
43
+ try {
44
+ const inspection = await inspectSharkcraft({ cwd });
45
+ const merged = await buildRegistryWithPacks(registry, inspection.packs);
46
+ packPackages = merged.packs;
47
+ packDiagnostics = merged.diagnostics;
48
+ }
49
+ catch (e) {
50
+ // Pack loading is best-effort. Surface the error as a diagnostic
51
+ // but don't block the index — built-in extractors still run.
52
+ packDiagnostics = [`pack discovery failed: ${e.message}`];
53
+ }
54
+ }
55
+ const r = runExtractors({
56
+ projectRoot: cwd,
57
+ registry,
58
+ ...(only ? { only: only.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
59
+ });
60
+ const diagnostics = [...packDiagnostics, ...r.diagnostics];
61
+ if (wantJson) {
62
+ process.stdout.write(asJson({
63
+ ok: true,
64
+ manifest: r.manifest,
65
+ filesScanned: r.filesScanned,
66
+ durationMs: r.durationMs,
67
+ diagnostics,
68
+ packExtractors: packPackages,
69
+ }) + '\n');
70
+ return 0;
71
+ }
72
+ process.stdout.write(header('Framework index'));
73
+ process.stdout.write(kv('schema', r.manifest.schema) + '\n');
74
+ process.stdout.write(kv('frameworks', r.manifest.frameworks.join(', ')) + '\n');
75
+ process.stdout.write(kv('files scanned', String(r.filesScanned)) + '\n');
76
+ for (const [framework, count] of Object.entries(r.manifest.countsByFramework)) {
77
+ process.stdout.write(kv(`entities (${framework})`, String(count)) + '\n');
78
+ }
79
+ if (packPackages.length > 0) {
80
+ process.stdout.write(kv('pack extractors', packPackages.join(', ')) + '\n');
81
+ }
82
+ process.stdout.write(kv('duration', `${r.durationMs}ms`) + '\n');
83
+ for (const d of diagnostics.slice(0, 5))
84
+ process.stdout.write(`! ${d}\n`);
85
+ return 0;
86
+ }
87
+ catch (e) {
88
+ const msg = e.message;
89
+ if (wantJson) {
90
+ process.stdout.write(asJson({ ok: false, error: msg, nextCommand: 'shrk graph index' }) + '\n');
91
+ }
92
+ else {
93
+ process.stderr.write(msg + '\n');
94
+ }
95
+ return 1;
96
+ }
97
+ }
98
+ async function runStatus(args) {
99
+ const cwd = resolveCwd(args);
100
+ const wantJson = flagBool(args, 'json');
101
+ const missing = FrameworkQueryApi.missingDescription(cwd);
102
+ if (missing) {
103
+ if (wantJson) {
104
+ process.stdout.write(asJson({ ok: false, state: 'missing', message: missing, nextCommand: 'shrk framework index' }) + '\n');
105
+ return 1;
106
+ }
107
+ process.stderr.write(missing + '\n');
108
+ return 1;
109
+ }
110
+ const api = FrameworkQueryApi.fromStore(cwd);
111
+ const m = api.manifest();
112
+ if (wantJson) {
113
+ process.stdout.write(asJson({ ok: true, manifest: m }) + '\n');
114
+ return 0;
115
+ }
116
+ process.stdout.write(header('Framework status'));
117
+ process.stdout.write(kv('schema', m.schema) + '\n');
118
+ process.stdout.write(kv('frameworks', m.frameworks.join(', ')) + '\n');
119
+ for (const [k, v] of Object.entries(m.countsBySubtype)) {
120
+ process.stdout.write(kv(` ${k}`, String(v)) + '\n');
121
+ }
122
+ process.stdout.write(kv('last built', m.lastBuiltAt) + '\n');
123
+ return 0;
124
+ }
125
+ async function runList(args) {
126
+ const cwd = resolveCwd(args);
127
+ const wantJson = flagBool(args, 'json');
128
+ const missing = FrameworkQueryApi.missingDescription(cwd);
129
+ if (missing) {
130
+ process.stderr.write(missing + '\n');
131
+ return 1;
132
+ }
133
+ const api = FrameworkQueryApi.fromStore(cwd);
134
+ const framework = flagString(args, 'framework');
135
+ const subtype = flagString(args, 'subtype');
136
+ const file = flagString(args, 'file');
137
+ const limit = Number(flagString(args, 'limit') ?? '50');
138
+ const entities = api.list({
139
+ ...(framework ? { framework } : {}),
140
+ ...(subtype ? { subtype } : {}),
141
+ ...(file ? { file } : {}),
142
+ limit,
143
+ });
144
+ if (wantJson) {
145
+ process.stdout.write(asJson({
146
+ schema: 'sharkcraft.framework-list/v1',
147
+ filters: { framework, subtype, file },
148
+ total: entities.length,
149
+ entities: entities.map((n) => ({ id: n.id, label: n.label, path: n.path, data: n.data })),
150
+ }) + '\n');
151
+ return 0;
152
+ }
153
+ process.stdout.write(header('Framework entities'));
154
+ process.stdout.write(kv('total', String(entities.length)) + '\n');
155
+ for (const n of entities) {
156
+ process.stdout.write(` ${n.data?.['framework'] ?? '?'}:${n.data?.['subtype'] ?? '?'} ${n.label} ${n.path ?? ''}\n`);
157
+ }
158
+ return 0;
159
+ }
160
+ async function runRoutes(args) {
161
+ const cwd = resolveCwd(args);
162
+ const wantJson = flagBool(args, 'json');
163
+ const missing = FrameworkQueryApi.missingDescription(cwd);
164
+ if (missing) {
165
+ process.stderr.write(missing + '\n');
166
+ return 1;
167
+ }
168
+ const api = FrameworkQueryApi.fromStore(cwd);
169
+ const routes = api.routes();
170
+ if (wantJson) {
171
+ process.stdout.write(asJson({ schema: 'sharkcraft.framework-routes/v1', total: routes.length, routes }) + '\n');
172
+ return 0;
173
+ }
174
+ process.stdout.write(header('NestJS routes'));
175
+ process.stdout.write(kv('total', String(routes.length)) + '\n');
176
+ for (const r of routes.slice(0, 100)) {
177
+ process.stdout.write(` ${r.method.padEnd(6)} ${r.path.padEnd(36)} → ${r.handler} (${r.file})\n`);
178
+ }
179
+ return 0;
180
+ }
@@ -0,0 +1,15 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ /**
3
+ * `shrk gate` — run all code-intelligence quality gates and emit one
4
+ * pass/fail report. Designed as the single command CI should call
5
+ * before merge.
6
+ *
7
+ * Exit codes:
8
+ * - 0 if overall status is `pass` (no failures, no warnings)
9
+ * - 0 if overall is `warn` (default — opt-in to fail via --strict)
10
+ * - 1 if overall is `fail`
11
+ *
12
+ * Pass `--strict` to treat `warn` as failure.
13
+ */
14
+ export declare const gateCommand: ICommandHandler;
15
+ //# sourceMappingURL=gate.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eA+FzB,CAAC"}