@shrkcrft/cli 0.1.0-alpha.22 → 0.1.0-alpha.24

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 (108) hide show
  1. package/dist/command-registry.d.ts +9 -0
  2. package/dist/command-registry.d.ts.map +1 -1
  3. package/dist/command-registry.js +15 -0
  4. package/dist/commands/ai-status.command.d.ts.map +1 -1
  5. package/dist/commands/ai-status.command.js +10 -0
  6. package/dist/commands/apply.command.d.ts.map +1 -1
  7. package/dist/commands/apply.command.js +9 -2
  8. package/dist/commands/ask.command.d.ts.map +1 -1
  9. package/dist/commands/ask.command.js +2 -1
  10. package/dist/commands/changes.command.d.ts.map +1 -1
  11. package/dist/commands/changes.command.js +14 -4
  12. package/dist/commands/check.command.d.ts.map +1 -1
  13. package/dist/commands/check.command.js +139 -7
  14. package/dist/commands/command-catalog.d.ts +11 -0
  15. package/dist/commands/command-catalog.d.ts.map +1 -1
  16. package/dist/commands/command-catalog.js +291 -0
  17. package/dist/commands/commands.command.d.ts +15 -0
  18. package/dist/commands/commands.command.d.ts.map +1 -1
  19. package/dist/commands/commands.command.js +162 -3
  20. package/dist/commands/compress.command.d.ts.map +1 -1
  21. package/dist/commands/compress.command.js +39 -4
  22. package/dist/commands/context.command.d.ts.map +1 -1
  23. package/dist/commands/context.command.js +3 -1
  24. package/dist/commands/daily.commands.d.ts.map +1 -1
  25. package/dist/commands/daily.commands.js +48 -5
  26. package/dist/commands/diff-check.command.d.ts.map +1 -1
  27. package/dist/commands/diff-check.command.js +6 -2
  28. package/dist/commands/feedback.command.d.ts.map +1 -1
  29. package/dist/commands/feedback.command.js +4 -0
  30. package/dist/commands/finish.command.d.ts +3 -0
  31. package/dist/commands/finish.command.d.ts.map +1 -0
  32. package/dist/commands/finish.command.js +70 -0
  33. package/dist/commands/framework.command.d.ts.map +1 -1
  34. package/dist/commands/framework.command.js +20 -3
  35. package/dist/commands/gate.command.d.ts.map +1 -1
  36. package/dist/commands/gate.command.js +98 -13
  37. package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
  38. package/dist/commands/graph-code-subverbs.js +215 -81
  39. package/dist/commands/graph.command.d.ts.map +1 -1
  40. package/dist/commands/graph.command.js +6 -0
  41. package/dist/commands/help.command.d.ts.map +1 -1
  42. package/dist/commands/help.command.js +20 -2
  43. package/dist/commands/impact.command.d.ts.map +1 -1
  44. package/dist/commands/impact.command.js +86 -1
  45. package/dist/commands/knowledge.command.d.ts.map +1 -1
  46. package/dist/commands/knowledge.command.js +21 -1
  47. package/dist/commands/onboard.command.d.ts.map +1 -1
  48. package/dist/commands/onboard.command.js +11 -2
  49. package/dist/commands/orchestrate.command.d.ts.map +1 -1
  50. package/dist/commands/orchestrate.command.js +6 -1
  51. package/dist/commands/pack-author.command.d.ts.map +1 -1
  52. package/dist/commands/pack-author.command.js +13 -1
  53. package/dist/commands/packs.command.d.ts.map +1 -1
  54. package/dist/commands/packs.command.js +95 -17
  55. package/dist/commands/paths.command.d.ts.map +1 -1
  56. package/dist/commands/paths.command.js +8 -1
  57. package/dist/commands/pipelines.command.d.ts.map +1 -1
  58. package/dist/commands/pipelines.command.js +2 -1
  59. package/dist/commands/plan-context.command.d.ts.map +1 -1
  60. package/dist/commands/plan-context.command.js +6 -3
  61. package/dist/commands/plan-simulate.command.js +1 -1
  62. package/dist/commands/policy-lint.command.d.ts.map +1 -1
  63. package/dist/commands/policy-lint.command.js +20 -7
  64. package/dist/commands/recommend.command.d.ts.map +1 -1
  65. package/dist/commands/recommend.command.js +10 -2
  66. package/dist/commands/registry.command.d.ts.map +1 -1
  67. package/dist/commands/registry.command.js +117 -9
  68. package/dist/commands/report.command.js +1 -1
  69. package/dist/commands/reuse.command.d.ts.map +1 -1
  70. package/dist/commands/reuse.command.js +17 -5
  71. package/dist/commands/review.command.d.ts.map +1 -1
  72. package/dist/commands/review.command.js +20 -2
  73. package/dist/commands/rule-graph-subverbs.d.ts.map +1 -1
  74. package/dist/commands/rule-graph-subverbs.js +31 -4
  75. package/dist/commands/search-structural.command.d.ts.map +1 -1
  76. package/dist/commands/search-structural.command.js +2 -2
  77. package/dist/commands/search.command.d.ts.map +1 -1
  78. package/dist/commands/search.command.js +19 -1
  79. package/dist/commands/simulate.command.d.ts.map +1 -1
  80. package/dist/commands/simulate.command.js +6 -1
  81. package/dist/commands/smart-context.command.d.ts.map +1 -1
  82. package/dist/commands/smart-context.command.js +101 -41
  83. package/dist/commands/spec.command.d.ts.map +1 -1
  84. package/dist/commands/spec.command.js +73 -13
  85. package/dist/commands/task-context.command.d.ts.map +1 -1
  86. package/dist/commands/task-context.command.js +26 -2
  87. package/dist/commands/test.command.d.ts.map +1 -1
  88. package/dist/commands/test.command.js +17 -0
  89. package/dist/commands/trace.command.d.ts.map +1 -1
  90. package/dist/commands/trace.command.js +87 -4
  91. package/dist/commands/wiring.command.d.ts +12 -0
  92. package/dist/commands/wiring.command.d.ts.map +1 -0
  93. package/dist/commands/wiring.command.js +384 -0
  94. package/dist/diff/collect-changed-paths.d.ts +5 -3
  95. package/dist/diff/collect-changed-paths.d.ts.map +1 -1
  96. package/dist/diff/collect-changed-paths.js +73 -36
  97. package/dist/diff/deleted-orphans.d.ts +42 -0
  98. package/dist/diff/deleted-orphans.d.ts.map +1 -0
  99. package/dist/diff/deleted-orphans.js +46 -0
  100. package/dist/finish/run-finish.d.ts +62 -0
  101. package/dist/finish/run-finish.d.ts.map +1 -0
  102. package/dist/finish/run-finish.js +239 -0
  103. package/dist/main.d.ts.map +1 -1
  104. package/dist/main.js +19 -1
  105. package/dist/status/freshness.d.ts +30 -0
  106. package/dist/status/freshness.d.ts.map +1 -0
  107. package/dist/status/freshness.js +81 -0
  108. package/package.json +33 -33
