@beignet/cli 0.0.50 → 0.0.52

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 (105) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +64 -22
  3. package/dist/analysis/layers.d.ts.map +1 -1
  4. package/dist/analysis/layers.js +4 -2
  5. package/dist/analysis/layers.js.map +1 -1
  6. package/dist/analysis/port-wiring.d.ts.map +1 -1
  7. package/dist/analysis/port-wiring.js +2 -1
  8. package/dist/analysis/port-wiring.js.map +1 -1
  9. package/dist/analysis/source-files.d.ts +15 -0
  10. package/dist/analysis/source-files.d.ts.map +1 -0
  11. package/dist/analysis/source-files.js +52 -0
  12. package/dist/analysis/source-files.js.map +1 -0
  13. package/dist/analysis/workspace.d.ts.map +1 -1
  14. package/dist/analysis/workspace.js +21 -25
  15. package/dist/analysis/workspace.js.map +1 -1
  16. package/dist/app-map-changes.d.ts.map +1 -1
  17. package/dist/app-map-changes.js +5 -7
  18. package/dist/app-map-changes.js.map +1 -1
  19. package/dist/app-map.d.ts.map +1 -1
  20. package/dist/app-map.js +33 -30
  21. package/dist/app-map.js.map +1 -1
  22. package/dist/check.d.ts +12 -0
  23. package/dist/check.d.ts.map +1 -1
  24. package/dist/check.js +92 -25
  25. package/dist/check.js.map +1 -1
  26. package/dist/git-changes.d.ts +17 -1
  27. package/dist/git-changes.d.ts.map +1 -1
  28. package/dist/git-changes.js +118 -84
  29. package/dist/git-changes.js.map +1 -1
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +35 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/inspect.d.ts.map +1 -1
  35. package/dist/inspect.js +436 -112
  36. package/dist/inspect.js.map +1 -1
  37. package/dist/lint.d.ts.map +1 -1
  38. package/dist/lint.js +3 -6
  39. package/dist/lint.js.map +1 -1
  40. package/dist/make.d.ts.map +1 -1
  41. package/dist/make.js +0 -2
  42. package/dist/make.js.map +1 -1
  43. package/dist/mcp.d.ts.map +1 -1
  44. package/dist/mcp.js +23 -5
  45. package/dist/mcp.js.map +1 -1
  46. package/dist/operational-process.d.ts +1 -0
  47. package/dist/operational-process.d.ts.map +1 -1
  48. package/dist/operational-process.js.map +1 -1
  49. package/dist/operational-runner.js +1 -0
  50. package/dist/operational-runner.js.map +1 -1
  51. package/dist/outbox.d.ts +1 -0
  52. package/dist/outbox.d.ts.map +1 -1
  53. package/dist/outbox.js +58 -12
  54. package/dist/outbox.js.map +1 -1
  55. package/dist/templates/agents.d.ts.map +1 -1
  56. package/dist/templates/agents.js +43 -15
  57. package/dist/templates/agents.js.map +1 -1
  58. package/dist/templates/base.js +5 -5
  59. package/dist/templates/db/mysql.d.ts.map +1 -1
  60. package/dist/templates/db/mysql.js +51 -22
  61. package/dist/templates/db/mysql.js.map +1 -1
  62. package/dist/templates/db/postgres.d.ts.map +1 -1
  63. package/dist/templates/db/postgres.js +60 -20
  64. package/dist/templates/db/postgres.js.map +1 -1
  65. package/dist/templates/db/sqlite.d.ts.map +1 -1
  66. package/dist/templates/db/sqlite.js +51 -23
  67. package/dist/templates/db/sqlite.js.map +1 -1
  68. package/dist/templates/shadcn.js +1 -1
  69. package/dist/templates/shadcn.js.map +1 -1
  70. package/dist/templates/shared.js +4 -4
  71. package/dist/templates/shared.js.map +1 -1
  72. package/dist/templates/testing.d.ts.map +1 -1
  73. package/dist/templates/testing.js +7 -19
  74. package/dist/templates/testing.js.map +1 -1
  75. package/dist/test-discovery.d.ts +9 -0
  76. package/dist/test-discovery.d.ts.map +1 -0
  77. package/dist/test-discovery.js +40 -0
  78. package/dist/test-discovery.js.map +1 -0
  79. package/package.json +7 -5
  80. package/skills/app-structure/SKILL.md +37 -10
  81. package/src/analysis/layers.ts +4 -2
  82. package/src/analysis/port-wiring.ts +2 -1
  83. package/src/analysis/source-files.ts +61 -0
  84. package/src/analysis/workspace.ts +27 -31
  85. package/src/app-map-changes.ts +10 -8
  86. package/src/app-map.ts +43 -32
  87. package/src/check.ts +115 -25
  88. package/src/git-changes.ts +218 -86
  89. package/src/index.ts +53 -8
  90. package/src/inspect.ts +627 -146
  91. package/src/lint.ts +6 -5
  92. package/src/make.ts +0 -2
  93. package/src/mcp.ts +34 -11
  94. package/src/operational-process.ts +1 -0
  95. package/src/operational-runner.ts +1 -0
  96. package/src/outbox.ts +63 -12
  97. package/src/templates/agents.ts +43 -15
  98. package/src/templates/base.ts +5 -5
  99. package/src/templates/db/mysql.ts +51 -22
  100. package/src/templates/db/postgres.ts +60 -20
  101. package/src/templates/db/sqlite.ts +51 -23
  102. package/src/templates/shadcn.ts +1 -1
  103. package/src/templates/shared.ts +4 -4
  104. package/src/templates/testing.ts +12 -19
  105. package/src/test-discovery.ts +53 -0
