@shrkcrft/cli 0.1.0-alpha.7 → 0.1.0-alpha.9

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 (46) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/boundaries.command.d.ts.map +1 -1
  3. package/dist/commands/boundaries.command.js +12 -0
  4. package/dist/commands/check.command.d.ts.map +1 -1
  5. package/dist/commands/check.command.js +30 -20
  6. package/dist/commands/command-catalog.d.ts.map +1 -1
  7. package/dist/commands/command-catalog.js +14 -1
  8. package/dist/commands/doctor.command.d.ts.map +1 -1
  9. package/dist/commands/doctor.command.js +7 -8
  10. package/dist/commands/help.command.d.ts.map +1 -1
  11. package/dist/commands/help.command.js +8 -6
  12. package/dist/commands/init.command.d.ts.map +1 -1
  13. package/dist/commands/init.command.js +13 -37
  14. package/dist/commands/mcp.command.d.ts.map +1 -1
  15. package/dist/commands/mcp.command.js +131 -2
  16. package/dist/commands/onboard.command.d.ts.map +1 -1
  17. package/dist/commands/onboard.command.js +15 -3
  18. package/dist/commands/packs-new.d.ts.map +1 -1
  19. package/dist/commands/packs-new.js +10 -3
  20. package/dist/commands/packs.command.d.ts.map +1 -1
  21. package/dist/commands/packs.command.js +17 -3
  22. package/dist/commands/review.command.d.ts.map +1 -1
  23. package/dist/commands/review.command.js +28 -2
  24. package/dist/init/init-templates.d.ts.map +1 -1
  25. package/dist/init/init-templates.js +113 -133
  26. package/dist/main.d.ts +1 -1
  27. package/dist/main.d.ts.map +1 -1
  28. package/dist/main.js +35 -13
  29. package/dist/output/failure-hints.d.ts +9 -1
  30. package/dist/output/failure-hints.d.ts.map +1 -1
  31. package/dist/output/failure-hints.js +8 -2
  32. package/dist/output/watch-loop.d.ts +1 -9
  33. package/dist/output/watch-loop.d.ts.map +1 -1
  34. package/dist/output/watch-loop.js +3 -13
  35. package/dist/schemas/json-schemas.d.ts +36 -36
  36. package/dist/schemas/json-schemas.js +36 -36
  37. package/dist/surface/about.d.ts.map +1 -1
  38. package/dist/surface/about.js +15 -37
  39. package/dist/surface/no-args-landing.d.ts.map +1 -1
  40. package/dist/surface/no-args-landing.js +13 -9
  41. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  42. package/dist/surface/surface-config-writer.js +11 -23
  43. package/package.json +25 -26
  44. package/dist/init/paths-advisory.d.ts +0 -20
  45. package/dist/init/paths-advisory.d.ts.map +0 -1
  46. package/dist/init/paths-advisory.js +0 -88
@@ -1,10 +1,18 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
1
9
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
10
  import * as nodePath from 'node:path';
11
+ import { pathToFileURL } from 'node:url';
3
12
  import { buildPackDoctorReport, buildPackSignatureStatusReport, checkPackSymbolCompat, explainPackSignatureStatus, inspectSharkcraft, mergePackReleaseChecks, runPackReleaseCheck, runPackReleaseChecksForReport, } from '@shrkcrft/inspector';
4
13
  import { PACK_SECRET_ENV, signPackManifest, validatePackManifest, verifyPackManifest, } from '@shrkcrft/plugin-api';
5
14
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
6
15
  import { asJson, header, kv } from "../output/format-output.js";
7
- import { importModuleViaLoader } from '@shrkcrft/core';
8
16
  function statusLabel(valid) {
9
17
  return valid ? 'OK ' : 'INVALID';
10
18
  }
@@ -545,7 +553,7 @@ async function loadManifestFromPath(manifestPath) {
545
553
  if (manifestPath.endsWith('.json')) {
546
554
  return JSON.parse(readFileSync(manifestPath, 'utf8'));
547
555
  }
548
- const mod = (await importModuleViaLoader(manifestPath));
556
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(manifestPath).href)));
549
557
  return (mod.default ?? mod);
550
558
  }
