@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
@@ -114,10 +114,18 @@ export const recommendCommand = {
114
114
  }));
115
115
  const promotedSet = new Set(promoted.map((p) => p.command));
116
116
  const isGenericDefault = (c) => /^(bun run )?shrk (review|report|impact)\b/i.test(c);
117
+ // The recommender (inspector) already arbitrates the keyword recipe
118
+ // against the shared ranker and may have promoted a `shrk gen <template>`
119
+ // / `shrk task` headline (its `why` starts with "Ranker matched"). That
120
+ // ranker-confirmed scaffold is the engine's corrected route — keep it
121
+ // ahead of even the routing-hint promotion so the headline survives.
122
+ const isRankerPromoted = (r) => /^Ranker matched /.test(r.why);
117
123
  const existing = report.recommendations;
118
- const keptNonGeneric = existing.filter((r) => !promotedSet.has(r.command) && !isGenericDefault(r.command));
119
- const keptGeneric = existing.filter((r) => !promotedSet.has(r.command) && isGenericDefault(r.command));
124
+ const keptRankerPromoted = existing.filter((r) => !promotedSet.has(r.command) && isRankerPromoted(r));
125
+ const keptNonGeneric = existing.filter((r) => !promotedSet.has(r.command) && !isRankerPromoted(r) && !isGenericDefault(r.command));
126
+ const keptGeneric = existing.filter((r) => !promotedSet.has(r.command) && !isRankerPromoted(r) && isGenericDefault(r.command));
120
127
  report.recommendations = [
128
+ ...keptRankerPromoted,
121
129
  ...promoted,
122
130
  ...keptNonGeneric,
123
131
  ...keptGeneric,
@@ -1 +1 @@
1
- {"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,wBAAwB,EAAE,eActC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAa7B,CAAC"}
1
+ {"version":3,"file":"registry.command.d.ts","sourceRoot":"","sources":["../../src/commands/registry.command.ts"],"names":[],"mappings":"AAwBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,wBAAwB,EAAE,eAetC,CAAC;AAsGF,eAAO,MAAM,eAAe,EAAE,eAuB7B,CAAC"}
@@ -1,18 +1,27 @@
1
1
  /**
2
2
  * `shrk registry` commands.
3
3
  *
4
- * shrk registry lifecycle [--json]
4
+ * shrk registry lifecycle [--json] # register/remove symmetry
5
+ * shrk registry <name> list [--json] # every declared id
6
+ * shrk registry <name> exists <id> [--json] # is the id taken? (exit 1 if not)
7
+ * shrk registry <name> where <id> [--json] # declaration (+ consumer) sites
8
+ *
9
+ * `<name>` resolves a `registries[]` declaration in sharkcraft.config.ts — one
10
+ * deterministic multi-root scan that answers "is this id taken / where is it"
11
+ * without an agent re-running a fragile grep.
5
12
  */
6
- import { buildRegistryLifecycleReport, renderRegistryLifecycleReportText, } from '@shrkcrft/inspector';
7
- import { flagBool, resolveCwd, } from "../command-registry.js";
13
+ import { buildRegistryLifecycleReport, renderRegistryLifecycleReportText, resolveProjectConfig, } from '@shrkcrft/inspector';
14
+ import { scanRegistry, registryExists, registryWhere, } from '@shrkcrft/boundaries';
15
+ import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
8
16
  import { asJson } from "../output/format-output.js";
9
17
  export const registryLifecycleCommand = {
10
18
  name: 'lifecycle',
11
- description: 'Scan the workspace for register*/remove* symmetry. Read-only.',
12
- usage: 'shrk registry lifecycle [--json]',
19
+ description: 'Scan the workspace for register/remove symmetry. Read-only.',
20
+ usage: 'shrk registry lifecycle [--scope <dir>] [--json]',
13
21
  async run(args) {
14
22
  const cwd = resolveCwd(args);
15
- const report = buildRegistryLifecycleReport({ projectRoot: cwd });
23
+ const scope = flagString(args, 'scope');
24
+ const report = buildRegistryLifecycleReport({ projectRoot: cwd, ...(scope ? { scope } : {}) });
16
25
  if (flagBool(args, 'json')) {
17
26
  process.stdout.write(asJson(report) + '\n');
18
27
  return report.missingRemovers.length === 0 ? 0 : 1;
@@ -21,17 +30,116 @@ export const registryLifecycleCommand = {
21
30
  return report.missingRemovers.length === 0 ? 0 : 1;
22
31
  },
23
32
  };
33
+ async function loadRegistries(cwd) {
34
+ const loaded = await resolveProjectConfig(cwd);
35
+ if (!loaded.ok)
36
+ return { ok: false, message: loaded.error.message };
37
+ return {
38
+ ok: true,
39
+ registries: loaded.value.config.registries ?? [],
40
+ planeDiagnostics: loaded.value.planeDiagnostics,
41
+ };
42
+ }
43
+ function findRegistry(registries, name) {
44
+ return registries.find((r) => r.name === name);
45
+ }
46
+ /** Run a `list | exists | where` query against a declared registry. */
47
+ async function runRegistryInventory(args, name) {
48
+ const cwd = resolveCwd(args);
49
+ const json = flagBool(args, 'json');
50
+ const action = args.positional[1];
51
+ const id = args.positional[2];
52
+ const loaded = await loadRegistries(cwd);
53
+ if (!loaded.ok) {
54
+ if (json)
55
+ process.stdout.write(asJson({ error: loaded.message }) + '\n');
56
+ else
57
+ process.stderr.write(`Could not load config: ${loaded.message}\n Run \`shrk doctor\` for details.\n`);
58
+ return 1;
59
+ }
60
+ const decl = findRegistry(loaded.registries, name);
61
+ if (!decl) {
62
+ const names = loaded.registries.map((r) => r.name);
63
+ const avail = names.length > 0 ? `Declared registries: ${names.join(', ')}.` : 'No registries declared in sharkcraft.config.ts `registries[]`.';
64
+ if (json)
65
+ process.stdout.write(asJson({ error: `unknown registry "${name}"`, available: names }) + '\n');
66
+ else
67
+ process.stderr.write(`No registry named "${name}". ${avail}\n`);
68
+ return 2;
69
+ }
70
+ const inventory = scanRegistry(cwd, decl);
71
+ if (action === 'list' || action === undefined) {
72
+ // Fold pack-plane merge notes (missing/invalid pack registry files, dropped
73
+ // collisions) into the inventory's own scan diagnostics.
74
+ const diagnostics = [...inventory.diagnostics, ...loaded.planeDiagnostics];
75
+ if (json) {
76
+ process.stdout.write(asJson({ name: inventory.name, count: inventory.entries.length, ids: inventory.entries.map((e) => e.id), diagnostics }) + '\n');
77
+ return 0;
78
+ }
79
+ process.stdout.write(`Registry "${inventory.name}"${inventory.description ? ' — ' + inventory.description : ''}\n`);
80
+ process.stdout.write(` ${inventory.entries.length} id(s)\n`);
81
+ for (const e of inventory.entries) {
82
+ process.stdout.write(` • ${e.id} (${e.sites.length} site${e.sites.length === 1 ? '' : 's'})\n`);
83
+ }
84
+ for (const d of diagnostics)
85
+ process.stdout.write(` ! ${d}\n`);
86
+ return 0;
87
+ }
88
+ if (action === 'exists') {
89
+ if (!id) {
90
+ process.stderr.write(`Usage: shrk registry ${name} exists <id>\n`);
91
+ return 2;
92
+ }
93
+ const exists = registryExists(inventory, id);
94
+ if (json)
95
+ process.stdout.write(asJson({ name: inventory.name, id, exists }) + '\n');
96
+ else
97
+ process.stdout.write(`${exists ? 'yes' : 'no'} — "${id}" is ${exists ? 'declared' : 'NOT declared'} in registry "${inventory.name}".\n`);
98
+ return exists ? 0 : 1;
99
+ }
100
+ if (action === 'where') {
101
+ if (!id) {
102
+ process.stderr.write(`Usage: shrk registry ${name} where <id>\n`);
103
+ return 2;
104
+ }
105
+ const entry = registryWhere(inventory, id);
106
+ if (json) {
107
+ process.stdout.write(asJson({ name: inventory.name, id, found: entry !== undefined, entry: entry ?? null }) + '\n');
108
+ return entry ? 0 : 1;
109
+ }
110
+ if (!entry) {
111
+ process.stdout.write(`"${id}" is not declared in registry "${inventory.name}".\n`);
112
+ return 1;
113
+ }
114
+ process.stdout.write(`"${id}" in registry "${inventory.name}":\n`);
115
+ for (const s of entry.sites)
116
+ process.stdout.write(` declared ${s.file}:${s.line}\n`);
117
+ for (const s of entry.consumerSites ?? [])
118
+ process.stdout.write(` consumed ${s.file}:${s.line}\n`);
119
+ return 0;
120
+ }
121
+ process.stderr.write(`Unknown action "${action}". Usage: shrk registry ${name} list | exists <id> | where <id>\n`);
122
+ return 2;
123
+ }
24
124
  export const registryCommand = {
25
125
  name: 'registry',
26
- description: 'Registry inspections (lifecycle symmetry, etc.). Read-only.',
27
- usage: 'shrk registry lifecycle',
126
+ description: 'Registry inspections: lifecycle symmetry + declared-registry inventory. Read-only.',
127
+ usage: 'shrk registry lifecycle | <name> list | <name> exists <id> | <name> where <id>',
28
128
  async run(args) {
29
129
  const sub = args.positional[0];
30
130
  if (sub === 'lifecycle') {
31
131
  args.positional = args.positional.slice(1);
32
132
  return registryLifecycleCommand.run(args);
33
133
  }
34
- process.stderr.write('Usage: shrk registry lifecycle\n');
134
+ if (sub !== undefined && sub.length > 0) {
135
+ // `<name> list | exists <id> | where <id>` — sub is the registry name.
136
+ return runRegistryInventory(args, sub);
137
+ }
138
+ const cwd = resolveCwd(args);
139
+ const loaded = await loadRegistries(cwd);
140
+ const names = loaded.ok ? loaded.registries.map((r) => r.name) : [];
141
+ process.stderr.write('Usage: shrk registry lifecycle | <name> list | <name> exists <id> | <name> where <id>\n' +
142
+ (names.length > 0 ? `Declared registries: ${names.join(', ')}.\n` : 'No registries declared (sharkcraft.config.ts `registries[]`).\n'));
35
143
  return 2;
36
144
  },
37
145
  };
@@ -8,7 +8,7 @@ const VALID_FORMATS = new Set(['text', 'markdown', 'html', 'json']);
8
8
  export const reportCommand = {
9
9
  name: 'report',
10
10
  description: 'Render runtime reports in text / markdown / html / json. Subcommands: adoption / session / quality / safety / review / coverage / drift / graph.',
11
- usage: 'shrk report <adoption|session <id>|quality|safety|review <packet.json>|coverage|drift|graph> [--format text|markdown|html|json] [--output <path>] [--collapse-long-sections] [--max-items N] [--include-raw-json] [--json]',
11
+ usage: 'shrk report <adoption|session <id>|quality|safety|review <packet.json>|coverage|drift|graph> [--format text|markdown|html|json] [--output <path>] [--collapse-long-sections] [--max-items N] [--json]',
12
12
  async run(args) {
13
13
  const sub = args.positional[0];
14
14
  const sliced = { ...args, positional: args.positional.slice(1) };
@@ -1 +1 @@
1
- {"version":3,"file":"reuse.command.d.ts","sourceRoot":"","sources":["../../src/commands/reuse.command.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA4DhC,eAAO,MAAM,YAAY,EAAE,eA0J1B,CAAC"}
1
+ {"version":3,"file":"reuse.command.d.ts","sourceRoot":"","sources":["../../src/commands/reuse.command.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA4DhC,eAAO,MAAM,YAAY,EAAE,eAqK1B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { GraphStore, GraphQueryApi } from '@shrkcrft/graph';
2
- import { loadProjectConfig } from '@shrkcrft/config';
2
+ import { resolveProjectConfig } from '@shrkcrft/inspector';
3
3
  import { flagBool, flagNumber, resolveCwd, } from "../command-registry.js";
4
4
  import { asJson, header } from "../output/format-output.js";
5
5
  const STOP = new Set([
@@ -49,7 +49,7 @@ export const reuseCommand = {
49
49
  process.stderr.write('Usage: shrk reuse "<what I want to build>" [--limit N] [--json]\n');
50
50
  return 2;
51
51
  }
52
- const loaded = await loadProjectConfig(cwd);
52
+ const loaded = await resolveProjectConfig(cwd);
53
53
  if (!loaded.ok) {
54
54
  const msg = loaded.error.message;
55
55
  if (wantJson) {
@@ -61,14 +61,24 @@ export const reuseCommand = {
61
61
  return 1;
62
62
  }
63
63
  const primitives = loaded.value.config.reusePrimitives ?? [];
64
+ const planeDiagnostics = loaded.value.planeDiagnostics;
65
+ // reuse has no pre-existing diagnostics surface; expose pack-plane merge
66
+ // notes (missing/invalid pack primitive files, dropped collisions) so a
67
+ // pack contribution that failed to load isn't silently invisible.
68
+ const planeJson = planeDiagnostics.length > 0 ? { planeDiagnostics } : {};
69
+ const writePlaneNotes = () => {
70
+ for (const d of planeDiagnostics)
71
+ process.stdout.write(` ! ${d}\n`);
72
+ };
64
73
  if (primitives.length === 0) {
65
74
  if (wantJson) {
66
- process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, results: [] }) + '\n');
75
+ process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, results: [], ...planeJson }) + '\n');
67
76
  return 0;
68
77
  }
69
78
  process.stdout.write(header(`Reuse: "${intent}"`));
70
79
  process.stdout.write(' No reuse primitives configured. Declare `reusePrimitives[]` in sharkcraft.config.ts\n' +
71
80
  ' to map roles/intents to canonical symbols (see docs/reuse.md).\n');
81
+ writePlaneNotes();
72
82
  return 0;
73
83
  }
74
84
  const tokens = tokenize(intent);
@@ -82,13 +92,14 @@ export const reuseCommand = {
82
92
  if (ranked.length === 0) {
83
93
  const roles = [...new Set(primitives.flatMap((p) => p.roles))].sort();
84
94
  if (wantJson) {
85
- process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, results: [], availableRoles: roles }) + '\n');
95
+ process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, results: [], availableRoles: roles, ...planeJson }) + '\n');
86
96
  return 0;
87
97
  }
88
98
  process.stdout.write(header(`Reuse: "${intent}"`));
89
99
  process.stdout.write(' No primitive matched. Available roles:\n');
90
100
  for (const r of roles.slice(0, 40))
91
101
  process.stdout.write(` • ${r}\n`);
102
+ writePlaneNotes();
92
103
  return 0;
93
104
  }
94
105
  const results = ranked.map(({ p, score }) => {
@@ -149,10 +160,11 @@ export const reuseCommand = {
149
160
  return r;
150
161
  });
151
162
  if (wantJson) {
152
- process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, graphIndexed: !!api, results }) + '\n');
163
+ process.stdout.write(asJson({ schema: 'sharkcraft.reuse/v1', intent, graphIndexed: !!api, results, ...planeJson }) + '\n');
153
164
  return 0;