@@ -1,3 +1,4 @@
1
+ import { appTestFilePattern, configuredTestDirectoryPathKeys, configuredTestFilePathKeys, defaultAppTestRoots, } from "../test-discovery.js";
1
2
  export const testHelperTemplateFile = {
2
3
  path: "lib/beignet-test.ts",
3
4
  content: `import assert from "node:assert/strict";
@@ -405,16 +406,9 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
405
406
  import path from "node:path";
406
407
  import { pathToFileURL } from "node:url";
407
408
 
408
- const defaultTestRoots = [
409
- "app",
410
- "features",
411
- "infra",
412
- "lib",
413
- "ports",
414
- "server",
415
- "tests",
416
- "src/tests",
417
- ];
409
+ const defaultTestRoots = ${JSON.stringify(defaultAppTestRoots, null, 2)};
410
+ const configuredTestDirectoryPathKeys = ${JSON.stringify(configuredTestDirectoryPathKeys)};
411
+ const configuredTestFilePathKeys = ${JSON.stringify(configuredTestFilePathKeys)};
418
412
  const ignoredDirectories = new Set([
419
413
  ".beignet-local",
420
414
  ".git",
@@ -424,7 +418,7 @@ const ignoredDirectories = new Set([
424
418
  "dist",
425
419
  "node_modules",
426
420
  ]);
427
- const testFilePattern = /\\.(test|spec)\\.[cm]?[tj]sx?$/;
421
+ const testFilePattern = /${appTestFilePattern.source}/;
428
422
 
429
423
  const configFileNames = [
430
424
  "beignet.config.ts",
@@ -529,14 +523,8 @@ async function configuredRoots(cwd: string): Promise<string[]> {
529
523
  }
530
524
  const paths = isObjectRecord(config.paths) ? config.paths : {};
531
525
  return [
532
- paths.tests,
533
- paths.features,
534
- parentPath(paths.routes),
535
- parentPath(paths.server),
536
- parentPath(paths.portWiring),
537
- parentPath(paths.ports),
538
- parentPath(paths.useCaseBuilder),
539
- parentPath(paths.routesBuilder),
526
+ ...configuredTestDirectoryPathKeys.map((key) => paths[key]),
527
+ ...configuredTestFilePathKeys.map((key) => parentPath(paths[key])),
540
528
  ].filter((root): root is string => Boolean(root) && root !== ".");
541
529
  }
542
530
 
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/templates/testing.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAiB;IAClD,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4YV;CACA,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAiB;IAClD,IAAI,EAAE,4BAA4B;IAClC,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkKV;CACA,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACtD,sBAAsB;IACtB,sBAAsB;CACvB,CAAC"}
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/templates/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,CAAC,MAAM,sBAAsB,GAAiB;IAClD,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4YV;CACA,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAiB;IAClD,IAAI,EAAE,4BAA4B;IAClC,OAAO,EAAE;;;;;2BAKgB,IAAI,CAAC,SAAS,CAAC,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC;0CAC7B,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC;qCACpD,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC;;;;;;;;;;2BAUpD,kBAAkB,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoInD;CACA,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACtD,sBAAsB;IACtB,sBAAsB;CACvB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type BeignetPaths } from "./config.js";
2
+ export declare const defaultAppTestRoots: readonly ["app", "features", "infra", "lib", "ports", "server", "tests", "src/tests"];
3
+ export declare const configuredTestDirectoryPathKeys: readonly ["tests", "features"];
4
+ export declare const configuredTestFilePathKeys: readonly ["routes", "server", "portWiring", "ports", "useCaseBuilder", "routesBuilder"];
5
+ export declare const appTestFilePattern: RegExp;
6
+ export declare function appTestRoots(paths: BeignetPaths): string[];
7
+ export declare function isAppTestFile(file: string): boolean;
8
+ export declare function isWithinAppTestRoots(file: string, roots: readonly string[]): boolean;
9
+ //# sourceMappingURL=test-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-discovery.d.ts","sourceRoot":"","sources":["../src/test-discovery.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D,eAAO,MAAM,mBAAmB,uFAStB,CAAC;AAEX,eAAO,MAAM,+BAA+B,gCAGQ,CAAC;AAErD,eAAO,MAAM,0BAA0B,yFAOa,CAAC;AAErD,eAAO,MAAM,kBAAkB,QAAiC,CAAC;AAEjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,CAW1D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAET"}
@@ -0,0 +1,40 @@
1
+ import path from "node:path";
2
+ import { directoryPath } from "./config.js";
3
+ export const defaultAppTestRoots = [
4
+ "app",
5
+ "features",
6
+ "infra",
7
+ "lib",
8
+ "ports",
9
+ "server",
10
+ "tests",
11
+ "src/tests",
12
+ ];
13
+ export const configuredTestDirectoryPathKeys = [
14
+ "tests",
15
+ "features",
16
+ ];
17
+ export const configuredTestFilePathKeys = [
18
+ "routes",
19
+ "server",
20
+ "portWiring",
21
+ "ports",
22
+ "useCaseBuilder",
23
+ "routesBuilder",
24
+ ];
25
+ export const appTestFilePattern = /\.(test|spec)\.[cm]?[jt]sx?$/;
26
+ export function appTestRoots(paths) {
27
+ const roots = [
28
+ ...defaultAppTestRoots,
29
+ ...configuredTestDirectoryPathKeys.map((key) => paths[key]),
30
+ ...configuredTestFilePathKeys.map((key) => directoryPath(path.posix.dirname(paths[key]))),
31
+ ];
32
+ return Array.from(new Set(roots.map(directoryPath).filter((root) => root && root !== ".")));
33
+ }
34
+ export function isAppTestFile(file) {
35
+ return appTestFilePattern.test(file);
36
+ }
37
+ export function isWithinAppTestRoots(file, roots) {
38
+ return roots.some((root) => file.startsWith(`${root}/`));
39
+ }
40
+ //# sourceMappingURL=test-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-discovery.js","sourceRoot":"","sources":["../src/test-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAqB,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,KAAK;IACL,UAAU;IACV,OAAO;IACP,KAAK;IACL,OAAO;IACP,QAAQ;IACR,OAAO;IACP,WAAW;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,OAAO;IACP,UAAU;CACwC,CAAC;AAErD,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,QAAQ;IACR,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,gBAAgB;IAChB,eAAe;CACmC,CAAC;AAErD,MAAM,CAAC,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAEjE,MAAM,UAAU,YAAY,CAAC,KAAmB;IAC9C,MAAM,KAAK,GAAG;QACZ,GAAG,mBAAmB;QACtB,GAAG,+BAA+B,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3D,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACxC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAC9C;KACF,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CACf,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CACzE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,KAAwB;IAExB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beignet/cli",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "type": "module",
5
5
  "description": "CLI for creating and maintaining Beignet apps.",
6
6
  "main": "./dist/lib.js",
@@ -33,8 +33,8 @@
33
33
  "prepack": "bun run build",
34
34
  "dev": "tsc --watch",
35
35
  "clean": "rm -rf dist coverage .turbo",
36
- "test": "bun test",
37
- "test:coverage": "bun test --coverage",
36
+ "test": "bun test --parallel=2",
37
+ "test:coverage": "bun test --parallel=2 --coverage",
38
38
  "lint": "biome check ."
39
39
  },
40
40
  "keywords": [
@@ -64,18 +64,20 @@
64
64
  },
65
65
  "devDependencies": {
66
66
  "@modelcontextprotocol/client": "^2.0.0",
67
- "@types/bun": "^1.3.13",
67
+ "@types/bun": "^1.4.0",
68
68
  "@types/node": "^22.0.0",
69
+ "@types/semver": "^7.8.0",
69
70
  "next-themes": "^0.4.6",
70
71
  "tsx": "^4.22.4"
71
72
  },
72
73
  "dependencies": {
73
- "@beignet/core": "^0.0.50",
74
+ "@beignet/core": "^0.0.52",
74
75
  "@clack/prompts": "^1.5.1",
75
76
  "@modelcontextprotocol/server": "^2.0.0",
76
77
  "@stricli/core": "^1.2.7",
77
78
  "diff": "^8.0.4",
78
79
  "jiti": "^2.7.0",
80
+ "semver": "^7.8.5",
79
81
  "typescript": "^5.3.0",
80
82
  "zod": "^4.2.0"
81
83
  }
@@ -8,8 +8,8 @@ description: "Maintain Beignet app structure with @beignet/cli: deterministic ap
8
8
  Use this skill when working with Beignet apps through `@beignet/cli`. Prefer
9
9
  generators and validation commands before hand-editing framework wiring.
10
10
 
11
- Beignet apps require Node.js 22.12 or newer. Bun 1.3.14 or newer is supported
12
- for Beignet commands.
11
+ Beignet apps require Node.js 22.12 or newer. Bun is optional; npm, pnpm, Yarn,
12
+ and Bun can run Beignet commands.
13
13
 
14
14
  ## Starter Sources
15
15
 
@@ -53,6 +53,12 @@ beignet make upload issues.attachment --ui
53
53
  Generators place files under the owning feature, update central registries
54
54
  when possible, and keep `doctor` clean.
55
55
 
56
+ Event schemas must output canonical JSON transport data. Use null, strings,
57
+ finite numbers, booleans, arrays, and plain objects. Keep validation
58
+ deterministic and side-effect-free, keep transforms idempotent, and represent
59
+ timestamps as strings or numbers rather than `Date` objects.
60
+ Beignet rejects payloads that would change when decoded and validated again.
61
+
56
62
  Use `make upload --ui` in full-stack apps when the upload should include a
57
63
  shared client/server constraint manifest, typed React upload client, uploader,
58
64
  and component test. Omit `--ui` for API-only or backend-only workflows.
@@ -174,6 +180,15 @@ Start unfamiliar app work with `beignet map`. Its human output summarizes each
174
180
  feature; `beignet map --json` returns the versioned graph with source evidence,
175
181
  registration status, doctor/lint findings, and unresolved references. Use
176
182
  `--feature <name>` or `--kind route,use-case,event` to bound agent context.
183
+ The graph analyzes TypeScript (`.ts`, `.tsx`, `.mts`, and `.cts`) and
184
+ JavaScript (`.js`, `.jsx`, `.mjs`, and `.cjs`) source. Statically recognizable
185
+ Beignet declarations using ES module exports become normal concept nodes, and
186
+ CommonJS `require()` calls contribute dependency evidence. Test/spec files
187
+ under canonical and configured app test roots become test nodes.
188
+ Feature tests keep feature ownership; adjacent app, infra, server, and
189
+ top-level tests remain app-level nodes and can surface as reverse-import
190
+ consumers in changed mode. Treat those links as impact evidence, not proof of
191
+ behavioral coverage.
177
192
  After editing inside a Git worktree, use `beignet map --changed` to inspect
178
193
  directly changed concepts, reverse imports, one-hop semantic consumers, feature
179
194
  context, and unresolved evidence before running `beignet check`. The report
@@ -184,8 +199,16 @@ and does not prove correctness. Recognized guide files and root
184
199
  documentation-directory content stay non-impacting; other `.mdx` and `.txt`
185
200
  paths remain application changes. Changed files with no mapped concept appear
186
201
  as explicit gaps.
187
- The graph resolves local modules with the full TypeScript `baseUrl` and `paths`
188
- configuration. Treat `local_import_unresolved` and
202
+
203
+ `beignet check` fingerprints Git source before and after the full validation
204
+ loop. Treat `provenance.status: "stale"` as a failed result even when every
205
+ individual step passed, and rerun against the resulting source. Outside Git,
206
+ the same checks run with provenance marked unavailable; report that limitation
207
+ instead of describing the result as source-current.
208
+
209
+ The graph resolves local modules with the full `baseUrl` and `paths`
210
+ configuration from `tsconfig.json`, or `jsconfig.json` when no TypeScript
211
+ config exists. Treat `local_import_unresolved` and
189
212
  `contract_declaration_unresolved` as real blind spots to fix, not absent
190
213
  relationships. This includes direct and contract-group declarations whose
191
214
  method, path, or group prefix is dynamic. Local fluent-builder aliases and
@@ -208,8 +231,8 @@ the declaration without parsing the diagnostic message.
208
231
  and feature-root helpers must not depend back on agent adapters
209
232
  - seeds should compose factories and context ports rather than concrete infra,
210
233
  providers, UI, or runtime composition
211
- - TypeScript imports, re-exports, `require()`, import assignments, and literal
212
- dynamic imports all participate in the same graph
234
+ - JavaScript and TypeScript imports, re-exports, `require()`, import
235
+ assignments, and literal dynamic imports all participate in the same graph
213
236
  - feature-root helpers are classified and followed transitively, so they cannot
214
237
  hide a forbidden dependency; computed module references in constrained layers
215
238
  fail when lint cannot verify their destination
@@ -274,7 +297,9 @@ names, source selectors, and applicable aliases. `check` accepts optional
274
297
  `{ preflight?: boolean, preflightConnect?: boolean, connectTimeoutMs?: number, timeoutMs?: number }`.
275
298
  Connected preflight adds migration status and dependency health checks. Check
276
299
  bounds failure output, cancels the active script with the MCP request, and
277
- returns the same versioned result as `beignet check --json`. It does not apply
300
+ returns the same versioned result as `beignet check --json`, including
301
+ starting and ending source provenance. An ending fingerprint that differs from
302
+ the baseline makes the result stale and unsuccessful. It does not apply
278
303
  Beignet fixes, but app package scripts retain their normal side effects. The
279
304
  target package must declare `@beignet/core`, `@beignet/next`, or `@beignet/web`
280
305
  as a runtime dependency. The root must also contain `app-context.ts` or
@@ -293,9 +318,11 @@ and inspection failures use another non-zero exit. `task_run` and
293
318
  `schedule_run` execute registered app workflows. Use `outbox_inspect` for
294
319
  read-only `list` and `show`, and `outbox_run` for state-changing `drain`,
295
320
  `requeue`, `purge`, and `prune`; purge and prune support `dryRun`. Operational
296
- tools isolate app code, bound structured results, and stop the complete process
297
- tree on request cancellation or timeout. Optional operational `module`
298
- overrides must stay inside the app root where the MCP server started.
321
+ drains with nonzero `settlementFailed` or `leaseLost` return the complete JSON
322
+ report as an error result. Parallel delivery is opt-in and unordered.
323
+ Operational tools isolate app code, bound structured results, and stop the
324
+ complete process tree on request cancellation or timeout. Optional operational
325
+ `module` overrides must stay inside the app root where the MCP server started.
299
326
  Cancellation and timeouts cannot roll back side effects that already
300
327
  completed. Inspect app state before retrying an interrupted operation or one
301
328
  that reports a cleanup failure. `doctor`, `lint`, and their JSON output match
@@ -5,6 +5,8 @@ import {
5
5
  normalizePath,
6
6
  type ResolvedBeignetConfig,
7
7
  } from "../config.js";
8
+ import { isAppTestFile } from "../test-discovery.js";
9
+ import { stripSourceFileExtension } from "./source-files.js";
8
10
 
9
11
  export type SourceLayer =
10
12
  | "app"
@@ -190,7 +192,7 @@ export function canonicalFeatureLayerPath(
190
192
 
191
193
  function isTestSourceFile(segment: string, rest: string[]): boolean {
192
194
  const fileName = rest.at(-1) ?? segment;
193
- return /\.(?:test|spec)\.(?:ts|tsx|mts|cts)$/.test(fileName);
195
+ return isAppTestFile(fileName);
194
196
  }
195
197
 
196
198
  function featureDirectoryConvention(
@@ -233,7 +235,7 @@ function normalizeLayerSegment(segment: string): string {
233
235
  }
234
236
 
235
237
  function stripKnownExtension(filePath: string): string {
236
- return filePath.replace(/\.(c|m)?tsx?$/, "");
238
+ return stripSourceFileExtension(filePath);
237
239
  }
238
240
 
239
241
  function isTestPath(filePath: string): boolean {
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import ts from "typescript";
4
4
  import type { ResolvedBeignetConfig } from "../config.js";
5
+ import { sourceFileScriptKind } from "./source-files.js";
5
6
  import type { AnalysisWorkspace } from "./workspace.js";
6
7
 
7
8
  type PortAnalysisWorkspace = Pick<
@@ -241,7 +242,7 @@ export async function analyzePortWiringFiles(options: {
241
242
  await readFile(path.join(options.targetDir, file), "utf8"),
242
243
  ts.ScriptTarget.Latest,
243
244
  true,
244
- file.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
245
+ sourceFileScriptKind(file),
245
246
  );
246
247
  sourceFiles.set(file, sourceFile);
247
248
  return sourceFile;
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import ts from "typescript";
3
+
4
+ export const typescriptAnalysisSourceFileExtensions = [
5
+ ".ts",
6
+ ".tsx",
7
+ ".mts",
8
+ ".cts",
9
+ ] as const;
10
+
11
+ export const javascriptAnalysisSourceFileExtensions = [
12
+ ".js",
13
+ ".jsx",
14
+ ".mjs",
15
+ ".cjs",
16
+ ] as const;
17
+
18
+ export const analysisSourceFileExtensions = [
19
+ ...typescriptAnalysisSourceFileExtensions,
20
+ ...javascriptAnalysisSourceFileExtensions,
21
+ ] as const;
22
+
23
+ const sourceFileExtensions = new Set<string>(analysisSourceFileExtensions);
24
+ const javascriptSourceFileExtensions = new Set<string>(
25
+ javascriptAnalysisSourceFileExtensions,
26
+ );
27
+ const declarationFilePattern = /\.d\.(?:ts|mts|cts)$/;
28
+
29
+ /** Whether an extension belongs to the JavaScript/TypeScript source family. */
30
+ export function isAnalysisSourceFileExtension(extension: string): boolean {
31
+ return sourceFileExtensions.has(extension);
32
+ }
33
+
34
+ /** Whether a project file can participate in Beignet source analysis. */
35
+ export function isAnalysisSourceFile(file: string): boolean {
36
+ return (
37
+ isAnalysisSourceFileExtension(path.posix.extname(file)) &&
38
+ !declarationFilePattern.test(file)
39
+ );
40
+ }
41
+
42
+ /** Remove a JavaScript or TypeScript source extension from a path. */
43
+ export function stripSourceFileExtension(file: string): string {
44
+ const extension = path.posix.extname(file);
45
+ return isAnalysisSourceFileExtension(extension)
46
+ ? file.slice(0, -extension.length)
47
+ : file;
48
+ }
49
+
50
+ /** Whether a source file uses JavaScript rather than TypeScript syntax. */
51
+ export function isJavaScriptSourceFile(file: string): boolean {
52
+ return javascriptSourceFileExtensions.has(path.posix.extname(file));
53
+ }
54
+
55
+ /** Select the TypeScript parser mode for a JavaScript or TypeScript file. */
56
+ export function sourceFileScriptKind(file: string): ts.ScriptKind {
57
+ if (file.endsWith(".tsx")) return ts.ScriptKind.TSX;
58
+ if (file.endsWith(".jsx")) return ts.ScriptKind.JSX;
59
+ if (isJavaScriptSourceFile(file)) return ts.ScriptKind.JS;
60
+ return ts.ScriptKind.TS;
61
+ }
@@ -8,6 +8,10 @@ import {
8
8
  type ResolvedBeignetConfig,
9
9
  resolveConfig,
10
10
  } from "../config.js";
11
+ import {
12
+ isAnalysisSourceFileExtension,
13
+ sourceFileScriptKind,
14
+ } from "./source-files.js";
11
15
 
12
16
  const ignoredDirectoryNames = new Set([
13
17
  ".git",
@@ -84,7 +88,7 @@ export async function createAnalysisWorkspace(
84
88
  await readSource(file),
85
89
  ts.ScriptTarget.Latest,
86
90
  true,
87
- scriptKind(file),
91
+ sourceFileScriptKind(file),
88
92
  );
89
93
  sourceFiles.set(file, sourceFile);
90
94
  return sourceFile;
@@ -208,12 +212,7 @@ export function isLocalModuleSpecifier(
208
212
  importPath: string,
209
213
  ): boolean {
210
214
  const extension = path.posix.extname(importPath);
211
- if (
212
- extension &&
213
- ![".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"].includes(
214
- extension,
215
- )
216
- ) {
215
+ if (extension && !isAnalysisSourceFileExtension(extension)) {
217
216
  return false;
218
217
  }
219
218
  if (importPath.startsWith(".")) return true;
@@ -298,28 +297,32 @@ function resolveCompilerConfiguration(targetDir: string): {
298
297
  compilerOptions: ts.CompilerOptions;
299
298
  } {
300
299
  const compilerConfigFiles = new Set<string>();
301
- const configPath = path.join(targetDir, "tsconfig.json");
302
- const compilerOptions =
303
- ts.getParsedCommandLineOfConfigFile(
304
- configPath,
305
- {},
306
- {
307
- ...ts.sys,
308
- readFile(file) {
309
- compilerConfigFiles.add(path.resolve(file));
310
- return ts.sys.readFile(file);
300
+ const configPath = ["tsconfig.json", "jsconfig.json"]
301
+ .map((file) => path.join(targetDir, file))
302
+ .find(ts.sys.fileExists);
303
+ const compilerOptions = configPath
304
+ ? (ts.getParsedCommandLineOfConfigFile(
305
+ configPath,
306
+ {},
307
+ {
308
+ ...ts.sys,
309
+ readFile(file) {
310
+ compilerConfigFiles.add(path.resolve(file));
311
+ return ts.sys.readFile(file);
312
+ },
313
+ onUnRecoverableConfigFileDiagnostic: () => {},
311
314
  },
312
- onUnRecoverableConfigFileDiagnostic: () => {},
313
- },
314
- )?.options ?? {};
315
+ )?.options ?? {})
316
+ : {};
317
+ const analysisOptions = { ...compilerOptions, allowJs: true };
315
318
 
316
319
  return {
317
320
  compilerConfigFiles: [...compilerConfigFiles].sort(),
318
- compilerOptions: compilerOptions.paths
319
- ? compilerOptions
321
+ compilerOptions: analysisOptions.paths
322
+ ? analysisOptions
320
323
  : {
321
- ...compilerOptions,
322
- baseUrl: compilerOptions.baseUrl ?? targetDir,
324
+ ...analysisOptions,
325
+ baseUrl: analysisOptions.baseUrl ?? targetDir,
323
326
  paths: {
324
327
  "@/*": ["*"],
325
328
  },
@@ -338,10 +341,3 @@ function compilerPathsBase(
338
341
  compilerOptions.baseUrl ?? optionsWithPathBase.pathsBasePath ?? targetDir
339
342
  );
340
343
  }
341
-
342
- function scriptKind(file: string): ts.ScriptKind {
343
- if (file.endsWith(".tsx")) return ts.ScriptKind.TSX;
344
- if (file.endsWith(".jsx")) return ts.ScriptKind.JSX;
345
- if (file.endsWith(".js") || file.endsWith(".mjs")) return ts.ScriptKind.JS;
346
- return ts.ScriptKind.TS;
347
- }
@@ -1,5 +1,9 @@
1
1
  import { readFile, realpath } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import {
4
+ isAnalysisSourceFile,
5
+ stripSourceFileExtension,
6
+ } from "./analysis/source-files.js";
3
7
  import {
4
8
  type LocalImportReference,
5
9
  localImportReferences,
@@ -794,7 +798,7 @@ async function addImportConsumers(args: {
794
798
  reference: LocalImportReference;
795
799
  }>
796
800
  >();
797
- const sourceFiles = args.workspace.files.filter(isSourceFile);
801
+ const sourceFiles = args.workspace.files.filter(isAnalysisSourceFile);
798
802
  await Promise.all(
799
803
  sourceFiles.map(async (file) => {
800
804
  for (const reference of await localImportReferences(
@@ -826,7 +830,9 @@ async function addImportConsumers(args: {
826
830
  );
827
831
  }
828
832
 
829
- for (const change of args.changes.filter((item) => isSourceFile(item.file))) {
833
+ for (const change of args.changes.filter((item) =>
834
+ isAnalysisSourceFile(item.file),
835
+ )) {
830
836
  const queue: Array<{
831
837
  confidence: "exact" | "partial";
832
838
  depth: number;
@@ -1002,7 +1008,7 @@ function addUnmappedChangeGaps(args: {
1002
1008
  const represented = reasons.some(
1003
1009
  (reason) => reason.changedFile === change.repositoryPath,
1004
1010
  );
1005
- const sourceFile = isSourceFile(change.file);
1011
+ const sourceFile = isAnalysisSourceFile(change.file);
1006
1012
  if (sourceFile && change.status === "deleted") {
1007
1013
  args.gaps.push({
1008
1014
  code: "deleted_source_unresolved",
@@ -1289,7 +1295,7 @@ function scopeSort(scope: AppChangeFileScope): number {
1289
1295
  }
1290
1296
 
1291
1297
  function moduleStem(file: string): string {
1292
- return normalizePath(file).replace(/\.(?:c|m)?[jt]sx?$/, "");
1298
+ return stripSourceFileExtension(normalizePath(file));
1293
1299
  }
1294
1300
 
1295
1301
  function resolvedModuleKey(file: string): string {
@@ -1333,10 +1339,6 @@ function highestPriorityScope(
1333
1339
  );
1334
1340
  }
1335
1341
 
1336
- function isSourceFile(file: string): boolean {
1337
- return /\.(?:c|m)?[jt]sx?$/.test(file) && !file.endsWith(".d.ts");
1338
- }
1339
-
1340
1342
  function isDocumentationPath(file: string, roots: string[]): boolean {
1341
1343
  const normalized = normalizePath(file);
1342
1344
  const basename = path.posix.basename(normalized);