@beignet/cli 0.0.48 → 0.0.49

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 (88) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +76 -21
  3. package/dist/analysis/contract-factories.d.ts +17 -0
  4. package/dist/analysis/contract-factories.d.ts.map +1 -0
  5. package/dist/analysis/contract-factories.js +176 -0
  6. package/dist/analysis/contract-factories.js.map +1 -0
  7. package/dist/analysis/layers.d.ts +5 -0
  8. package/dist/analysis/layers.d.ts.map +1 -0
  9. package/dist/analysis/layers.js +172 -0
  10. package/dist/analysis/layers.js.map +1 -0
  11. package/dist/analysis/port-wiring.d.ts +23 -0
  12. package/dist/analysis/port-wiring.d.ts.map +1 -0
  13. package/dist/analysis/port-wiring.js +379 -0
  14. package/dist/analysis/port-wiring.js.map +1 -0
  15. package/dist/analysis/source-index.d.ts +5 -0
  16. package/dist/analysis/source-index.d.ts.map +1 -1
  17. package/dist/analysis/source-index.js +171 -42
  18. package/dist/analysis/source-index.js.map +1 -1
  19. package/dist/analysis/workspace.d.ts +10 -4
  20. package/dist/analysis/workspace.d.ts.map +1 -1
  21. package/dist/analysis/workspace.js +66 -6
  22. package/dist/analysis/workspace.js.map +1 -1
  23. package/dist/app-map.d.ts.map +1 -1
  24. package/dist/app-map.js +311 -92
  25. package/dist/app-map.js.map +1 -1
  26. package/dist/check.js +37 -3
  27. package/dist/check.js.map +1 -1
  28. package/dist/explain.js +4 -4
  29. package/dist/explain.js.map +1 -1
  30. package/dist/inspect.d.ts.map +1 -1
  31. package/dist/inspect.js +415 -168
  32. package/dist/inspect.js.map +1 -1
  33. package/dist/lint.d.ts.map +1 -1
  34. package/dist/lint.js +309 -487
  35. package/dist/lint.js.map +1 -1
  36. package/dist/make/inbox.d.ts.map +1 -1
  37. package/dist/make/inbox.js +11 -4
  38. package/dist/make/inbox.js.map +1 -1
  39. package/dist/make/payments.js +2 -2
  40. package/dist/make.d.ts.map +1 -1
  41. package/dist/make.js +144 -8
  42. package/dist/make.js.map +1 -1
  43. package/dist/mcp.d.ts +1 -1
  44. package/dist/mcp.d.ts.map +1 -1
  45. package/dist/mcp.js +19 -19
  46. package/dist/mcp.js.map +1 -1
  47. package/dist/provider-add.d.ts.map +1 -1
  48. package/dist/provider-add.js +142 -7
  49. package/dist/provider-add.js.map +1 -1
  50. package/dist/registry-edits.d.ts +7 -0
  51. package/dist/registry-edits.d.ts.map +1 -1
  52. package/dist/registry-edits.js +237 -53
  53. package/dist/registry-edits.js.map +1 -1
  54. package/dist/templates/agents.d.ts.map +1 -1
  55. package/dist/templates/agents.js +16 -0
  56. package/dist/templates/agents.js.map +1 -1
  57. package/dist/templates/base.d.ts.map +1 -1
  58. package/dist/templates/base.js +9 -1
  59. package/dist/templates/base.js.map +1 -1
  60. package/dist/templates/server.d.ts.map +1 -1
  61. package/dist/templates/server.js +2 -0
  62. package/dist/templates/server.js.map +1 -1
  63. package/dist/templates/shared.d.ts +1 -0
  64. package/dist/templates/shared.d.ts.map +1 -1
  65. package/dist/templates/shared.js +3 -2
  66. package/dist/templates/shared.js.map +1 -1
  67. package/package.json +7 -6
  68. package/skills/app-structure/SKILL.md +46 -5
  69. package/src/analysis/contract-factories.ts +256 -0
  70. package/src/analysis/layers.ts +266 -0
  71. package/src/analysis/port-wiring.ts +506 -0
  72. package/src/analysis/source-index.ts +225 -57
  73. package/src/analysis/workspace.ts +115 -6
  74. package/src/app-map.ts +418 -109
  75. package/src/check.ts +51 -3
  76. package/src/explain.ts +4 -4
  77. package/src/inspect.ts +583 -215
  78. package/src/lint.ts +393 -643
  79. package/src/make/inbox.ts +11 -4
  80. package/src/make/payments.ts +2 -2
  81. package/src/make.ts +225 -8
  82. package/src/mcp.ts +19 -22
  83. package/src/provider-add.ts +186 -7
  84. package/src/registry-edits.ts +313 -63
  85. package/src/templates/agents.ts +16 -0
  86. package/src/templates/base.ts +9 -1
  87. package/src/templates/server.ts +2 -0
  88. package/src/templates/shared.ts +3 -2