154
165
  }
155
166
  process.stdout.write(header(`Reuse: "${intent}"`));
167
+ writePlaneNotes();
156
168
  if (!api) {
157
169
  process.stdout.write(' (code graph missing — import path/siblings/consumers limited; run `shrk graph index`)\n');
158
170
  }
@@ -1 +1 @@
1
- {"version":3,"file":"review.command.d.ts","sourceRoot":"","sources":["../../src/commands/review.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA6GhC,eAAO,MAAM,aAAa,EAAE,eA+E3B,CAAC"}
1
+ {"version":3,"file":"review.command.d.ts","sourceRoot":"","sources":["../../src/commands/review.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA6GhC,eAAO,MAAM,aAAa,EAAE,eAiG3B,CAAC"}
@@ -77,7 +77,20 @@ function scaffoldOptionsFromArgs(args) {
77
77
  export const reviewCommand = {
78
78
  name: 'review',
79
79
  description: 'Build a PR-review packet: changed files (via git), affected path conventions, relevant rules/templates/pipelines, boundary violations on those files, missing-test heuristic, verification commands. Also: `shrk review scaffold github-action` prints a workflow template with optional `--with-boundaries / --with-coverage / --with-drift / --artifact-only / --comment-placeholder` flags. `shrk review render-comment <packet.json>` renders a Markdown PR comment from a saved packet.',
80
- usage: 'shrk [--cwd <dir>] review [--since <ref>] [--staged] [--files a,b] [--json] | shrk review scaffold github-action [...] | shrk review render-comment <packet.json> [--output <file>]',
80
+ usage: 'shrk [--cwd <dir>] review [files... | --files a,b] [--since <ref>] [--staged] [--no-untracked] [--json] | shrk review scaffold github-action [...] | shrk review render-comment <packet.json> [--output <file>]',
81
+ booleanFlags: new Set([
82
+ 'json',
83
+ 'staged',
84
+ 'untracked',
85
+ 'no-untracked',
86
+ 'v2',
87
+ 'v3',
88
+ 'with-boundaries',
89
+ 'with-coverage',
90
+ 'with-drift',
91
+ 'artifact-only',
92
+ 'comment-placeholder',
93
+ ]),
81
94
  async run(args) {
82
95
  if (args.positional[0] === 'scaffold') {
83
96
  const which = args.positional[1] ?? 'github-action';
@@ -97,11 +110,16 @@ export const reviewCommand = {
97
110
  }
98
111
  const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
99
112
  const since = flagString(args, 'since');
100
- const files = flagList(args, 'files');
113
+ // Accept files via `--files a,b` OR as bare positional args
114
+ // (`shrk review a.ts b.ts`). Positionals were previously dropped silently,
115
+ // yielding a confident "0 changed files" with exit 0.
116
+ const filesFlag = flagList(args, 'files');
117
+ const files = filesFlag.length ? filesFlag : args.positional;
101
118
  const packet = buildReviewPacket(inspection, {
102
119
  ...(since ? { since } : {}),
103
120
  ...(flagBool(args, 'staged') ? { staged: true } : {}),
104
121
  ...(files.length ? { files } : {}),
122
+ ...(flagBool(args, 'no-untracked') ? { untracked: false } : {}),
105
123
  });
106
124
  if (flagBool(args, 'json')) {
107
125
  process.stdout.write(asJson(packet) + '\n');
@@ -1 +1 @@
1
- {"version":3,"file":"rule-graph-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/rule-graph-subverbs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAwB,KAAK,eAAe,EAAmB,MAAM,wBAAwB,CAAC;AAGrG,eAAO,MAAM,gBAAgB,EAAE,eAc9B,CAAC"}
1
+ {"version":3,"file":"rule-graph-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/rule-graph-subverbs.ts"],"names":[],"mappings":"AASA,OAAO,EAAwB,KAAK,eAAe,EAAmB,MAAM,wBAAwB,CAAC;AAGrG,eAAO,MAAM,gBAAgB,EAAE,eAc9B,CAAC"}
@@ -5,7 +5,8 @@
5
5
  * because the verbs operate on a separate `.sharkcraft/bridge/` store
6
6
  * and don't conceptually belong under `shrk graph`.
7
7
  */
8
- import { buildBridge, RuleGraphQueryApi } from '@shrkcrft/rule-graph';
8
+ import { buildBridge, BridgeStore, RuleGraphQueryApi } from '@shrkcrft/rule-graph';
9
+ import { detectGraphFreshness } from '@shrkcrft/graph';
9
10
  import { flagBool, resolveCwd } from "../command-registry.js";
10
11
  import { asJson, header, kv } from "../output/format-output.js";
11
12
  export const ruleGraphCommand = {
@@ -54,13 +55,39 @@ async function runStatus(args) {
54
55
  return 1;
55
56
  }
56
57
  const api = RuleGraphQueryApi.fromStores(cwd);
57
- // No public counters from the API itself just confirm it loads.
58
+ // The bridge maps code-graph files asset registries, so it is stale once
59
+ // the underlying code graph has drifted. Reuse the already-available graph
60
+ // freshness signal (the same one `graph status` reports) rather than a
61
+ // hardcoded 'fresh'. `lastBuiltAt` comes from the bridge manifest.
62
+ let lastBuiltAt = null;
63
+ try {
64
+ lastBuiltAt = new BridgeStore(cwd).loadSnapshot().manifest.lastBuiltAt;
65
+ }
66
+ catch {
67
+ lastBuiltAt = null;
68
+ }
69
+ const fresh = detectGraphFreshness(cwd);
70
+ const behind = fresh.modified.length + fresh.added.length + fresh.deleted.length;
71
+ const state = behind > 0 ? 'stale' : 'fresh';
58
72
  if (wantJson) {
59
- process.stdout.write(asJson({ ok: true, state: 'fresh' }) + '\n');
73
+ process.stdout.write(asJson({
74
+ ok: true,
75
+ state,
76
+ lastBuiltAt,
77
+ modifiedSinceIndex: fresh.modified.length,
78
+ newSinceIndex: fresh.added.length,
79
+ deletedSinceIndex: fresh.deleted.length,
80
+ ...(state === 'stale' ? { nextCommand: 'shrk rule-graph index' } : {}),
81
+ }) + '\n');
60
82
  return 0;
61
83
  }
62
84
  process.stdout.write(header('Rule-graph status'));
63
- process.stdout.write(kv('state', 'fresh') + '\n');
85
+ if (lastBuiltAt)
86
+ process.stdout.write(kv('last built', lastBuiltAt) + '\n');
87
+ process.stdout.write(kv('state', state) + '\n');
88
+ if (state === 'stale') {
89
+ process.stdout.write(`! stale — ${fresh.modified.length} modified, ${fresh.added.length} new, ${fresh.deleted.length} deleted in the code graph since the bridge was built; re-run \`shrk rule-graph index\`\n`);
90
+ }
64
91
  void api;
65
92
  return 0;
66
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"search-structural.command.d.ts","sourceRoot":"","sources":["../../src/commands/search-structural.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,eAkErC,CAAC"}
1
+ {"version":3,"file":"search-structural.command.d.ts","sourceRoot":"","sources":["../../src/commands/search-structural.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,eAkErC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import * as nodePath from 'node:path';
3
3
  import { applyRewritePlan, PatternRegistryStore, planRewrite, runSearch, signRewritePlan, STARTER_PATTERNS, verifySignedRewritePlan, } from '@shrkcrft/structural-search';
4
- import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
4
+ import { flagBool, flagPositiveInt, flagString, resolveCwd, } from "../command-registry.js";
5
5
  import { asJson, header, kv } from "../output/format-output.js";
6
6
  /**
7
7
  * `shrk search structural` — declarative AST pattern matching with
@@ -49,7 +49,7 @@ export const searchStructuralCommand = {
49
49
  process.stderr.write(`Pattern parse error: ${e.message}\n`);
50
50
  return 2;
51
51
  }
52
- const limit = Number(flagString(args, 'limit') ?? '200');
52
+ const limit = flagPositiveInt(args, 'limit', 200);
53
53
  // Rewrite path?
54
54
  const recipeInline = flagString(args, 'rewrite');
55
55
  const recipeFile = flagString(args, 'rewrite-file');
@@ -1 +1 @@
1
- {"version":3,"file":"search.command.d.ts","sourceRoot":"","sources":["../../src/commands/search.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA0BhC,eAAO,MAAM,aAAa,EAAE,eAuG3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eA+FrC,CAAC;AAwEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,eASzC,CAAC"}
1
+ {"version":3,"file":"search.command.d.ts","sourceRoot":"","sources":["../../src/commands/search.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAkChC,eAAO,MAAM,aAAa,EAAE,eAsH3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eA+FrC,CAAC;AAwEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,eASzC,CAAC"}
@@ -1,8 +1,15 @@
1
- import { buildSearchIndex, entrypointBanner, explainSearchTuning, inspectSharkcraft, loadConstructs, loadPlaybooks, loadSearchTuning, renderSearchText, renderTuningExplainHtml, renderTuningExplainMarkdown, searchIndex, SearchKind, SearchSource, } from '@shrkcrft/inspector';
1
+ import { buildSearchIndex, entrypointBanner, explainSearchTuning, inspectSharkcraft, loadConstructs, loadPlaybooks, loadSearchTuning, renderSearchText, renderTuningExplainHtml, renderTuningExplainMarkdown, searchIndex, SearchKind, SearchResultKind, SearchSource, } from '@shrkcrft/inspector';
2
2
  import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
3
3
  import { asJson } from "../output/format-output.js";
4
4
  const VALID_KINDS = new Set(Object.values(SearchKind));
5
5
  const VALID_SOURCES = new Set(Object.values(SearchSource));
6
+ // Universal-search (v2) filter vocabularies. `--kind` is one of the
7
+ // `SearchResultKind` enum values; `--source` is the local/pack origin the
8
+ // engine actually filters hits by (`makeHit` only ever emits these two). Both
9
+ // are validated up front so a typo'd value rejects loudly instead of silently
10
+ // filtering every hit out and returning an empty result with exit 0.
11
+ const VALID_RESULT_KINDS = Object.values(SearchResultKind);
12
+ const VALID_RESULT_SOURCES = ['local', 'pack'];
6
13
  function parseKinds(args) {
7
14
  const list = flagList(args, 'type').concat(flagList(args, 'kind'));
8
15
  if (list.length === 0)
@@ -48,6 +55,17 @@ export const searchCommand = {
48
55
  const { buildUniversalSearch, renderUniversalSearchText, renderUniversalSearchMarkdown, } = await import('@shrkcrft/inspector');
49
56
  const kindFilter = flagString(args, 'kind');
50
57
  const sourceFilter = flagString(args, 'source');
58
+ // Validate the filter vocabularies BEFORE building opts — universal-search
59
+ // silently drops every non-matching hit, so an unknown `--kind`/`--source`
60
+ // would otherwise return an empty result with exit 0.
61
+ if (kindFilter && !VALID_RESULT_KINDS.includes(kindFilter)) {
62
+ process.stderr.write(`Unknown --kind ${kindFilter}. Valid: ${VALID_RESULT_KINDS.join(', ')}\n`);
63
+ return 2;
64
+ }
65
+ if (sourceFilter && !VALID_RESULT_SOURCES.includes(sourceFilter)) {
66
+ process.stderr.write(`Unknown --source ${sourceFilter}. Valid: ${VALID_RESULT_SOURCES.join(', ')}\n`);
67
+ return 2;
68
+ }
51
69
  const limit = flagNumber(args, 'limit');
52
70
  const opts = {};
53
71
  if (kindFilter)
@@ -1 +1 @@
1
- {"version":3,"file":"simulate.command.d.ts","sourceRoot":"","sources":["../../src/commands/simulate.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,eAAe,EAAE,eAmD7B,CAAC"}
1
+ {"version":3,"file":"simulate.command.d.ts","sourceRoot":"","sources":["../../src/commands/simulate.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,eAAe,EAAE,eAyD7B,CAAC"}
@@ -3,10 +3,11 @@ import * as nodePath from 'node:path';
3
3
  import { inspectSharkcraft, OrchestrationMode, renderWorkflowSimulationText, simulateWorkflow, } from '@shrkcrft/inspector';
4
4
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
5
5
  import { asJson } from "../output/format-output.js";
6
+ const VALID_SIMULATION_MODES = ['conservative', 'balanced', 'aggressive'];
6
7
  export const simulateCommand = {
7
8
  name: 'simulate',
8
9
  description: 'Predict what a workflow would do without executing anything. Read-only. Optionally takes a playbook or pipeline id.',
9
- usage: 'shrk simulate "<task>" [--playbook <id>] [--pipeline <id>] [--bundle] [--mode conservative|balanced|aggressive] [--json] [--output <file>]',
10
+ usage: 'shrk simulate "<task>" [--playbook <id>] [--pipeline <id>] [--mode conservative|balanced|aggressive] [--json] [--output <file>]',
10
11
  async run(args) {
11
12
  const task = args.positional.join(' ').trim();
12
13
  if (!task) {
@@ -18,6 +19,10 @@ export const simulateCommand = {
18
19
  const playbookId = flagString(args, 'playbook');
19
20
  const pipelineId = flagString(args, 'pipeline');
20
21
  const modeRaw = (flagString(args, 'mode') ?? '').toLowerCase();
22
+ // Warn on a typo'd --mode rather than silently treating it as balanced.
23
+ if (modeRaw && !VALID_SIMULATION_MODES.includes(modeRaw)) {
24
+ process.stderr.write(`Unknown --mode ${modeRaw}. Valid: ${VALID_SIMULATION_MODES.join(', ')} (defaulting to balanced).\n`);
25
+ }
21
26
  const mode = modeRaw === 'conservative'
22
27
  ? OrchestrationMode.Conservative
23
28
  : modeRaw === 'aggressive'
@@ -1 +1 @@
1
- {"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"AAyBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAmFhC,eAAO,MAAM,mBAAmB,EAAE,eAuNjC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,EAAE,eAmF1C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,uBAAuB,EAAE,eAwBrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,EAAE,eAkCrC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAmH/C,CAAC;AA2JF;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAoH/C,CAAC;AA2JF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAgG/C,CAAC;AAyPF,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,EAAE,eAuHhD,CAAC;AAMF,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,EAAE,eAsCjD,CAAC;AAqJF,UAAU,eAAe;IACvB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAWzF"}
1
+ {"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"AA2BA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAmFhC,eAAO,MAAM,mBAAmB,EAAE,eAmQjC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,EAAE,eAsF1C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,uBAAuB,EAAE,eAwBrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,EAAE,eAkCrC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAmH/C,CAAC;AA2JF;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAoH/C,CAAC;AA2JF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAgG/C,CAAC;AAyPF,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,EAAE,eAuHhD,CAAC;AAMF,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,EAAE,eAsCjD,CAAC;AAqJF,UAAU,eAAe;IACvB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAWzF"}