@@ -1 +1 @@
1
- {"version":3,"file":"context.command.d.ts","sourceRoot":"","sources":["../../src/commands/context.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgChC,eAAO,MAAM,cAAc,EAAE,eAyI5B,CAAC"}
1
+ {"version":3,"file":"context.command.d.ts","sourceRoot":"","sources":["../../src/commands/context.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgChC,eAAO,MAAM,cAAc,EAAE,eA2I5B,CAAC"}
@@ -1,4 +1,4 @@
1
- import { entrypointBanner, inspectSharkcraft } from '@shrkcrft/inspector';
1
+ import { contextTuningBoostFor, entrypointBanner, inspectSharkcraft } from '@shrkcrft/inspector';
2
2
  import { buildContext } from '@shrkcrft/context';
3
3
  import { loadIntentBenchmark, runIntentBenchmark, STARTER_INTENT_BENCHMARK, writeBenchmarkRun, } from '@shrkcrft/context-planner';
4
4
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
@@ -67,6 +67,7 @@ export const contextCommand = {
67
67
  const includeCommands = flagBool(args, 'include-commands');
68
68
  const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
69
69
  const overview = buildProjectOverview(inspection.workspace, inspection.config?.projectName);
70
+ const contextBoost = contextTuningBoostFor(inspection, task);
70
71
  const result = buildContext(inspection.knowledgeEntries, {
71
72
  task,
72
73
  framework,
@@ -81,6 +82,7 @@ export const contextCommand = {
81
82
  includeDocs,
82
83
  includeCommands,
83
84
  projectOverview: renderOverviewText(overview),
85
+ ...(contextBoost ? { boostFor: contextBoost } : {}),
84
86
  });
85
87
  // Surface top commands prominently before the long context body.
86
88
  // Auto-promote commands-first for action-like tasks (rename / add /
@@ -1 +1 @@
1
- {"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,WAAW,EAAE,eAsFzB,CAAC;AAcF,eAAO,MAAM,WAAW,EAAE,eAoGzB,CAAC;AAKF,eAAO,MAAM,cAAc,EAAE,eA6D5B,CAAC"}
1
+ {"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,WAAW,EAAE,eAsFzB,CAAC;AAcF,eAAO,MAAM,WAAW,EAAE,eAoGzB,CAAC;AAKF,eAAO,MAAM,cAAc,EAAE,eAyG5B,CAAC"}
@@ -186,14 +186,33 @@ export const explainCommand = {
186
186
  const top = inspection.knowledgeEntries
187
187
  .filter((e) => matches(topic, e.id, e.title, e.content, e.tags.join(' ')))
188
188
  .slice(0, maxEntries);
189
+ // Surface the matched items themselves, not just counts. `explain`
190
+ // otherwise duplicates `shrk search`'s job while hiding what matched.
191
+ const relevantRules = packet.relevantRules.map((r) => ({
192
+ id: r.id,
193
+ title: r.title,
194
+ priority: r.priority,
195
+ }));
196
+ const relevantPaths = packet.relevantPaths.map((p) => ({
197
+ id: p.id,
198
+ title: p.title,
199
+ priority: p.priority,
200
+ }));
201
+ const relevantTemplates = packet.relevantTemplates.map((t) => ({
202
+ id: t.id,
203
+ name: t.name,
204
+ }));
189
205
  if (flagBool(args, 'json')) {
190
206
  process.stdout.write(asJson({
191
207
  topic,
192
208
  summary: {
193
- relevantRules: packet.relevantRules.length,
194
- relevantPaths: packet.relevantPaths.length,
195
- relevantTemplates: packet.relevantTemplates.length,
209
+ relevantRules: relevantRules.length,
210
+ relevantPaths: relevantPaths.length,
211
+ relevantTemplates: relevantTemplates.length,
196
212
  },
213
+ relevantRules,
214
+ relevantPaths,
215
+ relevantTemplates,
197
216
  entries: top.map((e) => ({
198
217
  id: e.id,
199
218
  title: e.title,
@@ -205,11 +224,35 @@ export const explainCommand = {
205
224
  return 0;
206
225
  }
207
226
  process.stdout.write(header(`explain: ${topic}`));
208
- process.stdout.write(kv('matches', `rules=${packet.relevantRules.length} paths=${packet.relevantPaths.length} templates=${packet.relevantTemplates.length}`) + '\n\n');
227
+ process.stdout.write(kv('matches', `rules=${relevantRules.length} paths=${relevantPaths.length} templates=${relevantTemplates.length}`) + '\n\n');
228
+ if (relevantRules.length > 0) {
229
+ process.stdout.write('Rules:\n');
230
+ for (const r of relevantRules) {
231
+ process.stdout.write(` • [${r.priority}] ${r.id} — ${r.title}\n`);
232
+ }
233
+ process.stdout.write('\n');
234
+ }
235
+ if (relevantPaths.length > 0) {
236
+ process.stdout.write('Paths:\n');
237
+ for (const p of relevantPaths) {
238
+ process.stdout.write(` • [${p.priority}] ${p.id} — ${p.title}\n`);
239
+ }
240
+ process.stdout.write('\n');
241
+ }
242
+ if (relevantTemplates.length > 0) {
243
+ process.stdout.write('Templates:\n');
244
+ for (const t of relevantTemplates) {
245
+ process.stdout.write(` • ${t.id} — ${t.name}\n`);
246
+ }
247
+ process.stdout.write('\n');
248
+ }
209
249
  if (top.length === 0) {
210
- process.stdout.write('(no matching knowledge entries)\n');
250
+ if (relevantRules.length + relevantPaths.length + relevantTemplates.length === 0) {
251
+ process.stdout.write('(no matching knowledge entries)\n');
252
+ }
211
253
  return 0;
212
254
  }
255
+ process.stdout.write('Knowledge:\n');
213
256
  for (const e of top) {
214
257
  process.stdout.write(`• [${e.priority}] ${e.id} — ${e.title}\n`);
215
258
  const body = e.content
@@ -1 +1 @@
1
- {"version":3,"file":"diff-check.command.d.ts","sourceRoot":"","sources":["../../src/commands/diff-check.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAcH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA0GhC,eAAO,MAAM,gBAAgB,EAAE,eAiI9B,CAAC"}
1
+ {"version":3,"file":"diff-check.command.d.ts","sourceRoot":"","sources":["../../src/commands/diff-check.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAcH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA6GhC,eAAO,MAAM,gBAAgB,EAAE,eAkI9B,CAAC"}
@@ -34,9 +34,12 @@ function resolveScope(args, cwd) {
34
34
  const staged = flagBool(args, 'staged');
35
35
  const since = flagString(args, 'since');
36
36
  const filesRaw = flagString(args, 'files');
37
+ // Files come from `--files a,b` or as bare positional args
38
+ // (`shrk diff-check a.ts b.ts`). Positionals were previously ignored, which
39
+ // silently widened the scope back to the full worktree.
37
40
  const files = filesRaw
38
41
  ? filesRaw.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
39
- : [];
42
+ : args.positional.filter((s) => s.length > 0);
40
43
  if (files.length > 0) {
41
44
  return { mode: 'files', options: { projectRoot: cwd, files } };
42
45
  }
@@ -97,7 +100,8 @@ function deriveVerdict(env) {
97
100
  export const diffCheckCommand = {
98
101
  name: 'diff-check',
99
102
  description: 'Self-check the current git diff against this project\'s boundary + import-hygiene rules. Single-call composite of `shrk check boundaries --changed-only` + `shrk check imports --changed-only`, with one verdict and one nextAction line. Designed for AI agents to run after editing — pass --json for the structured envelope.',
100
- usage: 'shrk [--cwd <dir>] diff-check [--staged | --since <ref> | --files a.ts,b.ts] [--json]',
103
+ usage: 'shrk [--cwd <dir>] diff-check [files... | --files a.ts,b.ts | --staged | --since <ref>] [--json]',
104
+ booleanFlags: new Set(['json', 'staged']),
101
105
  async run(args) {
102
106
  const cwd = resolveCwd(args);
103
107
  const wantJson = flagBool(args, 'json');
@@ -1 +1 @@
1
- {"version":3,"file":"feedback.command.d.ts","sourceRoot":"","sources":["../../src/commands/feedback.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgBhC,eAAO,MAAM,qBAAqB,EAAE,eAiCnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eA0CtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAyDpC,CAAC;AAEF,eAAO,MAAM,+BAA+B,EAAE,eAwB7C,CAAC;AAIF,eAAO,MAAM,wBAAwB,EAAE,eAqBtC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,eAiDxC,CAAC;AAGF,eAAO,MAAM,sBAAsB,EAAE,eAqBpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA6BjC,CAAC"}
1
+ {"version":3,"file":"feedback.command.d.ts","sourceRoot":"","sources":["../../src/commands/feedback.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiBhC,eAAO,MAAM,qBAAqB,EAAE,eAiCnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eA0CtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAyDpC,CAAC;AAEF,eAAO,MAAM,+BAA+B,EAAE,eAwB7C,CAAC;AAIF,eAAO,MAAM,wBAAwB,EAAE,eAqBtC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,eAoDxC,CAAC;AAGF,eAAO,MAAM,sBAAsB,EAAE,eAqBpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA6BjC,CAAC"}
@@ -10,6 +10,7 @@ import * as nodePath from 'node:path';
10
10
  import { FeedbackBucket, ingestFeedbackFile, inspectSharkcraft, loadFeedbackRules, renderFeedbackBacklog, } from '@shrkcrft/inspector';
11
11
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
12
12
  import { asJson, header } from "../output/format-output.js";
13
+ import { feedbackRulesDoctorHints, renderFailureHints } from "../output/failure-hints.js";
13
14
  function ingestPositional(args) {
14
15
  return args.positional[0];
15
16
  }
@@ -256,6 +257,9 @@ export const feedbackRulesDoctorCommand = {
256
257
  process.stdout.write(` ERROR ${e.id}: ${e.message}\n`);
257
258
  for (const w of warnings)
258
259
  process.stdout.write(` WARN ${w.id}: ${w.message}\n`);
260
+ if (errors.length > 0) {
261
+ process.stdout.write(renderFailureHints(feedbackRulesDoctorHints()));
262
+ }
259
263
  return errors.length > 0 ? 1 : 0;
260
264
  },
261
265
  };
@@ -0,0 +1,3 @@
1
+ import { type ICommandHandler } from '../command-registry.js';
2
+ export declare const finishCommand: ICommandHandler;
3
+ //# sourceMappingURL=finish.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finish.command.d.ts","sourceRoot":"","sources":["../../src/commands/finish.command.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA8DhC,eAAO,MAAM,aAAa,EAAE,eAmB3B,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
2
+ import { asJson, bullet, header, kv } from "../output/format-output.js";
3
+ import { runFinishGates } from "../finish/run-finish.js";
4
+ function resolveScope(args, cwd) {
5
+ const staged = flagBool(args, 'staged');
6
+ const since = flagString(args, 'since');
7
+ const filesRaw = flagString(args, 'files');
8
+ const files = filesRaw
9
+ ? filesRaw.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
10
+ : args.positional.filter((s) => s.length > 0);
11
+ if (files.length > 0)
12
+ return { mode: 'files', options: { projectRoot: cwd, files } };
13
+ if (staged)
14
+ return { mode: 'staged', options: { projectRoot: cwd, staged: true } };
15
+ if (since)
16
+ return { mode: 'since', options: { projectRoot: cwd, since } };
17
+ return { mode: 'worktree', options: { projectRoot: cwd, includeWorktree: true } };
18
+ }
19
+ const STATUS_GLYPH = {
20
+ pass: '✓',
21
+ fail: '✗',
22
+ skipped: '–',
23
+ };
24
+ function renderText(report) {
25
+ process.stdout.write(header('Finish — is this changeset safe to complete?'));
26
+ process.stdout.write(kv('scope', `${report.scope.mode} (${report.scope.fileCount} file${report.scope.fileCount === 1 ? '' : 's'})`) +
27
+ '\n');
28
+ for (const g of report.gates) {
29
+ process.stdout.write(` ${STATUS_GLYPH[g.status]} ${g.name.padEnd(11)} ${g.status.padEnd(8)} ${g.detail}\n`);
30
+ }
31
+ if (report.impact.ran) {
32
+ process.stdout.write(kv('impact', `risk=${report.impact.risk}, ${report.impact.directDependents} direct / ${report.impact.transitiveDependents} transitive dependents`) +
33
+ '\n');
34
+ }
35
+ else if (report.impact.note) {
36
+ process.stdout.write(kv('impact', `(skipped — ${report.impact.note})`) + '\n');
37
+ }
38
+ process.stdout.write(kv('verdict', report.verdict) + '\n\n');
39
+ process.stdout.write(report.summary + '\n');
40
+ const failing = report.gates.filter((g) => g.status === 'fail');
41
+ for (const g of failing) {
42
+ process.stdout.write(`\n${g.name} — failing items:\n`);
43
+ for (const item of g.items.slice(0, 15)) {
44
+ const loc = item.file ? `${item.file}${item.line ? `:${item.line}` : ''}` : '';
45
+ process.stdout.write(bullet(`${loc ? loc + ' — ' : ''}${item.message}`) + '\n');
46
+ }
47
+ if (g.items.length > 15) {
48
+ process.stdout.write(` … and ${g.items.length - 15} more (pass --json for the full list).\n`);
49
+ }
50
+ }
51
+ process.stdout.write(`\nNext: ${report.nextAction}\n`);
52
+ }
53
+ export const finishCommand = {
54
+ name: 'finish',
55
+ description: 'Composite "is this changeset safe to finish?" gate: EXECUTES every deterministic changed-only check inline — boundaries + import-hygiene + wiring + policy + deleted-orphans — plus an impact summary, and returns ONE pass/fail. The single trustworthy "done?" call after editing (superset of `diff-check`; honors 0-rules→skipped). Read-only.',
56
+ usage: 'shrk [--cwd <dir>] finish [files... | --files a.ts,b.ts | --staged | --since <ref>] [--json]',
57
+ booleanFlags: new Set(['json', 'staged']),
58
+ async run(args) {
59
+ const cwd = resolveCwd(args);
60
+ const wantJson = flagBool(args, 'json');
61
+ const { mode, options } = resolveScope(args, cwd);
62
+ const report = await runFinishGates({ cwd, mode, scope: options });
63
+ if (wantJson) {
64
+ process.stdout.write(asJson(report) + '\n');
65
+ return report.verdict === 'fail' ? 1 : 0;
66
+ }
67
+ renderText(report);
68
+ return report.verdict === 'fail' ? 1 : 0;
69
+ },
70
+ };
@@ -1 +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"}
1
+ {"version":3,"file":"framework.command.d.ts","sourceRoot":"","sources":["../../src/commands/framework.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAIhC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { buildRegistryWithPacks, defaultRegistry, FrameworkQueryApi, runExtractors, } from '@shrkcrft/framework-scanners';
2
2
  import { inspectSharkcraft } from '@shrkcrft/inspector';
3
- import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
3
+ import { flagBool, flagPositiveInt, flagString, resolveCwd, } from "../command-registry.js";
4
4
  import { asJson, header, kv } from "../output/format-output.js";
5
+ import { computeMtimeFreshness } from "../status/freshness.js";
5
6
  /**
6
7
  * `shrk framework` — run / inspect the framework-aware extractors.
7
8
  *
@@ -109,8 +110,20 @@ async function runStatus(args) {
109
110
  }
110
111
  const api = FrameworkQueryApi.fromStore(cwd);
111
112
  const m = api.manifest();
113
+ // Honest freshness: the framework store is stale once a source file changed
114
+ // after it was built. Mirror the `state + lastBuiltAt + drift` shape that
115
+ // `graph status` exposes.
116
+ const fresh = computeMtimeFreshness(cwd, m.lastBuiltAt);
112
117
  if (wantJson) {
113
- process.stdout.write(asJson({ ok: true, manifest: m }) + '\n');
118
+ process.stdout.write(asJson({
119
+ ok: true,
120
+ state: fresh.state,
121
+ lastBuiltAt: m.lastBuiltAt,
122
+ lastChangedAt: fresh.lastChangedAt,
123
+ behindMs: fresh.behindMs,
124
+ ...(fresh.state === 'stale' ? { nextCommand: 'shrk framework index' } : {}),
125
+ manifest: m,
126
+ }) + '\n');
114
127
  return 0;
115
128
  }
116
129
  process.stdout.write(header('Framework status'));
@@ -120,6 +133,10 @@ async function runStatus(args) {
120
133
  process.stdout.write(kv(` ${k}`, String(v)) + '\n');
121
134
  }
122
135
  process.stdout.write(kv('last built', m.lastBuiltAt) + '\n');
136
+ process.stdout.write(kv('state', fresh.state) + '\n');
137
+ if (fresh.state === 'stale') {
138
+ process.stdout.write(`! stale — source changed since last build${fresh.lastChangedAt ? ` (last change ${fresh.lastChangedAt})` : ''}; re-run \`shrk framework index\`\n`);
139
+ }
123
140
  return 0;
124
141
  }
125
142
  async function runList(args) {
@@ -134,7 +151,7 @@ async function runList(args) {
134
151
  const framework = flagString(args, 'framework');
135
152
  const subtype = flagString(args, 'subtype');
136
153
  const file = flagString(args, 'file');
137
- const limit = Number(flagString(args, 'limit') ?? '50');
154
+ const limit = flagPositiveInt(args, 'limit', 50);
138
155
  const entities = api.list({
139
156
  ...(framework ? { framework } : {}),
140
157
  ...(subtype ? { subtype } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eA0GzB,CAAC"}
1
+ {"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eAiMzB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { QualityGateReportStore, renderGateReportMarkdown, runQualityGates, } from '@shrkcrft/quality-gates';
2
- import { loadProjectConfig } from '@shrkcrft/config';
2
+ import { inspectSharkcraft, resolveChangedFiles, resolveProjectConfig, } from '@shrkcrft/inspector';
3
3
  import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import * as nodePath from 'node:path';
5
5
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
@@ -19,7 +19,7 @@ import { asJson, header, kv } from "../output/format-output.js";
19
19
  export const gateCommand = {
20
20
  name: 'gate',
21
21
  description: 'Aggregator: runs the code-intelligence quality gates (graph freshness, architecture, impact-since-ref) and reports a single pass/fail.',
22
- usage: 'shrk gate [--since <gitref>] [--fail-on critical,high] [--arch-all] [--disable arch,impact,api-diff] [--api-baseline <path>] [--no-fail-on-breaking] [--strict] [--no-persist] [--json] [--markdown] [--output <path>]\n (the arch gate is baseline-relative by default once a baseline is frozen — fails only on NEW errors; --arch-all fails on total)\n shrk gate scaffold-ci [--provider github|generic] [--force] [--json]\n shrk gate scaffold-hook [--provider husky|raw] [--force] [--json]',
22
+ usage: 'shrk gate [--since <gitref>] [--changed-only] [--staged] [--files a,b,c] [--fail-on critical,high] [--arch-all] [--disable arch,impact,policy,knowledge-symbol,api-diff] [--api-baseline <path>] [--no-fail-on-breaking] [--strict] [--no-persist] [--json] [--markdown] [--output <path>]\n (the arch gate is baseline-relative once a baseline is frozen — fails only on NEW errors; with no baseline it warns on errors rather than going perpetually red — --arch-all fails on total, --strict escalates the warn)\n (--changed-only / --staged / --files / --since scope the wiring + policy + knowledge-symbol gates to the changeset; they also drive the impact gate — --since diffs the gitref, the others analyze the changed-file set)\n shrk gate scaffold-ci [--provider github|generic] [--force] [--json]\n shrk gate scaffold-hook [--provider husky|raw] [--force] [--json]',
23
23
  async run(args) {
24
24
  if (args.positional[0] === 'scaffold-ci') {
25
25
  const sliced = { ...args, positional: args.positional.slice(1) };
@@ -39,30 +39,115 @@ export const gateCommand = {
39
39
  const disableRaw = flagString(args, 'disable');
40
40
  const apiBaseline = flagString(args, 'api-baseline');
41
41
  const noFailOnBreaking = flagBool(args, 'no-fail-on-breaking');
42
- const failOn = failOnRaw
42
+ // `--fail-on` accepts only `high` / `critical`. An unknown token used to
43
+ // silently REPLACE the default `['critical']`, leaving nothing able to fail
44
+ // the gate — reject it loudly (exit 2) instead.
45
+ const failOnTokens = failOnRaw
43
46
  ? failOnRaw.split(',').map((s) => s.trim()).filter(Boolean)
44
47
  : undefined;
48
+ if (failOnTokens) {
49
+ const allowedRisk = new Set(['high', 'critical']);
50
+ const unknown = failOnTokens.filter((t) => !allowedRisk.has(t));
51
+ if (unknown.length > 0) {
52
+ process.stderr.write(`Unknown --fail-on value(s): ${unknown.join(', ')}. Allowed: high, critical.\n`);
53
+ return 2;
54
+ }
55
+ }
56
+ const failOn = failOnTokens && failOnTokens.length > 0
57
+ ? failOnTokens
58
+ : undefined;
45
59
  const disable = disableRaw ? disableRaw.split(',').map((s) => s.trim()).filter(Boolean) : undefined;
46
60
  // --arch-all: fail on TOTAL architecture errors (ignore the frozen baseline).
47
61
  // By default the arch gate is baseline-relative — it fails only on NEW errors.
48
62
  const archAll = flagBool(args, 'arch-all');
49
- // Wiring rules come from the project config; the gate is skipped (never red)
50
- // when none are declared, so this is inert for projects that don't opt in.
51
- // An INVALID config is surfaced (warn) rather than silently disabling wiring.
52
- const loadedConfig = await loadProjectConfig(cwd);
63
+ // Changeset scope. `--changed-only` (tracked + untracked worktree),
64
+ // `--staged`, `--files`, and `--since` all narrow the wiring + policy +
65
+ // knowledge-symbol gates to the change. `--since` additionally drives the
66
+ // (ref-based) impact gate, as before.
67
+ const changedOnly = flagBool(args, 'changed-only');
68
+ const staged = flagBool(args, 'staged');
69
+ const filesRaw = flagString(args, 'files');
70
+ const fileList = filesRaw ? filesRaw.split(',').map((s) => s.trim()).filter(Boolean) : [];
71
+ const wantChangedScope = changedOnly || staged || Boolean(sinceRef) || fileList.length > 0;
72
+ let changedFiles;
73
+ if (wantChangedScope) {
74
+ const resolved = resolveChangedFiles({
75
+ projectRoot: cwd,
76
+ ...(fileList.length > 0 ? { files: fileList } : {}),
77
+ ...(staged ? { staged: true } : {}),
78
+ ...(sinceRef ? { since: sinceRef } : {}),
79
+ ...(changedOnly && !staged && !sinceRef && fileList.length === 0
80
+ ? { includeWorktree: true }
81
+ : {}),
82
+ });
83
+ changedFiles = resolved.files;
84
+ }
85
+ // Wiring + policy rules come from the project config; each gate is skipped
86
+ // (never red) when none are declared, so they're inert for projects that
87
+ // don't opt in. An INVALID config is surfaced (warn) rather than silently
88
+ // disabling the plane.
89
+ const loadedConfig = await resolveProjectConfig(cwd);
53
90
  const wiringRules = loadedConfig.ok ? loadedConfig.value.config.wiringRules ?? [] : [];
54
- const wiringConfigError = loadedConfig.ok ? undefined : loadedConfig.error.message;
91
+ const policyRules = loadedConfig.ok ? loadedConfig.value.config.policyRules ?? [] : [];
92
+ const configError = loadedConfig.ok ? undefined : loadedConfig.error.message;
93
+ // Pack-plane merge notes (missing/invalid pack rule files, dropped
94
+ // collisions) go to stderr so they never pollute the JSON/markdown report
95
+ // on stdout that CI consumes.
96
+ if (loadedConfig.ok) {
97
+ for (const d of loadedConfig.value.planeDiagnostics) {
98
+ process.stderr.write(`plane: ${d}\n`);
99
+ }
100
+ }
101
+ const scopeOpts = wantChangedScope
102
+ ? { changedOnly: true, changedFiles: changedFiles ?? [] }
103
+ : {};
104
+ // Knowledge symbol-ref integrity needs the loaded knowledge entries. The
105
+ // inspection is async, so we build it here and inject it; the gate stays
106
+ // synchronous and resolves the code graph itself. Best-effort — a failed
107
+ // inspection just skips the gate rather than failing `shrk gate`.
108
+ let inspection;
109
+ if (!disable?.includes('knowledge-symbol')) {
110
+ try {
111
+ inspection = await inspectSharkcraft({ cwd });
112
+ }
113
+ catch {
114
+ inspection = undefined;
115
+ }
116
+ }
55
117
  const report = runQualityGates({
56
118
  projectRoot: cwd,
57
119
  ...(archAll ? { arch: { baselineRelative: false } } : {}),
58
- ...(wiringConfigError
59
- ? { wiring: { configError: wiringConfigError } }
60
- : wiringRules.length > 0
61
- ? { wiring: { rules: wiringRules } }
62
- : {}),
120
+ wiring: {
121
+ ...(configError
122
+ ? { configError }
123
+ : wiringRules.length > 0
124
+ ? { rules: wiringRules }
125
+ : {}),
126
+ ...scopeOpts,
127
+ },
128
+ policy: {
129
+ ...(configError
130
+ ? { configError }
131
+ : policyRules.length > 0
132
+ ? { rules: policyRules }
133
+ : {}),
134
+ ...scopeOpts,
135
+ },
136
+ ...(inspection
137
+ ? {
138
+ knowledgeSymbol: {
139
+ inspection,
140
+ ...(wantChangedScope ? { changedFiles: changedFiles ?? [] } : {}),
141
+ },
142
+ }
143
+ : {}),
63
144
  impact: {
64
145
  ...(sinceRef ? { sinceRef } : {}),
65
146
  ...(failOn ? { failOn } : {}),
147
+ // Scope the impact gate to the changeset too: with `--since` we keep the
148
+ // gitref diff; with `--changed-only` / `--staged` / `--files` (and no
149
+ // `--since`) we analyze the resolved changed-file set directly.
150
+ ...(wantChangedScope && !sinceRef ? { files: changedFiles ?? [] } : {}),
66
151
  },
67
152
  ...(apiBaseline
68
153
  ? {
@@ -1 +1 @@
1
- {"version":3,"file":"graph-code-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/graph-code-subverbs.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAiI3F,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBrE;AA4FD,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+DtE;AAiBD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwF1E;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8EpE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFtE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDtE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgMvE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8HtE;AAID;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CpE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkGvE;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgHpE"}
1
+ {"version":3,"file":"graph-code-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/graph-code-subverbs.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAiE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAgLxH,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBrE;AA4FD,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkEtE;AAiBD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2F1E;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+EpE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+FtE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwEtE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2MvE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+HtE;AAID;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEpE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAiGvE;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgHpE"}