551
559
  export const packsSignCommand = {
@@ -765,11 +773,17 @@ export const packsCompatCommand = {
765
773
  : nodePath.resolve(cwd, consumerRootRaw)
766
774
  : null;
767
775
  const distAware = flagBool(args, 'dist-aware');
776
+ const fastMode = flagBool(args, 'fast');
768
777
  // Run a release-check first; surfaces helper-missing diagnostics.
769
778
  const release = await runPackReleaseCheck(abs);
770
779
  const helperMissing = release.findings.filter((f) => f.code === 'contribution-helper-missing');
771
780
  // Symbol-level diff against the consumer's installed plugin-api.
772
- const symbol = checkPackSymbolCompat({ packPath: abs, consumerRoot, distAware });
781
+ const symbol = checkPackSymbolCompat({
782
+ packPath: abs,
783
+ consumerRoot,
784
+ distAware,
785
+ mode: fastMode ? 'fast' : 'ts',
786
+ });
773
787
  const passed = helperMissing.length === 0 && symbol.compatible;
774
788
  if (flagBool(args, 'json')) {
775
789
  process.stdout.write(asJson({
@@ -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,eA8F3B,CAAC"}
@@ -95,8 +95,21 @@ export const reviewCommand = {
95
95
  if (args.positional[0] === 'packet') {
96
96
  return runPacket(args);
97
97
  }
98
- const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
98
+ const cwd = resolveCwd(args);
99
+ const inspection = await inspectSharkcraft({ cwd });
99
100
  const since = flagString(args, 'since');
101
+ if (since) {
102
+ const { verifyGitRef } = await import('@shrkcrft/inspector');
103
+ const verify = verifyGitRef(cwd, since);
104
+ if (!verify.valid) {
105
+ process.stderr.write(`error: --since ref "${since}" does not resolve to a commit in this repository.\n` +
106
+ (verify.suggestions && verify.suggestions.length > 0
107
+ ? `\nDid you mean:\n${verify.suggestions.map((s) => ` --since ${s}`).join('\n')}\n`
108
+ : '') +
109
+ '\nUse `git branch -a` to list available refs.\n');
110
+ return 2;
111
+ }
112
+ }
100
113
  const files = flagList(args, 'files');
101
114
  const packet = buildReviewPacket(inspection, {
102
115
  ...(since ? { since } : {}),
@@ -295,8 +308,21 @@ function isReviewPacketV3Shape(v) {
295
308
  return v.schema === REVIEW_PACKET_V3_SCHEMA;
296
309
  }
297
310
  async function runPacket(args) {
298
- const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
311
+ const cwd = resolveCwd(args);
312
+ const inspection = await inspectSharkcraft({ cwd });
299
313
  const since = flagString(args, 'since');
314
+ if (since) {
315
+ const { verifyGitRef } = await import('@shrkcrft/inspector');
316
+ const verify = verifyGitRef(cwd, since);
317
+ if (!verify.valid) {
318
+ process.stderr.write(`error: --since ref "${since}" does not resolve to a commit in this repository.\n` +
319
+ (verify.suggestions && verify.suggestions.length > 0
320
+ ? `\nDid you mean:\n${verify.suggestions.map((s) => ` --since ${s}`).join('\n')}\n`
321
+ : '') +
322
+ '\nUse `git branch -a` to list available refs.\n');
323
+ return 2;
324
+ }
325
+ }
300
326
  const files = flagList(args, 'files');
301
327
  const v2 = flagBool(args, 'v2');
302
328
  const baselineFile = flagString(args, 'quality-baseline');
@@ -1 +1 @@
1
- {"version":3,"file":"init-templates.d.ts","sourceRoot":"","sources":["../../src/init/init-templates.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA2W1C,CAAC"}
1
+ {"version":3,"file":"init-templates.d.ts","sourceRoot":"","sources":["../../src/init/init-templates.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA4Z1C,CAAC"}
@@ -1,18 +1,9 @@
1
- // Self-contained scaffolding for `shrk init --legacy`. Every generated
2
- // `sharkcraft/*.ts` file must work in a brand-new downstream repo where
3
- // no `@shrkcrft/*` packages are installed beyond the CLI itself. The
4
- // loaders (knowledge / templates / pipelines / path-conventions) are
5
- // shape-agnostic — they accept any object with the required string
6
- // fields — so each emitted file declares its own minimal helpers +
7
- // enum-like constants inline.
8
1
  export const INIT_FILES = [
9
2
  {
10
3
  relativePath: 'sharkcraft.config.ts',
11
- content: `// Generated by \`shrk init --legacy\`. Plain default export — no
12
- // @shrkcrft/* import required. The config loader validates this object
13
- // by shape (zod), so the literal works without a helper call.
4
+ content: `import { defineSharkCraftConfig } from '@shrkcrft/config';
14
5
 
15
- export default {
6
+ export default defineSharkCraftConfig({
16
7
  projectName: 'my-project',
17
8
  description: 'A SharkCraft-powered repository.',
18
9
  knowledgeFiles: ['knowledge.ts'],
@@ -22,36 +13,12 @@ export default {
22
13
  docsFiles: ['docs/overview.md', 'docs/architecture.md', 'docs/quick-start.md'],
23
14
  defaultMaxTokens: 4000,
24
15
  defaultScope: ['typescript'],
25
- };
16
+ });
26
17
  `,
27
18
  },
28
19
  {
29
20
  relativePath: 'knowledge.ts',
30
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
31
- // The knowledge loader is shape-agnostic; it accepts any object whose
32
- // \`id\`, \`title\`, and \`content\` are strings.
33
-
34
- const KnowledgePriority = {
35
- Critical: 'critical',
36
- High: 'high',
37
- Medium: 'medium',
38
- Low: 'low',
39
- } as const;
40
-
41
- const KnowledgeType = {
42
- Rule: 'rule',
43
- Path: 'path',
44
- Template: 'template',
45
- Architecture: 'architecture',
46
- Technical: 'technical',
47
- Convention: 'convention',
48
- Workflow: 'workflow',
49
- Warning: 'warning',
50
- } as const;
51
-
52
- function defineKnowledgeEntry<T>(entry: T): T {
53
- return entry;
54
- }
21
+ content: `import { defineKnowledgeEntry, KnowledgeType, KnowledgePriority } from '@shrkcrft/knowledge';
55
22
 
56
23
  export const projectOverview = defineKnowledgeEntry({
57
24
  id: 'project.overview',
@@ -100,20 +67,8 @@ export default [projectOverview, aiAgentBriefing, generationSafety];
100
67
  },
101
68
  {
102
69
  relativePath: 'rules.ts',
103
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
104
-
105
- const KnowledgePriority = {
106
- Critical: 'critical',
107
- High: 'high',
108
- Medium: 'medium',
109
- Low: 'low',
110
- } as const;
111
-
112
- const KnowledgeType = { Rule: 'rule' } as const;
113
-
114
- function defineRule<T>(rule: T): T {
115
- return { ...rule, type: KnowledgeType.Rule } as T;
116
- }
70
+ content: `import { defineRule } from '@shrkcrft/rules';
71
+ import { KnowledgePriority } from '@shrkcrft/knowledge';
117
72
 
118
73
  export const tsNamingClasses = defineRule({
119
74
  id: 'typescript.naming.classes',
@@ -187,15 +142,81 @@ export default [
187
142
  },
188
143
  {
189
144
  relativePath: 'paths.ts',
190
- content: PATHS_PLACEHOLDER_CONTENT(),
145
+ content: `import { definePathConvention } from '@shrkcrft/paths';
146
+ import { KnowledgePriority } from '@shrkcrft/knowledge';
147
+
148
+ export const appSrc = definePathConvention({
149
+ id: 'app.src',
150
+ title: 'Application source root',
151
+ path: 'src',
152
+ description: 'All application source lives here.',
153
+ priority: KnowledgePriority.Critical,
154
+ scope: ['typescript'],
155
+ tags: ['source-path', 'root'],
156
+ appliesWhen: ['generate-code'],
157
+ });
158
+
159
+ export const services = definePathConvention({
160
+ id: 'app.services',
161
+ title: 'Application services',
162
+ path: 'src/services',
163
+ description: 'Application services live here.',
164
+ priority: KnowledgePriority.High,
165
+ scope: ['typescript', 'backend'],
166
+ tags: ['service', 'source-path'],
167
+ appliesWhen: ['generate-service', 'create-business-logic'],
168
+ });
169
+
170
+ export const utils = definePathConvention({
171
+ id: 'app.utils',
172
+ title: 'Utilities',
173
+ path: 'src/utils',
174
+ description: 'Pure functions, no side effects.',
175
+ priority: KnowledgePriority.Medium,
176
+ scope: ['typescript'],
177
+ tags: ['util', 'source-path'],
178
+ appliesWhen: ['generate-utility'],
179
+ });
180
+
181
+ export const features = definePathConvention({
182
+ id: 'app.features',
183
+ title: 'Feature folders',
184
+ path: 'src/features',
185
+ description: 'Vertical feature slices. Use this for end-to-end features.',
186
+ priority: KnowledgePriority.Medium,
187
+ scope: ['typescript'],
188
+ tags: ['feature', 'source-path'],
189
+ appliesWhen: ['generate-feature'],
190
+ });
191
+
192
+ export const tests = definePathConvention({
193
+ id: 'app.tests',
194
+ title: 'Test files',
195
+ path: 'tests',
196
+ description: 'Test files (or co-located *.spec.ts next to the unit under test).',
197
+ priority: KnowledgePriority.Medium,
198
+ scope: ['typescript', 'testing'],
199
+ tags: ['test', 'source-path'],
200
+ appliesWhen: ['generate-test'],
201
+ });
202
+
203
+ export const docs = definePathConvention({
204
+ id: 'app.docs',
205
+ title: 'Documentation',
206
+ path: 'docs',
207
+ description: 'Long-form human-readable docs (optional).',
208
+ priority: KnowledgePriority.Low,
209
+ scope: ['typescript'],
210
+ tags: ['docs'],
211
+ appliesWhen: ['add-docs'],
212
+ });
213
+
214
+ export default [appSrc, services, utils, features, tests, docs];
215
+ `,
191
216
  },
192
217
  {
193
218
  relativePath: 'templates.ts',
194
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
195
-
196
- function defineTemplate<T>(template: T): T {
197
- return template;
198
- }
219
+ content: `import { defineTemplate } from '@shrkcrft/templates';
199
220
 
200
221
  export const tsService = defineTemplate({
201
222
  id: 'typescript.service',
@@ -208,8 +229,8 @@ export const tsService = defineTemplate({
208
229
  { name: 'name', required: true, description: 'kebab-case file name (e.g. user-profile)' },
209
230
  { name: 'className', required: true, description: 'PascalCase class name (e.g. UserProfileService)' },
210
231
  ],
211
- targetPath: ({ name }: { name: string }) => \`src/services/\${name}.service.ts\`,
212
- content: ({ className }: { className: string }) => \`export class \${className} {
232
+ targetPath: ({ name }) => \`src/services/\${name}.service.ts\`,
233
+ content: ({ className }) => \`export class \${className} {
213
234
  constructor() {}
214
235
 
215
236
  init(): void {
@@ -232,14 +253,42 @@ export const tsUtility = defineTemplate({
232
253
  { name: 'name', required: true, description: 'kebab-case file name' },
233
254
  { name: 'camel', required: true, description: 'camelCase function name' },
234
255
  ],
235
- targetPath: ({ name }: { name: string }) => \`src/utils/\${name}.ts\`,
236
- content: ({ camel }: { camel: string }) => \`export function \${camel}(input: unknown): unknown {
256
+ targetPath: ({ name }) => \`src/utils/\${name}.ts\`,
257
+ content: ({ camel }) => \`export function \${camel}(input: unknown): unknown {
237
258
  return input;
238
259
  }
239
260
  \`,
240
261
  postGenerationNotes: ['Keep utilities pure. No side effects, no shared mutable state.'],
241
262
  });
242
263
 
264
+ export const tsFeatureFolder = defineTemplate({
265
+ id: 'typescript.feature',
266
+ name: 'TypeScript Feature folder',
267
+ description: 'Creates a vertical feature slice (index + service + types).',
268
+ tags: ['typescript', 'feature'],
269
+ scope: ['typescript'],
270
+ appliesWhen: ['generate-feature'],
271
+ variables: [
272
+ { name: 'name', required: true },
273
+ { name: 'pascal', required: true },
274
+ ],
275
+ files: ({ name, pascal }) => [
276
+ {
277
+ targetPath: \`src/features/\${name}/index.ts\`,
278
+ content: \`export * from './\${name}.service.ts';\nexport * from './\${name}.types.ts';\n\`,
279
+ },
280
+ {
281
+ targetPath: \`src/features/\${name}/\${name}.service.ts\`,
282
+ content: \`import type { I\${pascal}Config } from './\${name}.types.ts';\n\nexport class \${pascal}Service {\n constructor(private readonly config: I\${pascal}Config) {}\n}\n\`,
283
+ },
284
+ {
285
+ targetPath: \`src/features/\${name}/\${name}.types.ts\`,
286
+ content: \`export interface I\${pascal}Config {\n enabled: boolean;\n}\n\`,
287
+ },
288
+ ],
289
+ postGenerationNotes: ['Add tests under tests/<feature> or co-located *.spec.ts.'],
290
+ });
291
+
243
292
  export const tsTest = defineTemplate({
244
293
  id: 'typescript.test',
245
294
  name: 'TypeScript Test File',
@@ -251,18 +300,11 @@ export const tsTest = defineTemplate({
251
300
  { name: 'name', required: true },
252
301
  { name: 'pascal', required: true },
253
302
  ],
254
- targetPath: ({ name }: { name: string }) => \`tests/\${name}.spec.ts\`,
255
- content: ({ pascal }: { pascal: string }) => \`import { describe, expect, test } from 'bun:test';
256
-
257
- describe('\${pascal}', () => {
258
- test('placeholder', () => {
259
- expect(true).toBe(true);
260
- });
261
- });
262
- \`,
303
+ targetPath: ({ name }) => \`tests/\${name}.spec.ts\`,
304
+ content: ({ pascal }) => \`import { describe, expect, test } from 'bun:test';\n\ndescribe('\${pascal}', () => {\n test('placeholder', () => {\n expect(true).toBe(true);\n });\n});\n\`,
263
305
  });
264
306
 
265
- export default [tsService, tsUtility, tsTest];
307
+ export default [tsService, tsUtility, tsFeatureFolder, tsTest];
266
308
  `,
267
309
  },
268
310
  {
@@ -369,65 +411,3 @@ Lower-priority work for SharkCraft-powered knowledge in this repo.
369
411
  `,
370
412
  },
371
413
  ];
372
- function PATHS_PLACEHOLDER_CONTENT() {
373
- // The legacy seed ships generic path examples. The modern preset path
374
- // (`shrk init --preset <id>`) is workspace-aware. To avoid emitting
375
- // broken defaults like `src/services/` in repos that don't have them,
376
- // the legacy seed now ships *commented* examples plus an `// TODO:`
377
- // marker the human is expected to fill in. The runner-instructions in
378
- // docs/onboarding.md describe how to derive paths from the live repo.
379
- return `// Local helpers — keep this file self-contained (no @shrkcrft/* imports).
380
-
381
- const KnowledgePriority = {
382
- Critical: 'critical',
383
- High: 'high',
384
- Medium: 'medium',
385
- Low: 'low',
386
- } as const;
387
-
388
- const KnowledgeType = { Path: 'path' } as const;
389
-
390
- function definePathConvention<T>(convention: T): T {
391
- return { ...convention, type: KnowledgeType.Path } as T;
392
- }
393
-
394
- // TODO: replace the examples below with real path conventions for this
395
- // repository. Run \`shrk onboard --dry-run\` to see what the inference
396
- // engine detects from the workspace.
397
-
398
- export const appSrc = definePathConvention({
399
- id: 'app.src',
400
- title: 'Application source root',
401
- path: 'src',
402
- description: 'All application source lives here. Adjust to match this repo.',
403
- priority: KnowledgePriority.Critical,
404
- scope: ['typescript'],
405
- tags: ['source-path', 'root'],
406
- appliesWhen: ['generate-code'],
407
- });
408
-
409
- export const tests = definePathConvention({
410
- id: 'app.tests',
411
- title: 'Test files',
412
- path: 'tests',
413
- description: 'Test files. Many repos co-locate \`*.spec.ts\` next to the unit under test instead — pick one.',
414
- priority: KnowledgePriority.Medium,
415
- scope: ['typescript', 'testing'],
416
- tags: ['test', 'source-path'],
417
- appliesWhen: ['generate-test'],
418
- });
419
-
420
- export const docs = definePathConvention({
421
- id: 'app.docs',
422
- title: 'Documentation',
423
- path: 'docs',
424
- description: 'Long-form human-readable docs (optional).',
425
- priority: KnowledgePriority.Low,
426
- scope: ['typescript'],
427
- tags: ['docs'],
428
- appliesWhen: ['add-docs'],
429
- });
430
-
431
- export default [appSrc, tests, docs];
432
- `;
433
- }
package/dist/main.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  import { CommandRegistry } from './command-registry.js';
3
3
  export declare function buildRegistry(): CommandRegistry;
4
4
  export declare function runCli(argv: readonly string[]): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EACL,eAAe,EAIhB,MAAM,uBAAuB,CAAC;AAyV/B,wBAAgB,aAAa,IAAI,eAAe,CA2V/C;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrE;AA6FD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA4CxE"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EACL,eAAe,EAIhB,MAAM,uBAAuB,CAAC;AAqX/B,wBAAgB,aAAa,IAAI,eAAe,CA2V/C;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrE;AAqGD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA4CxE"}
package/dist/main.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  import { CommandRegistry, extractGlobalCwd, parseArgs, } from "./command-registry.js";
3
3
  import { initCommand } from "./commands/init.command.js";
4
4
  import { inspectCommand } from "./commands/inspect.command.js";
@@ -120,6 +120,33 @@ import { buildSurfaceSummary, findCommandInSummary } from "./surface/surface-sum
120
120
  import { makeSurfaceNotEnabledError, renderSurfaceNotEnabledText, SURFACE_NOT_ENABLED_EXIT_CODE, } from "./surface/not-enabled-error.js";
121
121
  import { extractCommandPath, recordUsage, sanitizeFlagNames, } from "./usage/usage-log.js";
122
122
  import { loadProjectConfig } from '@shrkcrft/config';
123
+ import { initTokenizer } from '@shrkcrft/context';
124
+ import { statSync } from 'node:fs';
125
+ import { resolve as resolvePath } from 'node:path';
126
+ function isUsableDirectory(path) {
127
+ try {
128
+ return statSync(path).isDirectory();
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ }
134
+ let tokenizerInitPromise = null;
135
+ /**
136
+ * Best-effort one-time upgrade from the estimator to gpt-tokenizer.
137
+ *
138
+ * The estimator is always active before this resolves, so commands that
139
+ * race the load still get a budget-safe (if slightly off) count. Subsequent
140
+ * `countTokens` calls get the real BPE count.
141
+ */
142
+ async function ensureTokenizerReady() {
143
+ if (process.env.SHARKCRAFT_NO_REAL_TOKENIZER === '1')
144
+ return;
145
+ if (!tokenizerInitPromise) {
146
+ tokenizerInitPromise = initTokenizer().catch(() => false);
147
+ }
148
+ await tokenizerInitPromise;
149
+ }
123
150
  export function buildRegistry() {
124
151
  const registry = new CommandRegistry();
125
152
  registry.register(initCommand);
@@ -491,9 +518,15 @@ async function isUsageEnabled(cwd) {
491
518
  }
492
519
  }
493
520
  async function runCliInner(argv) {
521
+ await ensureTokenizerReady();
494
522
  const registry = buildRegistry();
495
523
  // Pre-parse the global --cwd so it can appear anywhere (incl. before the command).
496
524
  const { cwd: globalCwd, rest: cleanArgv } = extractGlobalCwd(argv);
525
+ if (globalCwd !== undefined && !isUsableDirectory(globalCwd)) {
526
+ process.stderr.write(`error: --cwd "${globalCwd}" is not an existing directory.\n` +
527
+ `Resolved to: ${resolvePath(globalCwd)}\n`);
528
+ return 2;
529
+ }
497
530
  const [first] = cleanArgv;
498
531
  // bare invocation lands on the curated tiered view.
499
532
  if (!first) {
@@ -678,19 +711,8 @@ function printDidYouMean(attempted) {
678
711
  process.stderr.write(renderErrorFooter(footer));
679
712
  }
680
713
  // Entry point when invoked directly.
681
- //
682
- // Bun exposes `import.meta.main`; Node does not. When Node runs the
683
- // compiled `dist/main.js` directly the path-suffix check (`main.js`)
684
- // catches it. The npm bin shim points at `shrk` so that suffix also
685
- // triggers it. Source dev under Bun still runs via `main.ts`.
686
714
  const isMain = typeof import.meta !== 'undefined' && import.meta.main === true;
687
- const entryPath = process.argv[1] ?? '';
688
- if (isMain ||
689
- entryPath.endsWith('main.ts') ||
690
- entryPath.endsWith('main.js') ||
691
- entryPath.endsWith('shrk') ||
692
- entryPath.endsWith('shrk.js') ||
693
- entryPath.endsWith('shrk.cmd')) {
715
+ if (isMain || process.argv[1]?.endsWith('main.ts') || process.argv[1]?.endsWith('shrk')) {
694
716
  const argv = process.argv.slice(2);
695
717
  runCli(argv).then((code) => process.exit(code), (err) => {
696
718
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
@@ -12,7 +12,15 @@ export interface IFailureHint {
12
12
  doc?: string;
13
13
  }
14
14
  export declare function renderFailureHints(hints: readonly IFailureHint[]): string;
15
- export declare function doctorHints(): IFailureHint[];
15
+ export interface IDoctorHintsContext {
16
+ /**
17
+ * True when the workspace has no `sharkcraft/` folder. On a fresh repo
18
+ * the right next step is `shrk init`, not advanced suppressions/watch
19
+ * loops.
20
+ */
21
+ sharkcraftFolderMissing?: boolean;
22
+ }
23
+ export declare function doctorHints(context?: IDoctorHintsContext): IFailureHint[];
16
24
  export declare function staleKnowledgeHints(): IFailureHint[];
17
25
  export declare function templateDriftHints(): IFailureHint[];
18
26
  export declare function fuzzyImpactAmbiguousHints(): IFailureHint[];
@@ -1 +1 @@
1
- {"version":3,"file":"failure-hints.d.ts","sourceRoot":"","sources":["../../src/output/failure-hints.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAUzE;AAID,wBAAgB,WAAW,IAAI,YAAY,EAAE,CAM5C;AAED,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CAMpD;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAMnD;AAED,wBAAgB,yBAAyB,IAAI,YAAY,EAAE,CAK1D;AAED,wBAAgB,6BAA6B,IAAI,YAAY,EAAE,CAK9D;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAKnD;AAED,wBAAgB,wBAAwB,IAAI,YAAY,EAAE,CAKzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAiB9D;AAED,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,mBAAmB,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,uBAAuB,GACvB,kBAAkB,GAClB,eAAe,GACf,2BAA2B,GAC3B,sBAAsB,GACtB,+BAA+B,GAC/B,wBAAwB,GACxB,8BAA8B,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,GAAG,SAAS,CA4E3G"}
1
+ {"version":3,"file":"failure-hints.d.ts","sourceRoot":"","sources":["../../src/output/failure-hints.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAUzE;AAID,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,EAAE,CAa7E;AAED,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CAMpD;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAMnD;AAED,wBAAgB,yBAAyB,IAAI,YAAY,EAAE,CAK1D;AAED,wBAAgB,6BAA6B,IAAI,YAAY,EAAE,CAK9D;AAED,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAKnD;AAED,wBAAgB,wBAAwB,IAAI,YAAY,EAAE,CAKzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAiB9D;AAED,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,mBAAmB,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,uBAAuB,GACvB,kBAAkB,GAClB,eAAe,GACf,2BAA2B,GAC3B,sBAAsB,GACtB,+BAA+B,GAC/B,wBAAwB,GACxB,8BAA8B,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,GAAG,SAAS,CA4E3G"}
@@ -11,8 +11,14 @@ export function renderFailureHints(hints) {
11
11
  }
12
12
  return lines.join('\n') + '\n';
13
13
  }
14
- // ── Convenience hint builders ──────────────────────────────────────────
15
- export function doctorHints() {
14
+ export function doctorHints(context = {}) {
15
+ if (context.sharkcraftFolderMissing) {
16
+ return [
17
+ { label: 'scaffold sharkcraft/ (auto-pick a preset)', command: 'shrk init --zero-config' },
18
+ { label: 'preview the scaffold first', command: 'shrk init --zero-config --dry-run' },
19
+ { label: 'try the killer demo (no folder required)', command: 'shrk check boundaries --changed-only --since main' },
20
+ ];
21
+ }
16
22
  return [
17
23
  { label: 'preview fix suggestions', command: 'shrk fix preview --action-hints' },
18
24
  { label: 'list suppressions', command: 'shrk doctor suppressions list' },
@@ -27,19 +27,11 @@ export interface IWatchPlan {
27
27
  }
28
28
  export declare function buildWatchPlan(options: IWatchLoopOptions, steps: readonly string[]): IWatchPlan;
29
29
  import type { ParsedArgs } from '../command-registry.js';
30
- export interface IWatchModeOptions {
31
- /**
32
- * Paths to watch when the user does not pass `--paths`. Defaults to
33
- * `['sharkcraft']` if omitted. Use this for commands that scan code
34
- * outside `sharkcraft/` (e.g. `check boundaries` scans the whole repo).
35
- */
36
- defaultPaths?: readonly string[];
37
- }
38
30
  /**
39
31
  * Run a command's `run` function inside a watch loop when --watch is set.
40
32
  *
41
33
  * @returns null if --watch is not set (caller proceeds as before), otherwise
42
34
  * the exit code of the watch loop.
43
35
  */
44
- export declare function maybeRunInWatchMode(args: ParsedArgs, runner: (innerArgs: ParsedArgs) => Promise<number>, options?: IWatchModeOptions): Promise<number | null>;
36
+ export declare function maybeRunInWatchMode(args: ParsedArgs, runner: (innerArgs: ParsedArgs) => Promise<number>): Promise<number | null>;
45
37
  //# sourceMappingURL=watch-loop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch-loop.d.ts","sourceRoot":"","sources":["../../src/output/watch-loop.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,4BAA4B;IAC5B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACvC;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,CA6DjB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,0BAA0B,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,UAAU,CAYZ;AAED,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,EAClD,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqCxB"}
1
+ {"version":3,"file":"watch-loop.d.ts","sourceRoot":"","sources":["../../src/output/watch-loop.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,4BAA4B;IAC5B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACvC;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,CA6DjB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,0BAA0B,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,UAAU,CAYZ;AAED,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,GACjD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BxB"}
@@ -81,41 +81,31 @@ export function buildWatchPlan(options, steps) {
81
81
  steps,
82
82
  };
83
83
  }
84
- import { flagBool, flagNumber, flagString, resolveCwd } from "../command-registry.js";
84
+ import { flagBool, flagNumber, resolveCwd } from "../command-registry.js";
85
85
  /**
86
86
  * Run a command's `run` function inside a watch loop when --watch is set.
87
87
  *
88
88
  * @returns null if --watch is not set (caller proceeds as before), otherwise
89
89
  * the exit code of the watch loop.
90
90
  */
91
- export async function maybeRunInWatchMode(args, runner, options = {}) {
91
+ export async function maybeRunInWatchMode(args, runner) {
92
92
  if (!flagBool(args, 'watch'))
93
93
  return null;
94
94
  const cwd = resolveCwd(args);
95
95
  const debounce = flagNumber(args, 'debounce') ?? 300;
96
96
  const once = flagBool(args, 'once');
97
- const pathsFlag = flagString(args, 'paths');
98
- const userPaths = pathsFlag
99
- ? pathsFlag.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
100
- : [];
101
- const paths = userPaths.length > 0
102
- ? userPaths
103
- : options.defaultPaths && options.defaultPaths.length > 0
104
- ? options.defaultPaths
105
- : ['sharkcraft'];
106
97
  // Strip --watch so the inner snapshot doesn't recurse.
107
98
  const innerFlags = new Map(args.flags);
108
99
  innerFlags.delete('watch');
109
100
  innerFlags.delete('once');
110
101
  innerFlags.delete('debounce');
111
- innerFlags.delete('paths');
112
102
  const innerArgs = {
113
103
  positional: args.positional,
114
104
  flags: innerFlags,
115
105
  multiFlags: args.multiFlags,
116
106
  ...(args.globalCwd ? { globalCwd: args.globalCwd } : {}),
117
107
  };
118
- return runWatchLoop({ cwd, debounce, once, paths }, {
108
+ return runWatchLoop({ cwd, debounce, once }, {
119
109
  snapshot: async () => {
120
110
  const ts = new Date().toLocaleTimeString();
121
111
  process.stdout.write(`\n[watch] ${ts}\n`);