@@ -0,0 +1,266 @@
1
+ import path from "node:path";
2
+ import {
3
+ clientDirPath,
4
+ directoryPath,
5
+ normalizePath,
6
+ type ResolvedBeignetConfig,
7
+ } from "../config.js";
8
+
9
+ export type SourceLayer =
10
+ | "app"
11
+ | "app-context"
12
+ | "agent-capability"
13
+ | "client"
14
+ | "component"
15
+ | "contract"
16
+ | "domain"
17
+ | "feature-helper"
18
+ | "feature-port"
19
+ | "infra"
20
+ | "lib"
21
+ | "policy"
22
+ | "port"
23
+ | "route"
24
+ | "schema"
25
+ | "seed"
26
+ | "server"
27
+ | "test"
28
+ | "use-case"
29
+ | "workflow"
30
+ | "unknown";
31
+
32
+ type FeatureLayerConvention = {
33
+ path: string;
34
+ layer: SourceLayer;
35
+ };
36
+
37
+ const canonicalFeatureDirectories = {
38
+ client: "client",
39
+ components: "component",
40
+ domain: "domain",
41
+ jobs: "workflow",
42
+ listeners: "workflow",
43
+ notifications: "workflow",
44
+ schedules: "workflow",
45
+ seeds: "seed",
46
+ tasks: "workflow",
47
+ tests: "test",
48
+ uploads: "workflow",
49
+ "use-cases": "use-case",
50
+ } as const satisfies Record<string, SourceLayer>;
51
+
52
+ const canonicalFeatureRootFiles = {
53
+ "agent-capabilities": "agent-capability",
54
+ contracts: "contract",
55
+ policy: "policy",
56
+ ports: "feature-port",
57
+ routes: "route",
58
+ schemas: "schema",
59
+ } as const satisfies Record<string, SourceLayer>;
60
+
61
+ const featureDirectoryAliases = {
62
+ command: "tasks",
63
+ commands: "tasks",
64
+ event: "domain/events",
65
+ events: "domain/events",
66
+ job: "jobs",
67
+ listener: "listeners",
68
+ notification: "notifications",
69
+ schedule: "schedules",
70
+ scheduled: "schedules",
71
+ task: "tasks",
72
+ upload: "uploads",
73
+ } as const;
74
+
75
+ export function classifySourcePath(
76
+ filePath: string,
77
+ config: ResolvedBeignetConfig,
78
+ ): SourceLayer {
79
+ const normalizedPath = stripKnownExtension(normalizePath(filePath));
80
+ const featuresPath = directoryPath(config.paths.features);
81
+ const portsDir = directoryPath(path.dirname(config.paths.ports));
82
+
83
+ if (
84
+ isTestPath(normalizedPath) ||
85
+ isUnder(normalizedPath, directoryPath(config.paths.tests))
86
+ ) {
87
+ return "test";
88
+ }
89
+ if (normalizedPath === stripKnownExtension(config.paths.appContext)) {
90
+ return "app-context";
91
+ }
92
+
93
+ const featurePath = featureRelativePath(normalizedPath, featuresPath);
94
+ if (featurePath) {
95
+ const layerSegment = featurePath.split("/")[1];
96
+ if (!layerSegment) return "feature-helper";
97
+ const convention = featureDirectoryConvention(layerSegment);
98
+ if (convention) return convention.layer;
99
+ const rootConvention = featureRootFileConvention(layerSegment);
100
+ if (rootConvention) return rootConvention.layer;
101
+ if (layerSegment.endsWith("-contracts")) return "contract";
102
+ return "feature-helper";
103
+ }
104
+
105
+ if (
106
+ directoryPath(config.paths.contracts) !== featuresPath &&
107
+ isUnder(normalizedPath, directoryPath(config.paths.contracts))
108
+ ) {
109
+ return "contract";
110
+ }
111
+ if (
112
+ directoryPath(config.paths.useCases) !== featuresPath &&
113
+ isUnder(normalizedPath, directoryPath(config.paths.useCases))
114
+ ) {
115
+ return "use-case";
116
+ }
117
+ if (
118
+ directoryPath(config.paths.policies) !== featuresPath &&
119
+ isUnder(normalizedPath, directoryPath(config.paths.policies))
120
+ ) {
121
+ return "policy";
122
+ }
123
+
124
+ if (
125
+ isUnder(normalizedPath, directoryPath(path.dirname(config.paths.server)))
126
+ ) {
127
+ return "server";
128
+ }
129
+ if (
130
+ isUnder(
131
+ normalizedPath,
132
+ directoryPath(path.dirname(config.paths.portWiring)),
133
+ )
134
+ ) {
135
+ return "infra";
136
+ }
137
+ if (isUnder(normalizedPath, portsDir)) return "port";
138
+ if (isUnder(normalizedPath, clientDirPath(config))) return "client";
139
+ if (isUnder(normalizedPath, "components")) return "component";
140
+ if (isUnder(normalizedPath, legacySharedDomainPath(config))) return "domain";
141
+ if (isUnder(normalizedPath, "lib")) return "lib";
142
+ if (
143
+ isUnder(normalizedPath, directoryPath(path.dirname(config.paths.routes)))
144
+ ) {
145
+ return "app";
146
+ }
147
+
148
+ return "unknown";
149
+ }
150
+
151
+ export function canonicalFeatureLayerPath(
152
+ filePath: string,
153
+ config: ResolvedBeignetConfig,
154
+ ): string | undefined {
155
+ const featuresPath = directoryPath(config.paths.features);
156
+ const relativePath = featureRelativePath(
157
+ normalizePath(filePath),
158
+ featuresPath,
159
+ );
160
+ if (!relativePath) return undefined;
161
+
162
+ const [feature, segment, ...rest] = relativePath.split("/");
163
+ if (!feature || !segment) return undefined;
164
+
165
+ if (isTestSourceFile(segment, rest) && segment !== "tests") {
166
+ return path.posix.join(
167
+ featuresPath,
168
+ feature,
169
+ "tests",
170
+ ...(rest.length > 0 ? rest : [segment]),
171
+ );
172
+ }
173
+
174
+ if (rest.length > 0) {
175
+ const convention = featureDirectoryConvention(segment);
176
+ if (!convention || convention.path === segment) return undefined;
177
+ return path.posix.join(featuresPath, feature, convention.path, ...rest);
178
+ }
179
+
180
+ const extension = path.posix.extname(segment);
181
+ const stem = extension ? segment.slice(0, -extension.length) : segment;
182
+ const convention = featureRootFileConvention(stem);
183
+ if (!convention || convention.path === stem) return undefined;
184
+ return path.posix.join(
185
+ featuresPath,
186
+ feature,
187
+ `${convention.path}${extension}`,
188
+ );
189
+ }
190
+
191
+ function isTestSourceFile(segment: string, rest: string[]): boolean {
192
+ const fileName = rest.at(-1) ?? segment;
193
+ return /\.(?:test|spec)\.(?:ts|tsx|mts|cts)$/.test(fileName);
194
+ }
195
+
196
+ function featureDirectoryConvention(
197
+ segment: string,
198
+ ): FeatureLayerConvention | undefined {
199
+ const canonical = Object.entries(canonicalFeatureDirectories).find(
200
+ ([candidate]) =>
201
+ normalizeLayerSegment(candidate) === normalizeLayerSegment(segment),
202
+ );
203
+ if (canonical) return { path: canonical[0], layer: canonical[1] };
204
+
205
+ const alias =
206
+ featureDirectoryAliases[
207
+ normalizeLayerSegment(segment) as keyof typeof featureDirectoryAliases
208
+ ];
209
+ if (!alias) return undefined;
210
+ return {
211
+ path: alias,
212
+ layer:
213
+ alias === "domain/events"
214
+ ? "domain"
215
+ : canonicalFeatureDirectories[
216
+ alias as keyof typeof canonicalFeatureDirectories
217
+ ],
218
+ };
219
+ }
220
+
221
+ function featureRootFileConvention(
222
+ segment: string,
223
+ ): FeatureLayerConvention | undefined {
224
+ const canonical = Object.entries(canonicalFeatureRootFiles).find(
225
+ ([candidate]) =>
226
+ normalizeLayerSegment(candidate) === normalizeLayerSegment(segment),
227
+ );
228
+ return canonical ? { path: canonical[0], layer: canonical[1] } : undefined;
229
+ }
230
+
231
+ function normalizeLayerSegment(segment: string): string {
232
+ return segment.replaceAll("-", "").replaceAll("_", "").toLowerCase();
233
+ }
234
+
235
+ function stripKnownExtension(filePath: string): string {
236
+ return filePath.replace(/\.(c|m)?tsx?$/, "");
237
+ }
238
+
239
+ function isTestPath(filePath: string): boolean {
240
+ return (
241
+ filePath.includes("/tests/") ||
242
+ filePath.endsWith(".test") ||
243
+ filePath.endsWith(".spec")
244
+ );
245
+ }
246
+
247
+ function isUnder(filePath: string, directory: string): boolean {
248
+ const normalizedDir = directoryPath(directory);
249
+ if (!normalizedDir || normalizedDir === ".") return false;
250
+ return filePath === normalizedDir || filePath.startsWith(`${normalizedDir}/`);
251
+ }
252
+
253
+ function featureRelativePath(
254
+ filePath: string,
255
+ featuresPath: string,
256
+ ): string | undefined {
257
+ if (!isUnder(filePath, featuresPath)) return undefined;
258
+ return filePath.slice(`${directoryPath(featuresPath)}/`.length);
259
+ }
260
+
261
+ function legacySharedDomainPath(config: ResolvedBeignetConfig): string {
262
+ const featuresDir = directoryPath(config.paths.features);
263
+ const featuresParent = directoryPath(path.dirname(featuresDir));
264
+ if (!featuresParent || featuresParent === ".") return "domain";
265
+ return path.join(featuresParent, "domain");
266
+ }