@tailor-platform/erp-kit 0.1.1 → 0.1.2
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.
- package/CHANGELOG.md +6 -0
- package/README.md +77 -50
- package/dist/cli.js +691 -571
- package/package.json +1 -1
- package/schemas/module/command.yml +1 -0
- package/schemas/module/model.yml +9 -0
- package/schemas/module/query.yml +53 -0
- package/src/cli.ts +6 -88
- package/src/commands/app/index.ts +74 -0
- package/src/commands/check.test.ts +2 -1
- package/src/commands/check.ts +1 -0
- package/src/commands/module/index.ts +85 -0
- package/src/commands/module/list.test.ts +62 -0
- package/src/commands/module/list.ts +64 -0
- package/src/commands/scaffold.test.ts +5 -0
- package/src/commands/scaffold.ts +2 -1
- package/src/commands/sync-check.test.ts +28 -0
- package/src/commands/sync-check.ts +6 -0
- package/src/integration.test.ts +6 -8
- package/src/module.ts +4 -3
- package/src/modules/primitives/docs/models/Currency.md +4 -0
- package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
- package/src/modules/primitives/docs/models/Unit.md +4 -1
- package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
- package/src/modules/primitives/index.ts +2 -2
- package/src/modules/primitives/module.ts +5 -3
- package/src/modules/primitives/permissions.ts +0 -2
- package/src/modules/primitives/{command → query}/convertAmount.test.ts +2 -19
- package/src/modules/primitives/query/convertAmount.ts +122 -0
- package/src/modules/primitives/{command → query}/convertQuantity.test.ts +2 -13
- package/src/modules/primitives/{command → query}/convertQuantity.ts +4 -6
- package/src/modules/shared/defineQuery.test.ts +28 -0
- package/src/modules/shared/defineQuery.ts +16 -0
- package/src/modules/shared/internal.ts +2 -1
- package/src/modules/shared/types.ts +8 -0
- package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
- package/src/modules/user-management/docs/models/Permission.md +2 -0
- package/src/modules/user-management/docs/models/Role.md +2 -0
- package/src/modules/user-management/docs/models/RolePermission.md +2 -0
- package/src/modules/user-management/docs/models/User.md +2 -0
- package/src/modules/user-management/docs/models/UserRole.md +2 -0
- package/src/schemas.ts +1 -0
- package/src/modules/primitives/command/convertAmount.ts +0 -126
- /package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +0 -0
- /package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,441 +1,113 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { z as
|
|
5
|
-
import { defineCommand as
|
|
4
|
+
import { z as z4 } from "zod";
|
|
5
|
+
import { defineCommand as defineCommand4, runMain, arg as arg4 } from "politty";
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
import path from "path";
|
|
7
|
+
// src/commands/init.ts
|
|
9
8
|
import fs from "fs";
|
|
10
|
-
import { execFile } from "child_process";
|
|
11
|
-
import { createRequire } from "module";
|
|
12
|
-
var require2 = createRequire(import.meta.url);
|
|
13
|
-
function getMdschemaBin() {
|
|
14
|
-
const pkgPath = require2.resolve("@jackchuka/mdschema/package.json");
|
|
15
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
16
|
-
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.mdschema;
|
|
17
|
-
if (!bin) {
|
|
18
|
-
throw new Error("Could not resolve mdschema binary from package.json bin field");
|
|
19
|
-
}
|
|
20
|
-
return path.join(path.dirname(pkgPath), bin);
|
|
21
|
-
}
|
|
22
|
-
function runMdschema(args, cwd2) {
|
|
23
|
-
return new Promise((resolve4) => {
|
|
24
|
-
execFile(
|
|
25
|
-
getMdschemaBin(),
|
|
26
|
-
args,
|
|
27
|
-
{ encoding: "utf-8", cwd: cwd2, timeout: 3e4 },
|
|
28
|
-
(error, stdout, stderr) => {
|
|
29
|
-
if (error) {
|
|
30
|
-
const execError = error;
|
|
31
|
-
resolve4({
|
|
32
|
-
exitCode: execError.code === "ENOENT" ? 127 : execError.status ?? 1,
|
|
33
|
-
stdout: stdout ?? "",
|
|
34
|
-
stderr: stderr ?? ""
|
|
35
|
-
});
|
|
36
|
-
} else {
|
|
37
|
-
resolve4({ exitCode: 0, stdout: stdout ?? "", stderr: stderr ?? "" });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// src/schemas.ts
|
|
45
|
-
import path3 from "path";
|
|
46
|
-
|
|
47
|
-
// src/util.ts
|
|
48
9
|
import path2 from "path";
|
|
49
|
-
|
|
10
|
+
import chalk from "chalk";
|
|
50
11
|
|
|
51
|
-
// src/
|
|
52
|
-
|
|
53
|
-
var
|
|
54
|
-
module: path3.join(SCHEMAS_ROOT, "module", "module.yml"),
|
|
55
|
-
command: path3.join(SCHEMAS_ROOT, "module", "command.yml"),
|
|
56
|
-
model: path3.join(SCHEMAS_ROOT, "module", "model.yml"),
|
|
57
|
-
feature: path3.join(SCHEMAS_ROOT, "module", "feature.yml")
|
|
58
|
-
};
|
|
59
|
-
var APP_COMPOSE_SCHEMAS = {
|
|
60
|
-
requirements: path3.join(SCHEMAS_ROOT, "app-compose", "requirements.yml"),
|
|
61
|
-
actors: path3.join(SCHEMAS_ROOT, "app-compose", "actors.yml"),
|
|
62
|
-
"business-flow": path3.join(SCHEMAS_ROOT, "app-compose", "business-flow.yml"),
|
|
63
|
-
story: path3.join(SCHEMAS_ROOT, "app-compose", "story.yml"),
|
|
64
|
-
screen: path3.join(SCHEMAS_ROOT, "app-compose", "screen.yml"),
|
|
65
|
-
resolver: path3.join(SCHEMAS_ROOT, "app-compose", "resolver.yml")
|
|
66
|
-
};
|
|
67
|
-
var ALL_SCHEMAS = {
|
|
68
|
-
...MODULE_SCHEMAS,
|
|
69
|
-
...APP_COMPOSE_SCHEMAS
|
|
70
|
-
};
|
|
12
|
+
// src/util.ts
|
|
13
|
+
import path from "path";
|
|
14
|
+
var PACKAGE_ROOT = path.resolve(import.meta.dirname, "..");
|
|
71
15
|
|
|
72
|
-
// src/commands/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (!schemaPath) {
|
|
103
|
-
console.error(`Unknown schema key: ${target.schemaKey}`);
|
|
104
|
-
return 2;
|
|
16
|
+
// src/commands/init.ts
|
|
17
|
+
var SKILLS_SRC = path2.join(PACKAGE_ROOT, "skills");
|
|
18
|
+
var FRAMEWORK_SKILLS = [
|
|
19
|
+
"1-module-docs",
|
|
20
|
+
"2-module-feature-breakdown",
|
|
21
|
+
"3-module-doc-review",
|
|
22
|
+
"4-module-tdd-implementation",
|
|
23
|
+
"5-module-implementation-review",
|
|
24
|
+
"app-compose-1-requirement-analysis",
|
|
25
|
+
"app-compose-2-requirements-breakdown",
|
|
26
|
+
"app-compose-3-doc-review",
|
|
27
|
+
"app-compose-4-design-mock",
|
|
28
|
+
"app-compose-5-design-mock-review",
|
|
29
|
+
"app-compose-6-implementation-spec",
|
|
30
|
+
"mock-scenario"
|
|
31
|
+
];
|
|
32
|
+
function copyDirectoryRecursive(srcDir, destDir, force) {
|
|
33
|
+
let copied = 0;
|
|
34
|
+
let skipped = 0;
|
|
35
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
36
|
+
const srcPath = path2.join(srcDir, entry.name);
|
|
37
|
+
const destPath = path2.join(destDir, entry.name);
|
|
38
|
+
if (entry.isDirectory()) {
|
|
39
|
+
const sub = copyDirectoryRecursive(srcPath, destPath, force);
|
|
40
|
+
copied += sub.copied;
|
|
41
|
+
skipped += sub.skipped;
|
|
42
|
+
} else {
|
|
43
|
+
if (!force && fs.existsSync(destPath)) {
|
|
44
|
+
skipped++;
|
|
45
|
+
continue;
|
|
105
46
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
);
|
|
110
|
-
if (stdout.trim()) console.log(stdout);
|
|
111
|
-
if (stderr.trim()) console.error(stderr);
|
|
112
|
-
return exitCode;
|
|
113
|
-
})
|
|
114
|
-
);
|
|
115
|
-
if (results.includes(2)) return 2;
|
|
116
|
-
return results.some((code) => code !== 0) ? 1 : 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// src/commands/sync-check.ts
|
|
120
|
-
import path4 from "path";
|
|
121
|
-
import fg from "fast-glob";
|
|
122
|
-
import chalk from "chalk";
|
|
123
|
-
function moduleCategories(root) {
|
|
124
|
-
return [
|
|
125
|
-
{
|
|
126
|
-
name: "command",
|
|
127
|
-
sourcePattern: `${root}/*/command/*.ts`,
|
|
128
|
-
docPattern: `${root}/*/docs/commands/*.md`,
|
|
129
|
-
exclusions: [/\.test\.ts$/]
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: "model",
|
|
133
|
-
sourcePattern: `${root}/*/db/*.ts`,
|
|
134
|
-
docPattern: `${root}/*/docs/models/*.md`,
|
|
135
|
-
exclusions: [/\.test\.ts$/, /^index\.ts$/]
|
|
47
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
48
|
+
fs.copyFileSync(srcPath, destPath);
|
|
49
|
+
copied++;
|
|
136
50
|
}
|
|
137
|
-
|
|
51
|
+
}
|
|
52
|
+
return { copied, skipped };
|
|
138
53
|
}
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
54
|
+
function runInit(cwd4, force) {
|
|
55
|
+
console.log(chalk.bold("erp-kit init\n"));
|
|
56
|
+
const skillsDest = path2.join(cwd4, ".agents", "skills");
|
|
57
|
+
let copiedCount = 0;
|
|
58
|
+
let skippedCount = 0;
|
|
59
|
+
for (const skill of FRAMEWORK_SKILLS) {
|
|
60
|
+
const srcSkillDir = path2.join(SKILLS_SRC, skill);
|
|
61
|
+
if (!fs.existsSync(srcSkillDir)) continue;
|
|
62
|
+
const destDir = path2.join(skillsDest, skill);
|
|
63
|
+
const result = copyDirectoryRecursive(srcSkillDir, destDir, force);
|
|
64
|
+
copiedCount += result.copied;
|
|
65
|
+
if (result.skipped > 0) {
|
|
66
|
+
console.log(chalk.yellow(` Skipped ${skill}/ (${result.skipped} existing files)`));
|
|
67
|
+
skippedCount += result.skipped;
|
|
146
68
|
}
|
|
147
|
-
];
|
|
148
|
-
}
|
|
149
|
-
function shouldExclude(fileName, exclusions) {
|
|
150
|
-
return exclusions.some((pattern) => pattern.test(fileName));
|
|
151
|
-
}
|
|
152
|
-
async function runSyncCheck(config, cwd2) {
|
|
153
|
-
const errors = [];
|
|
154
|
-
let totalSources = 0;
|
|
155
|
-
let totalDocs = 0;
|
|
156
|
-
const allCategories = [];
|
|
157
|
-
if (config.modulesRoot) {
|
|
158
|
-
allCategories.push(...moduleCategories(config.modulesRoot));
|
|
159
|
-
}
|
|
160
|
-
if (config.appRoot) {
|
|
161
|
-
allCategories.push(...appComposeCategories(config.appRoot));
|
|
162
69
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for (const sourcePath of sources) {
|
|
169
|
-
const fileName = path4.basename(sourcePath);
|
|
170
|
-
if (shouldExclude(fileName, category.exclusions)) continue;
|
|
171
|
-
const basename2 = path4.basename(sourcePath, path4.extname(sourcePath));
|
|
172
|
-
sourceBasenames.set(basename2.toLowerCase(), sourcePath);
|
|
173
|
-
}
|
|
174
|
-
for (const docPath of docs) {
|
|
175
|
-
const basename2 = path4.basename(docPath, path4.extname(docPath));
|
|
176
|
-
docBasenames.set(basename2.toLowerCase(), docPath);
|
|
177
|
-
}
|
|
178
|
-
for (const [basename2, sourcePath] of sourceBasenames) {
|
|
179
|
-
if (!docBasenames.has(basename2)) {
|
|
180
|
-
errors.push({
|
|
181
|
-
type: "missing-doc",
|
|
182
|
-
category: category.name,
|
|
183
|
-
sourcePath,
|
|
184
|
-
expectedBasename: basename2
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
for (const [basename2, docPath] of docBasenames) {
|
|
189
|
-
if (!sourceBasenames.has(basename2)) {
|
|
190
|
-
errors.push({
|
|
191
|
-
type: "orphaned-doc",
|
|
192
|
-
category: category.name,
|
|
193
|
-
docPath,
|
|
194
|
-
expectedBasename: basename2
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
totalSources += sourceBasenames.size;
|
|
199
|
-
totalDocs += docBasenames.size;
|
|
70
|
+
console.log(chalk.green(` Copied ${copiedCount} skill files to .agents/skills/`));
|
|
71
|
+
if (skippedCount > 0) {
|
|
72
|
+
console.log(
|
|
73
|
+
chalk.yellow(` Skipped ${skippedCount} existing files (use --force to overwrite)`)
|
|
74
|
+
);
|
|
200
75
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function formatSyncCheckReport(result) {
|
|
212
|
-
const lines = [];
|
|
213
|
-
lines.push(chalk.bold("docs-sync-check: Checking source-documentation correspondence...\n"));
|
|
214
|
-
if (result.errors.length > 0) {
|
|
215
|
-
lines.push(chalk.red.bold("Errors:\n"));
|
|
216
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
217
|
-
for (const error of result.errors) {
|
|
218
|
-
const existing = byCategory.get(error.category) ?? [];
|
|
219
|
-
existing.push(error);
|
|
220
|
-
byCategory.set(error.category, existing);
|
|
76
|
+
const claudeSkills = path2.join(cwd4, ".claude", "skills");
|
|
77
|
+
const relTarget = path2.relative(path2.dirname(claudeSkills), skillsDest);
|
|
78
|
+
const claudeSkillsExists = (() => {
|
|
79
|
+
try {
|
|
80
|
+
fs.lstatSync(claudeSkills);
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
221
84
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
85
|
+
})();
|
|
86
|
+
if (claudeSkillsExists) {
|
|
87
|
+
const stat2 = fs.lstatSync(claudeSkills);
|
|
88
|
+
if (stat2.isSymbolicLink()) {
|
|
89
|
+
const current = fs.readlinkSync(claudeSkills);
|
|
90
|
+
if (current === relTarget) {
|
|
91
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (already linked)"));
|
|
92
|
+
} else if (force) {
|
|
93
|
+
fs.unlinkSync(claudeSkills);
|
|
94
|
+
fs.symlinkSync(relTarget, claudeSkills);
|
|
95
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (relinked)"));
|
|
96
|
+
} else {
|
|
97
|
+
console.log(
|
|
98
|
+
chalk.yellow(` Skipped .claude/skills (symlink exists -> ${current}, use --force)`)
|
|
99
|
+
);
|
|
236
100
|
}
|
|
101
|
+
} else {
|
|
102
|
+
console.log(chalk.yellow(" Skipped .claude/skills (directory exists, not a symlink)"));
|
|
237
103
|
}
|
|
238
104
|
} else {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
lines.push(` Categories checked: ${result.summary.categoriesChecked}`);
|
|
243
|
-
lines.push(
|
|
244
|
-
` Source files: ${result.summary.totalSources}, Doc files: ${result.summary.totalDocs}`
|
|
245
|
-
);
|
|
246
|
-
if (result.errors.length > 0) {
|
|
247
|
-
lines.push(chalk.red(` Errors: ${result.errors.length}`));
|
|
248
|
-
lines.push("");
|
|
249
|
-
lines.push(chalk.red.bold(`docs-sync-check failed with ${result.errors.length} error(s).`));
|
|
250
|
-
} else {
|
|
251
|
-
lines.push(chalk.green(" Errors: 0"));
|
|
252
|
-
lines.push("");
|
|
253
|
-
lines.push(chalk.green.bold("docs-sync-check passed."));
|
|
105
|
+
fs.mkdirSync(path2.dirname(claudeSkills), { recursive: true });
|
|
106
|
+
fs.symlinkSync(relTarget, claudeSkills);
|
|
107
|
+
console.log(chalk.green(" .claude/skills -> .agents/skills/ (linked)"));
|
|
254
108
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// src/commands/scaffold.ts
|
|
259
|
-
import path5 from "path";
|
|
260
|
-
import fs2 from "fs";
|
|
261
|
-
var MODULE_TYPES = ["module", "feature", "command", "model"];
|
|
262
|
-
var APP_TYPES = [
|
|
263
|
-
"requirements",
|
|
264
|
-
"actors",
|
|
265
|
-
"business-flow",
|
|
266
|
-
"story",
|
|
267
|
-
"screen",
|
|
268
|
-
"resolver"
|
|
269
|
-
];
|
|
270
|
-
var ALL_TYPES = [...MODULE_TYPES, ...APP_TYPES];
|
|
271
|
-
var MODULE_DIR_MAP = {
|
|
272
|
-
feature: "docs/features",
|
|
273
|
-
command: "docs/commands",
|
|
274
|
-
model: "docs/models"
|
|
275
|
-
};
|
|
276
|
-
var APP_DIR_MAP = {
|
|
277
|
-
actors: "docs/actors",
|
|
278
|
-
"business-flow": "docs/business-flow",
|
|
279
|
-
screen: "docs/screen",
|
|
280
|
-
resolver: "docs/resolver"
|
|
281
|
-
};
|
|
282
|
-
function isModuleType(type) {
|
|
283
|
-
return MODULE_TYPES.includes(type);
|
|
284
|
-
}
|
|
285
|
-
function resolveScaffoldPath(type, parentName, name, root) {
|
|
286
|
-
if (type === "module" || type === "requirements") {
|
|
287
|
-
return path5.join(root, parentName, "README.md");
|
|
288
|
-
}
|
|
289
|
-
if (!name) {
|
|
290
|
-
throw new Error(`Name is required for scaffold type "${type}"`);
|
|
291
|
-
}
|
|
292
|
-
if (type === "business-flow") {
|
|
293
|
-
return path5.join(root, parentName, "docs/business-flow", name, "README.md");
|
|
294
|
-
}
|
|
295
|
-
if (type === "story") {
|
|
296
|
-
const parts = name.split("/");
|
|
297
|
-
if (parts.length !== 2) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
return path5.join(root, parentName, "docs/business-flow", parts[0], "story", `${parts[1]}.md`);
|
|
303
|
-
}
|
|
304
|
-
if (MODULE_DIR_MAP[type]) {
|
|
305
|
-
return path5.join(root, parentName, MODULE_DIR_MAP[type], `${name}.md`);
|
|
306
|
-
}
|
|
307
|
-
if (APP_DIR_MAP[type]) {
|
|
308
|
-
return path5.join(root, parentName, APP_DIR_MAP[type], `${name}.md`);
|
|
309
|
-
}
|
|
310
|
-
throw new Error(`Unknown scaffold type: ${type}`);
|
|
311
|
-
}
|
|
312
|
-
async function runScaffold(type, parentName, name, root, cwd2) {
|
|
313
|
-
const outputPath = resolveScaffoldPath(type, parentName, name, root);
|
|
314
|
-
const absoluteOutput = path5.resolve(cwd2, outputPath);
|
|
315
|
-
if (fs2.existsSync(absoluteOutput)) {
|
|
316
|
-
console.error(`File already exists: ${outputPath}`);
|
|
317
|
-
return 1;
|
|
318
|
-
}
|
|
319
|
-
const schemaPath = ALL_SCHEMAS[type];
|
|
320
|
-
if (!schemaPath) {
|
|
321
|
-
console.error(`No schema found for type: ${type}`);
|
|
322
|
-
return 2;
|
|
323
|
-
}
|
|
324
|
-
try {
|
|
325
|
-
fs2.mkdirSync(path5.dirname(absoluteOutput), { recursive: true });
|
|
326
|
-
} catch (err) {
|
|
327
|
-
console.error(
|
|
328
|
-
`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`
|
|
329
|
-
);
|
|
330
|
-
return 1;
|
|
331
|
-
}
|
|
332
|
-
const { exitCode, stdout, stderr } = await runMdschema(
|
|
333
|
-
["generate", "--schema", schemaPath, "--output", absoluteOutput],
|
|
334
|
-
cwd2
|
|
335
|
-
);
|
|
336
|
-
if (stdout.trim()) console.log(stdout);
|
|
337
|
-
if (stderr.trim()) console.error(stderr);
|
|
338
|
-
return exitCode;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// src/commands/init.ts
|
|
342
|
-
import fs3 from "fs";
|
|
343
|
-
import path6 from "path";
|
|
344
|
-
import chalk2 from "chalk";
|
|
345
|
-
var SKILLS_SRC = path6.join(PACKAGE_ROOT, "skills");
|
|
346
|
-
var FRAMEWORK_SKILLS = [
|
|
347
|
-
"1-module-docs",
|
|
348
|
-
"2-module-feature-breakdown",
|
|
349
|
-
"3-module-doc-review",
|
|
350
|
-
"4-module-tdd-implementation",
|
|
351
|
-
"5-module-implementation-review",
|
|
352
|
-
"app-compose-1-requirement-analysis",
|
|
353
|
-
"app-compose-2-requirements-breakdown",
|
|
354
|
-
"app-compose-3-doc-review",
|
|
355
|
-
"app-compose-4-design-mock",
|
|
356
|
-
"app-compose-5-design-mock-review",
|
|
357
|
-
"app-compose-6-implementation-spec",
|
|
358
|
-
"mock-scenario"
|
|
359
|
-
];
|
|
360
|
-
function copyDirectoryRecursive(srcDir, destDir, force) {
|
|
361
|
-
let copied = 0;
|
|
362
|
-
let skipped = 0;
|
|
363
|
-
for (const entry of fs3.readdirSync(srcDir, { withFileTypes: true })) {
|
|
364
|
-
const srcPath = path6.join(srcDir, entry.name);
|
|
365
|
-
const destPath = path6.join(destDir, entry.name);
|
|
366
|
-
if (entry.isDirectory()) {
|
|
367
|
-
const sub = copyDirectoryRecursive(srcPath, destPath, force);
|
|
368
|
-
copied += sub.copied;
|
|
369
|
-
skipped += sub.skipped;
|
|
370
|
-
} else {
|
|
371
|
-
if (!force && fs3.existsSync(destPath)) {
|
|
372
|
-
skipped++;
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
fs3.mkdirSync(destDir, { recursive: true });
|
|
376
|
-
fs3.copyFileSync(srcPath, destPath);
|
|
377
|
-
copied++;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return { copied, skipped };
|
|
381
|
-
}
|
|
382
|
-
function runInit(cwd2, force) {
|
|
383
|
-
console.log(chalk2.bold("erp-kit init\n"));
|
|
384
|
-
const skillsDest = path6.join(cwd2, ".agents", "skills");
|
|
385
|
-
let copiedCount = 0;
|
|
386
|
-
let skippedCount = 0;
|
|
387
|
-
for (const skill of FRAMEWORK_SKILLS) {
|
|
388
|
-
const srcSkillDir = path6.join(SKILLS_SRC, skill);
|
|
389
|
-
if (!fs3.existsSync(srcSkillDir)) continue;
|
|
390
|
-
const destDir = path6.join(skillsDest, skill);
|
|
391
|
-
const result = copyDirectoryRecursive(srcSkillDir, destDir, force);
|
|
392
|
-
copiedCount += result.copied;
|
|
393
|
-
if (result.skipped > 0) {
|
|
394
|
-
console.log(chalk2.yellow(` Skipped ${skill}/ (${result.skipped} existing files)`));
|
|
395
|
-
skippedCount += result.skipped;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
console.log(chalk2.green(` Copied ${copiedCount} skill files to .agents/skills/`));
|
|
399
|
-
if (skippedCount > 0) {
|
|
400
|
-
console.log(
|
|
401
|
-
chalk2.yellow(` Skipped ${skippedCount} existing files (use --force to overwrite)`)
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
const claudeSkills = path6.join(cwd2, ".claude", "skills");
|
|
405
|
-
const relTarget = path6.relative(path6.dirname(claudeSkills), skillsDest);
|
|
406
|
-
const claudeSkillsExists = (() => {
|
|
407
|
-
try {
|
|
408
|
-
fs3.lstatSync(claudeSkills);
|
|
409
|
-
return true;
|
|
410
|
-
} catch {
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
})();
|
|
414
|
-
if (claudeSkillsExists) {
|
|
415
|
-
const stat2 = fs3.lstatSync(claudeSkills);
|
|
416
|
-
if (stat2.isSymbolicLink()) {
|
|
417
|
-
const current = fs3.readlinkSync(claudeSkills);
|
|
418
|
-
if (current === relTarget) {
|
|
419
|
-
console.log(chalk2.green(" .claude/skills -> .agents/skills/ (already linked)"));
|
|
420
|
-
} else if (force) {
|
|
421
|
-
fs3.unlinkSync(claudeSkills);
|
|
422
|
-
fs3.symlinkSync(relTarget, claudeSkills);
|
|
423
|
-
console.log(chalk2.green(" .claude/skills -> .agents/skills/ (relinked)"));
|
|
424
|
-
} else {
|
|
425
|
-
console.log(
|
|
426
|
-
chalk2.yellow(` Skipped .claude/skills (symlink exists -> ${current}, use --force)`)
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
} else {
|
|
430
|
-
console.log(chalk2.yellow(" Skipped .claude/skills (directory exists, not a symlink)"));
|
|
431
|
-
}
|
|
432
|
-
} else {
|
|
433
|
-
fs3.mkdirSync(path6.dirname(claudeSkills), { recursive: true });
|
|
434
|
-
fs3.symlinkSync(relTarget, claudeSkills);
|
|
435
|
-
console.log(chalk2.green(" .claude/skills -> .agents/skills/ (linked)"));
|
|
436
|
-
}
|
|
437
|
-
console.log(chalk2.bold.green("\nDone! Run `erp-kit check` to validate your docs."));
|
|
438
|
-
return 0;
|
|
109
|
+
console.log(chalk.bold.green("\nDone! Run `erp-kit check` to validate your docs."));
|
|
110
|
+
return 0;
|
|
439
111
|
}
|
|
440
112
|
|
|
441
113
|
// src/commands/mock/index.ts
|
|
@@ -620,12 +292,12 @@ Reverse proxy listening on http://localhost:${port}`);
|
|
|
620
292
|
// src/commands/mock/validate.ts
|
|
621
293
|
import { readdir, readFile, stat } from "fs/promises";
|
|
622
294
|
import { join as join2, resolve as resolve3, relative as relative2, dirname as dirname2, basename } from "path";
|
|
623
|
-
import
|
|
295
|
+
import chalk2 from "chalk";
|
|
624
296
|
function pass(msg) {
|
|
625
|
-
console.log(
|
|
297
|
+
console.log(chalk2.green(`\u2713 ${msg}`));
|
|
626
298
|
}
|
|
627
299
|
function fail(ctx, msg) {
|
|
628
|
-
console.log(
|
|
300
|
+
console.log(chalk2.red(`\u2717 ${msg}`));
|
|
629
301
|
ctx.failures++;
|
|
630
302
|
}
|
|
631
303
|
async function subdirs(dir) {
|
|
@@ -683,232 +355,680 @@ function checkResponseQuality(ctx, data, label) {
|
|
|
683
355
|
}
|
|
684
356
|
}
|
|
685
357
|
}
|
|
686
|
-
function checkDatabucketRefs(ctx, data, label) {
|
|
687
|
-
const bucketIds = new Set((data.data ?? []).map((d) => d.id));
|
|
688
|
-
for (const route of data.routes ?? []) {
|
|
689
|
-
for (const resp of route.responses ?? []) {
|
|
690
|
-
if (resp.bodyType === "DATABUCKET") {
|
|
691
|
-
const respLabel = `${label} \u2192 ${route.uuid}/${resp.uuid}`;
|
|
692
|
-
if (resp.databucketID && bucketIds.has(resp.databucketID)) {
|
|
693
|
-
pass(`${respLabel}: databucket "${resp.databucketID}" exists`);
|
|
358
|
+
function checkDatabucketRefs(ctx, data, label) {
|
|
359
|
+
const bucketIds = new Set((data.data ?? []).map((d) => d.id));
|
|
360
|
+
for (const route of data.routes ?? []) {
|
|
361
|
+
for (const resp of route.responses ?? []) {
|
|
362
|
+
if (resp.bodyType === "DATABUCKET") {
|
|
363
|
+
const respLabel = `${label} \u2192 ${route.uuid}/${resp.uuid}`;
|
|
364
|
+
if (resp.databucketID && bucketIds.has(resp.databucketID)) {
|
|
365
|
+
pass(`${respLabel}: databucket "${resp.databucketID}" exists`);
|
|
366
|
+
} else {
|
|
367
|
+
fail(ctx, `${respLabel}: databucketID "${resp.databucketID}" not found in data array`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function discoverAllScenarios(mocksDir) {
|
|
374
|
+
const scenarios = [];
|
|
375
|
+
const providers = await subdirs(mocksDir);
|
|
376
|
+
for (const provider of providers) {
|
|
377
|
+
const providerDir = join2(mocksDir, provider);
|
|
378
|
+
for (const scenario of await subdirs(providerDir)) {
|
|
379
|
+
scenarios.push(`${provider}/${scenario}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return scenarios;
|
|
383
|
+
}
|
|
384
|
+
function resolveScenarioDir(arg5) {
|
|
385
|
+
const abs = resolve3(arg5);
|
|
386
|
+
if (basename(abs) === "mock.json") return dirname2(abs);
|
|
387
|
+
return abs;
|
|
388
|
+
}
|
|
389
|
+
async function validateScenario(ctx, scenarioDir, mocksDir) {
|
|
390
|
+
const label = relative2(mocksDir, scenarioDir);
|
|
391
|
+
console.log(chalk2.bold(`
|
|
392
|
+
\u2500\u2500 ${label} \u2500\u2500`));
|
|
393
|
+
let entries;
|
|
394
|
+
try {
|
|
395
|
+
entries = await readdir(scenarioDir);
|
|
396
|
+
} catch {
|
|
397
|
+
fail(ctx, `${label}: directory not found`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const hasMock = checkStructure(ctx, entries, label);
|
|
401
|
+
if (!hasMock) return;
|
|
402
|
+
const mockPath = join2(scenarioDir, "mock.json");
|
|
403
|
+
let data;
|
|
404
|
+
try {
|
|
405
|
+
data = JSON.parse(await readFile(mockPath, "utf-8"));
|
|
406
|
+
} catch (err) {
|
|
407
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
408
|
+
fail(ctx, `${label}: invalid JSON \u2014 ${errMsg}`);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
await checkSchema(ctx, data, label);
|
|
412
|
+
checkResponseQuality(ctx, data, label);
|
|
413
|
+
checkDatabucketRefs(ctx, data, label);
|
|
414
|
+
}
|
|
415
|
+
async function runMockValidate(mocksRoot, paths) {
|
|
416
|
+
const ctx = { failures: 0 };
|
|
417
|
+
const mocksDir = resolve3(mocksRoot);
|
|
418
|
+
const targets = paths.length > 0 ? paths.map(resolveScenarioDir) : (await discoverAllScenarios(mocksDir)).map((s) => join2(mocksDir, s));
|
|
419
|
+
if (targets.length === 0) {
|
|
420
|
+
fail(ctx, "No scenarios found under mocks/");
|
|
421
|
+
return 1;
|
|
422
|
+
}
|
|
423
|
+
console.log(chalk2.bold("\nValidating mock configs...\n"));
|
|
424
|
+
for (const target of targets) {
|
|
425
|
+
await validateScenario(ctx, target, mocksDir);
|
|
426
|
+
}
|
|
427
|
+
console.log(chalk2.bold("\n\u2500\u2500 summary \u2500\u2500"));
|
|
428
|
+
if (ctx.failures === 0) {
|
|
429
|
+
console.log(chalk2.green("\u2713 All checks passed"));
|
|
430
|
+
} else {
|
|
431
|
+
console.log(chalk2.red(`\u2717 ${ctx.failures} check(s) failed`));
|
|
432
|
+
}
|
|
433
|
+
return ctx.failures === 0 ? 0 : 1;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/commands/mock/index.ts
|
|
437
|
+
var startCommand = defineCommand({
|
|
438
|
+
name: "start",
|
|
439
|
+
description: "Start mock API servers with reverse proxy",
|
|
440
|
+
args: z.object({
|
|
441
|
+
mocksRoot: arg(z.string().default("./mocks"), {
|
|
442
|
+
description: "Path to mocks directory"
|
|
443
|
+
}),
|
|
444
|
+
port: arg(z.coerce.number().default(3e3), {
|
|
445
|
+
alias: "p",
|
|
446
|
+
description: "Reverse proxy port"
|
|
447
|
+
}),
|
|
448
|
+
filter: arg(z.array(z.string()).default([]), {
|
|
449
|
+
positional: true,
|
|
450
|
+
description: "Filter by provider or provider/scenario"
|
|
451
|
+
})
|
|
452
|
+
}),
|
|
453
|
+
run: async (args) => {
|
|
454
|
+
const exitCode = await runMockStart(args.mocksRoot, args.filter, args.port);
|
|
455
|
+
process.exit(exitCode);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
var validateCommand = defineCommand({
|
|
459
|
+
name: "validate",
|
|
460
|
+
description: "Validate mock scenario configs",
|
|
461
|
+
args: z.object({
|
|
462
|
+
mocksRoot: arg(z.string().default("./mocks"), {
|
|
463
|
+
description: "Path to mocks directory"
|
|
464
|
+
}),
|
|
465
|
+
paths: arg(z.array(z.string()).default([]), {
|
|
466
|
+
positional: true,
|
|
467
|
+
description: "Specific scenario paths to validate"
|
|
468
|
+
})
|
|
469
|
+
}),
|
|
470
|
+
run: async (args) => {
|
|
471
|
+
const exitCode = await runMockValidate(args.mocksRoot, args.paths);
|
|
472
|
+
process.exit(exitCode);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
var mockCommand = defineCommand({
|
|
476
|
+
name: "mock",
|
|
477
|
+
description: "Mock API server management",
|
|
478
|
+
subCommands: {
|
|
479
|
+
start: startCommand,
|
|
480
|
+
validate: validateCommand
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// src/commands/module/index.ts
|
|
485
|
+
import { z as z2 } from "zod";
|
|
486
|
+
import { defineCommand as defineCommand2, arg as arg2 } from "politty";
|
|
487
|
+
|
|
488
|
+
// src/mdschema.ts
|
|
489
|
+
import path3 from "path";
|
|
490
|
+
import fs2 from "fs";
|
|
491
|
+
import { execFile } from "child_process";
|
|
492
|
+
import { createRequire } from "module";
|
|
493
|
+
var require2 = createRequire(import.meta.url);
|
|
494
|
+
function getMdschemaBin() {
|
|
495
|
+
const pkgPath = require2.resolve("@jackchuka/mdschema/package.json");
|
|
496
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
497
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.mdschema;
|
|
498
|
+
if (!bin) {
|
|
499
|
+
throw new Error("Could not resolve mdschema binary from package.json bin field");
|
|
500
|
+
}
|
|
501
|
+
return path3.join(path3.dirname(pkgPath), bin);
|
|
502
|
+
}
|
|
503
|
+
function runMdschema(args, cwd4) {
|
|
504
|
+
return new Promise((resolve4) => {
|
|
505
|
+
execFile(
|
|
506
|
+
getMdschemaBin(),
|
|
507
|
+
args,
|
|
508
|
+
{ encoding: "utf-8", cwd: cwd4, timeout: 3e4 },
|
|
509
|
+
(error, stdout, stderr) => {
|
|
510
|
+
if (error) {
|
|
511
|
+
const execError = error;
|
|
512
|
+
resolve4({
|
|
513
|
+
exitCode: execError.code === "ENOENT" ? 127 : execError.status ?? 1,
|
|
514
|
+
stdout: stdout ?? "",
|
|
515
|
+
stderr: stderr ?? ""
|
|
516
|
+
});
|
|
517
|
+
} else {
|
|
518
|
+
resolve4({ exitCode: 0, stdout: stdout ?? "", stderr: stderr ?? "" });
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/schemas.ts
|
|
526
|
+
import path4 from "path";
|
|
527
|
+
var SCHEMAS_ROOT = path4.join(PACKAGE_ROOT, "schemas");
|
|
528
|
+
var MODULE_SCHEMAS = {
|
|
529
|
+
module: path4.join(SCHEMAS_ROOT, "module", "module.yml"),
|
|
530
|
+
command: path4.join(SCHEMAS_ROOT, "module", "command.yml"),
|
|
531
|
+
model: path4.join(SCHEMAS_ROOT, "module", "model.yml"),
|
|
532
|
+
feature: path4.join(SCHEMAS_ROOT, "module", "feature.yml"),
|
|
533
|
+
query: path4.join(SCHEMAS_ROOT, "module", "query.yml")
|
|
534
|
+
};
|
|
535
|
+
var APP_COMPOSE_SCHEMAS = {
|
|
536
|
+
requirements: path4.join(SCHEMAS_ROOT, "app-compose", "requirements.yml"),
|
|
537
|
+
actors: path4.join(SCHEMAS_ROOT, "app-compose", "actors.yml"),
|
|
538
|
+
"business-flow": path4.join(SCHEMAS_ROOT, "app-compose", "business-flow.yml"),
|
|
539
|
+
story: path4.join(SCHEMAS_ROOT, "app-compose", "story.yml"),
|
|
540
|
+
screen: path4.join(SCHEMAS_ROOT, "app-compose", "screen.yml"),
|
|
541
|
+
resolver: path4.join(SCHEMAS_ROOT, "app-compose", "resolver.yml")
|
|
542
|
+
};
|
|
543
|
+
var ALL_SCHEMAS = {
|
|
544
|
+
...MODULE_SCHEMAS,
|
|
545
|
+
...APP_COMPOSE_SCHEMAS
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
// src/commands/check.ts
|
|
549
|
+
function buildCheckTargets(config) {
|
|
550
|
+
const targets = [];
|
|
551
|
+
if (config.modulesRoot) {
|
|
552
|
+
const m = config.modulesRoot;
|
|
553
|
+
targets.push(
|
|
554
|
+
{ glob: `${m}/[a-zA-Z]*/docs/features/*.md`, schemaKey: "feature" },
|
|
555
|
+
{ glob: `${m}/[a-zA-Z]*/docs/commands/*.md`, schemaKey: "command" },
|
|
556
|
+
{ glob: `${m}/[a-zA-Z]*/docs/models/*.md`, schemaKey: "model" },
|
|
557
|
+
{ glob: `${m}/[a-zA-Z]*/docs/queries/*.md`, schemaKey: "query" },
|
|
558
|
+
{ glob: `${m}/[a-zA-Z]*/README.md`, schemaKey: "module" }
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
if (config.appRoot) {
|
|
562
|
+
const a = config.appRoot;
|
|
563
|
+
targets.push(
|
|
564
|
+
{ glob: `${a}/[a-zA-Z]*/README.md`, schemaKey: "requirements" },
|
|
565
|
+
{ glob: `${a}/[a-zA-Z]*/docs/actors/*.md`, schemaKey: "actors" },
|
|
566
|
+
{ glob: `${a}/[a-zA-Z]*/docs/business-flow/*/README.md`, schemaKey: "business-flow" },
|
|
567
|
+
{ glob: `${a}/[a-zA-Z]*/docs/business-flow/*/story/*.md`, schemaKey: "story" },
|
|
568
|
+
{ glob: `${a}/[a-zA-Z]*/docs/screen/*.md`, schemaKey: "screen" },
|
|
569
|
+
{ glob: `${a}/[a-zA-Z]*/docs/resolver/*.md`, schemaKey: "resolver" }
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
return targets;
|
|
573
|
+
}
|
|
574
|
+
async function runCheck(config, cwd4) {
|
|
575
|
+
const targets = buildCheckTargets(config);
|
|
576
|
+
const results = await Promise.all(
|
|
577
|
+
targets.map(async (target) => {
|
|
578
|
+
const schemaPath = ALL_SCHEMAS[target.schemaKey];
|
|
579
|
+
if (!schemaPath) {
|
|
580
|
+
console.error(`Unknown schema key: ${target.schemaKey}`);
|
|
581
|
+
return 2;
|
|
582
|
+
}
|
|
583
|
+
const { exitCode, stdout, stderr } = await runMdschema(
|
|
584
|
+
["check", target.glob, "--schema", schemaPath],
|
|
585
|
+
cwd4
|
|
586
|
+
);
|
|
587
|
+
if (stdout.trim()) console.log(stdout);
|
|
588
|
+
if (stderr.trim()) console.error(stderr);
|
|
589
|
+
return exitCode;
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
if (results.includes(2)) return 2;
|
|
593
|
+
return results.some((code) => code !== 0) ? 1 : 0;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/commands/sync-check.ts
|
|
597
|
+
import path5 from "path";
|
|
598
|
+
import fg from "fast-glob";
|
|
599
|
+
import chalk3 from "chalk";
|
|
600
|
+
function moduleCategories(root) {
|
|
601
|
+
return [
|
|
602
|
+
{
|
|
603
|
+
name: "command",
|
|
604
|
+
sourcePattern: `${root}/*/command/*.ts`,
|
|
605
|
+
docPattern: `${root}/*/docs/commands/*.md`,
|
|
606
|
+
exclusions: [/\.test\.ts$/]
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "model",
|
|
610
|
+
sourcePattern: `${root}/*/db/*.ts`,
|
|
611
|
+
docPattern: `${root}/*/docs/models/*.md`,
|
|
612
|
+
exclusions: [/\.test\.ts$/, /^index\.ts$/]
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: "query",
|
|
616
|
+
sourcePattern: `${root}/*/query/*.ts`,
|
|
617
|
+
docPattern: `${root}/*/docs/queries/*.md`,
|
|
618
|
+
exclusions: [/\.test\.ts$/]
|
|
619
|
+
}
|
|
620
|
+
];
|
|
621
|
+
}
|
|
622
|
+
function appComposeCategories(root) {
|
|
623
|
+
return [
|
|
624
|
+
{
|
|
625
|
+
name: "resolver",
|
|
626
|
+
sourcePattern: `${root}/*/backend/src/modules/**/resolvers/*.ts`,
|
|
627
|
+
docPattern: `${root}/*/docs/resolver/*.md`,
|
|
628
|
+
exclusions: [/\.test\.ts$/, /^index\.ts$/]
|
|
629
|
+
}
|
|
630
|
+
];
|
|
631
|
+
}
|
|
632
|
+
function shouldExclude(fileName, exclusions) {
|
|
633
|
+
return exclusions.some((pattern) => pattern.test(fileName));
|
|
634
|
+
}
|
|
635
|
+
async function runSyncCheck(config, cwd4) {
|
|
636
|
+
const errors = [];
|
|
637
|
+
let totalSources = 0;
|
|
638
|
+
let totalDocs = 0;
|
|
639
|
+
const allCategories = [];
|
|
640
|
+
if (config.modulesRoot) {
|
|
641
|
+
allCategories.push(...moduleCategories(config.modulesRoot));
|
|
642
|
+
}
|
|
643
|
+
if (config.appRoot) {
|
|
644
|
+
allCategories.push(...appComposeCategories(config.appRoot));
|
|
645
|
+
}
|
|
646
|
+
for (const category of allCategories) {
|
|
647
|
+
const sources = await fg(category.sourcePattern, { cwd: cwd4 });
|
|
648
|
+
const docs = await fg(category.docPattern, { cwd: cwd4 });
|
|
649
|
+
const sourceBasenames = /* @__PURE__ */ new Map();
|
|
650
|
+
const docBasenames = /* @__PURE__ */ new Map();
|
|
651
|
+
for (const sourcePath of sources) {
|
|
652
|
+
const fileName = path5.basename(sourcePath);
|
|
653
|
+
if (shouldExclude(fileName, category.exclusions)) continue;
|
|
654
|
+
const basename2 = path5.basename(sourcePath, path5.extname(sourcePath));
|
|
655
|
+
sourceBasenames.set(basename2.toLowerCase(), sourcePath);
|
|
656
|
+
}
|
|
657
|
+
for (const docPath of docs) {
|
|
658
|
+
const basename2 = path5.basename(docPath, path5.extname(docPath));
|
|
659
|
+
docBasenames.set(basename2.toLowerCase(), docPath);
|
|
660
|
+
}
|
|
661
|
+
for (const [basename2, sourcePath] of sourceBasenames) {
|
|
662
|
+
if (!docBasenames.has(basename2)) {
|
|
663
|
+
errors.push({
|
|
664
|
+
type: "missing-doc",
|
|
665
|
+
category: category.name,
|
|
666
|
+
sourcePath,
|
|
667
|
+
expectedBasename: basename2
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
for (const [basename2, docPath] of docBasenames) {
|
|
672
|
+
if (!sourceBasenames.has(basename2)) {
|
|
673
|
+
errors.push({
|
|
674
|
+
type: "orphaned-doc",
|
|
675
|
+
category: category.name,
|
|
676
|
+
docPath,
|
|
677
|
+
expectedBasename: basename2
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
totalSources += sourceBasenames.size;
|
|
682
|
+
totalDocs += docBasenames.size;
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
exitCode: errors.length > 0 ? 1 : 0,
|
|
686
|
+
errors,
|
|
687
|
+
summary: {
|
|
688
|
+
categoriesChecked: allCategories.length,
|
|
689
|
+
totalSources,
|
|
690
|
+
totalDocs
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function formatSyncCheckReport(result) {
|
|
695
|
+
const lines = [];
|
|
696
|
+
lines.push(chalk3.bold("docs-sync-check: Checking source-documentation correspondence...\n"));
|
|
697
|
+
if (result.errors.length > 0) {
|
|
698
|
+
lines.push(chalk3.red.bold("Errors:\n"));
|
|
699
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
700
|
+
for (const error of result.errors) {
|
|
701
|
+
const existing = byCategory.get(error.category) ?? [];
|
|
702
|
+
existing.push(error);
|
|
703
|
+
byCategory.set(error.category, existing);
|
|
704
|
+
}
|
|
705
|
+
for (const [category, categoryErrors] of byCategory) {
|
|
706
|
+
lines.push(chalk3.cyan(` Category: ${category}
|
|
707
|
+
`));
|
|
708
|
+
for (const error of categoryErrors) {
|
|
709
|
+
if (error.type === "missing-doc") {
|
|
710
|
+
lines.push(` ${chalk3.red(error.sourcePath)}`);
|
|
711
|
+
lines.push(` ${chalk3.yellow("Missing documentation for:")} ${error.expectedBasename}`);
|
|
694
712
|
} else {
|
|
695
|
-
|
|
713
|
+
lines.push(` ${chalk3.red(error.docPath)}`);
|
|
714
|
+
lines.push(
|
|
715
|
+
` ${chalk3.yellow("Orphaned documentation:")} no source file for ${error.expectedBasename}`
|
|
716
|
+
);
|
|
696
717
|
}
|
|
718
|
+
lines.push("");
|
|
697
719
|
}
|
|
698
720
|
}
|
|
721
|
+
} else {
|
|
722
|
+
lines.push(chalk3.green("All source files have corresponding documentation.\n"));
|
|
723
|
+
}
|
|
724
|
+
lines.push(chalk3.bold("Summary:"));
|
|
725
|
+
lines.push(` Categories checked: ${result.summary.categoriesChecked}`);
|
|
726
|
+
lines.push(
|
|
727
|
+
` Source files: ${result.summary.totalSources}, Doc files: ${result.summary.totalDocs}`
|
|
728
|
+
);
|
|
729
|
+
if (result.errors.length > 0) {
|
|
730
|
+
lines.push(chalk3.red(` Errors: ${result.errors.length}`));
|
|
731
|
+
lines.push("");
|
|
732
|
+
lines.push(chalk3.red.bold(`docs-sync-check failed with ${result.errors.length} error(s).`));
|
|
733
|
+
} else {
|
|
734
|
+
lines.push(chalk3.green(" Errors: 0"));
|
|
735
|
+
lines.push("");
|
|
736
|
+
lines.push(chalk3.green.bold("docs-sync-check passed."));
|
|
699
737
|
}
|
|
738
|
+
return lines.join("\n");
|
|
700
739
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
740
|
+
|
|
741
|
+
// src/commands/scaffold.ts
|
|
742
|
+
import path6 from "path";
|
|
743
|
+
import fs3 from "fs";
|
|
744
|
+
var MODULE_TYPES = ["module", "feature", "command", "model", "query"];
|
|
745
|
+
var APP_TYPES = [
|
|
746
|
+
"requirements",
|
|
747
|
+
"actors",
|
|
748
|
+
"business-flow",
|
|
749
|
+
"story",
|
|
750
|
+
"screen",
|
|
751
|
+
"resolver"
|
|
752
|
+
];
|
|
753
|
+
var ALL_TYPES = [...MODULE_TYPES, ...APP_TYPES];
|
|
754
|
+
var MODULE_DIR_MAP = {
|
|
755
|
+
feature: "docs/features",
|
|
756
|
+
command: "docs/commands",
|
|
757
|
+
model: "docs/models",
|
|
758
|
+
query: "docs/queries"
|
|
759
|
+
};
|
|
760
|
+
var APP_DIR_MAP = {
|
|
761
|
+
actors: "docs/actors",
|
|
762
|
+
"business-flow": "docs/business-flow",
|
|
763
|
+
screen: "docs/screen",
|
|
764
|
+
resolver: "docs/resolver"
|
|
765
|
+
};
|
|
766
|
+
function resolveScaffoldPath(type, parentName, name, root) {
|
|
767
|
+
if (type === "module" || type === "requirements") {
|
|
768
|
+
return path6.join(root, parentName, "README.md");
|
|
769
|
+
}
|
|
770
|
+
if (!name) {
|
|
771
|
+
throw new Error(`Name is required for scaffold type "${type}"`);
|
|
772
|
+
}
|
|
773
|
+
if (type === "business-flow") {
|
|
774
|
+
return path6.join(root, parentName, "docs/business-flow", name, "README.md");
|
|
775
|
+
}
|
|
776
|
+
if (type === "story") {
|
|
777
|
+
const parts = name.split("/");
|
|
778
|
+
if (parts.length !== 2) {
|
|
779
|
+
throw new Error(
|
|
780
|
+
`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`
|
|
781
|
+
);
|
|
708
782
|
}
|
|
783
|
+
return path6.join(root, parentName, "docs/business-flow", parts[0], "story", `${parts[1]}.md`);
|
|
709
784
|
}
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
function resolveScenarioDir(arg3) {
|
|
713
|
-
const abs = resolve3(arg3);
|
|
714
|
-
if (basename(abs) === "mock.json") return dirname2(abs);
|
|
715
|
-
return abs;
|
|
716
|
-
}
|
|
717
|
-
async function validateScenario(ctx, scenarioDir, mocksDir) {
|
|
718
|
-
const label = relative2(mocksDir, scenarioDir);
|
|
719
|
-
console.log(chalk3.bold(`
|
|
720
|
-
\u2500\u2500 ${label} \u2500\u2500`));
|
|
721
|
-
let entries;
|
|
722
|
-
try {
|
|
723
|
-
entries = await readdir(scenarioDir);
|
|
724
|
-
} catch {
|
|
725
|
-
fail(ctx, `${label}: directory not found`);
|
|
726
|
-
return;
|
|
785
|
+
if (MODULE_DIR_MAP[type]) {
|
|
786
|
+
return path6.join(root, parentName, MODULE_DIR_MAP[type], `${name}.md`);
|
|
727
787
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const mockPath = join2(scenarioDir, "mock.json");
|
|
731
|
-
let data;
|
|
732
|
-
try {
|
|
733
|
-
data = JSON.parse(await readFile(mockPath, "utf-8"));
|
|
734
|
-
} catch (err) {
|
|
735
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
736
|
-
fail(ctx, `${label}: invalid JSON \u2014 ${errMsg}`);
|
|
737
|
-
return;
|
|
788
|
+
if (APP_DIR_MAP[type]) {
|
|
789
|
+
return path6.join(root, parentName, APP_DIR_MAP[type], `${name}.md`);
|
|
738
790
|
}
|
|
739
|
-
|
|
740
|
-
checkResponseQuality(ctx, data, label);
|
|
741
|
-
checkDatabucketRefs(ctx, data, label);
|
|
791
|
+
throw new Error(`Unknown scaffold type: ${type}`);
|
|
742
792
|
}
|
|
743
|
-
async function
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
fail(ctx, "No scenarios found under mocks/");
|
|
793
|
+
async function runScaffold(type, parentName, name, root, cwd4) {
|
|
794
|
+
const outputPath = resolveScaffoldPath(type, parentName, name, root);
|
|
795
|
+
const absoluteOutput = path6.resolve(cwd4, outputPath);
|
|
796
|
+
if (fs3.existsSync(absoluteOutput)) {
|
|
797
|
+
console.error(`File already exists: ${outputPath}`);
|
|
749
798
|
return 1;
|
|
750
799
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
800
|
+
const schemaPath = ALL_SCHEMAS[type];
|
|
801
|
+
if (!schemaPath) {
|
|
802
|
+
console.error(`No schema found for type: ${type}`);
|
|
803
|
+
return 2;
|
|
754
804
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
805
|
+
try {
|
|
806
|
+
fs3.mkdirSync(path6.dirname(absoluteOutput), { recursive: true });
|
|
807
|
+
} catch (err) {
|
|
808
|
+
console.error(
|
|
809
|
+
`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`
|
|
810
|
+
);
|
|
811
|
+
return 1;
|
|
760
812
|
}
|
|
761
|
-
|
|
813
|
+
const { exitCode, stdout, stderr } = await runMdschema(
|
|
814
|
+
["generate", "--schema", schemaPath, "--output", absoluteOutput],
|
|
815
|
+
cwd4
|
|
816
|
+
);
|
|
817
|
+
if (stdout.trim()) console.log(stdout);
|
|
818
|
+
if (stderr.trim()) console.error(stderr);
|
|
819
|
+
return exitCode;
|
|
762
820
|
}
|
|
763
821
|
|
|
764
|
-
// src/commands/
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
822
|
+
// src/commands/module/list.ts
|
|
823
|
+
import { readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
824
|
+
import { join as join3 } from "path";
|
|
825
|
+
import chalk4 from "chalk";
|
|
826
|
+
var MODULES_DIR = join3(PACKAGE_ROOT, "src", "modules");
|
|
827
|
+
var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["shared", "testing"]);
|
|
828
|
+
function countFiles(dir, pattern, exclusions) {
|
|
829
|
+
if (!existsSync2(dir)) return 0;
|
|
830
|
+
return readdirSync2(dir).filter((f) => pattern.test(f) && !exclusions.some((p) => p.test(f))).length;
|
|
831
|
+
}
|
|
832
|
+
function listModules() {
|
|
833
|
+
if (!existsSync2(MODULES_DIR)) return [];
|
|
834
|
+
return readdirSync2(MODULES_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && !EXCLUDED_DIRS.has(d.name)).map((d) => {
|
|
835
|
+
const modDir = join3(MODULES_DIR, d.name);
|
|
836
|
+
return {
|
|
837
|
+
name: d.name,
|
|
838
|
+
commands: countFiles(join3(modDir, "command"), /\.ts$/, [/\.test\.ts$/]),
|
|
839
|
+
models: countFiles(join3(modDir, "db"), /\.ts$/, [/\.test\.ts$/, /^index\.ts$/]),
|
|
840
|
+
features: countFiles(join3(modDir, "docs", "features"), /\.md$/, [])
|
|
841
|
+
};
|
|
842
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
843
|
+
}
|
|
844
|
+
function formatModuleList(modules) {
|
|
845
|
+
if (modules.length === 0) return "No modules found.";
|
|
846
|
+
const lines = [];
|
|
847
|
+
lines.push(chalk4.bold("Modules:\n"));
|
|
848
|
+
const nameWidth = Math.max(...modules.map((m) => m.name.length), 4);
|
|
849
|
+
for (const mod of modules) {
|
|
850
|
+
const counts = [
|
|
851
|
+
`${mod.commands} commands`,
|
|
852
|
+
`${mod.models} models`,
|
|
853
|
+
`${mod.features} features`
|
|
854
|
+
].join(", ");
|
|
855
|
+
lines.push(` ${mod.name.padEnd(nameWidth)} ${counts}`);
|
|
856
|
+
}
|
|
857
|
+
lines.push("");
|
|
858
|
+
lines.push(`${modules.length} modules`);
|
|
859
|
+
return lines.join("\n");
|
|
860
|
+
}
|
|
861
|
+
function runModuleList() {
|
|
862
|
+
const modules = listModules();
|
|
863
|
+
console.log(formatModuleList(modules));
|
|
864
|
+
return 0;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/commands/module/index.ts
|
|
868
|
+
var cwd = process.cwd();
|
|
869
|
+
var rootArgs = z2.object({
|
|
870
|
+
root: arg2(z2.string(), {
|
|
871
|
+
alias: "r",
|
|
872
|
+
description: "Path to modules directory"
|
|
873
|
+
})
|
|
874
|
+
});
|
|
875
|
+
var listCommand = defineCommand2({
|
|
876
|
+
name: "list",
|
|
877
|
+
description: "List available modules",
|
|
878
|
+
run: () => {
|
|
879
|
+
const exitCode = runModuleList();
|
|
880
|
+
process.exit(exitCode);
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
var checkCommand = defineCommand2({
|
|
884
|
+
name: "check",
|
|
885
|
+
description: "Validate module docs against schemas",
|
|
886
|
+
args: rootArgs,
|
|
781
887
|
run: async (args) => {
|
|
782
|
-
const exitCode = await
|
|
888
|
+
const exitCode = await runCheck({ modulesRoot: args.root }, cwd);
|
|
783
889
|
process.exit(exitCode);
|
|
784
890
|
}
|
|
785
891
|
});
|
|
786
|
-
var
|
|
787
|
-
name: "
|
|
788
|
-
description: "Validate
|
|
789
|
-
args:
|
|
790
|
-
|
|
791
|
-
|
|
892
|
+
var syncCheckCommand = defineCommand2({
|
|
893
|
+
name: "sync-check",
|
|
894
|
+
description: "Validate source <-> doc correspondence",
|
|
895
|
+
args: rootArgs,
|
|
896
|
+
run: async (args) => {
|
|
897
|
+
const result = await runSyncCheck({ modulesRoot: args.root }, cwd);
|
|
898
|
+
console.log(formatSyncCheckReport(result));
|
|
899
|
+
process.exit(result.exitCode);
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
var scaffoldCommand = defineCommand2({
|
|
903
|
+
name: "scaffold",
|
|
904
|
+
description: "Generate module doc from schema template",
|
|
905
|
+
args: rootArgs.extend({
|
|
906
|
+
type: arg2(z2.enum(MODULE_TYPES), {
|
|
907
|
+
positional: true,
|
|
908
|
+
description: `Scaffold type (${MODULE_TYPES.join(", ")})`
|
|
792
909
|
}),
|
|
793
|
-
|
|
910
|
+
parent: arg2(z2.string(), {
|
|
794
911
|
positional: true,
|
|
795
|
-
description: "
|
|
912
|
+
description: "Module name"
|
|
913
|
+
}),
|
|
914
|
+
name: arg2(z2.string().optional(), {
|
|
915
|
+
positional: true,
|
|
916
|
+
description: "Item name (required for feature, command, model)"
|
|
796
917
|
})
|
|
797
918
|
}),
|
|
798
919
|
run: async (args) => {
|
|
799
|
-
const exitCode = await
|
|
920
|
+
const exitCode = await runScaffold(
|
|
921
|
+
args.type,
|
|
922
|
+
args.parent,
|
|
923
|
+
args.name,
|
|
924
|
+
args.root,
|
|
925
|
+
cwd
|
|
926
|
+
);
|
|
800
927
|
process.exit(exitCode);
|
|
801
928
|
}
|
|
802
929
|
});
|
|
803
|
-
var
|
|
804
|
-
name: "
|
|
805
|
-
description: "
|
|
930
|
+
var moduleCommand = defineCommand2({
|
|
931
|
+
name: "module",
|
|
932
|
+
description: "Module management",
|
|
806
933
|
subCommands: {
|
|
807
|
-
|
|
808
|
-
|
|
934
|
+
list: listCommand,
|
|
935
|
+
check: checkCommand,
|
|
936
|
+
"sync-check": syncCheckCommand,
|
|
937
|
+
scaffold: scaffoldCommand
|
|
809
938
|
}
|
|
810
939
|
});
|
|
811
940
|
|
|
812
|
-
// src/
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
alias: "a",
|
|
821
|
-
description: "Path to app-compose directory (apps/ or examples/)"
|
|
941
|
+
// src/commands/app/index.ts
|
|
942
|
+
import { z as z3 } from "zod";
|
|
943
|
+
import { defineCommand as defineCommand3, arg as arg3 } from "politty";
|
|
944
|
+
var cwd2 = process.cwd();
|
|
945
|
+
var rootArgs2 = z3.object({
|
|
946
|
+
root: arg3(z3.string(), {
|
|
947
|
+
alias: "r",
|
|
948
|
+
description: "Path to app-compose directory"
|
|
822
949
|
})
|
|
823
950
|
});
|
|
824
|
-
|
|
825
|
-
const paths = { modulesRoot: args.modulesRoot, appRoot: args.appRoot };
|
|
826
|
-
if (!paths.modulesRoot && !paths.appRoot) {
|
|
827
|
-
console.error("At least one of --modules-root or --app-root is required.");
|
|
828
|
-
process.exit(2);
|
|
829
|
-
}
|
|
830
|
-
return paths;
|
|
831
|
-
}
|
|
832
|
-
var checkCommand = defineCommand2({
|
|
951
|
+
var checkCommand2 = defineCommand3({
|
|
833
952
|
name: "check",
|
|
834
|
-
description: "Validate docs against schemas",
|
|
835
|
-
args:
|
|
953
|
+
description: "Validate app docs against schemas",
|
|
954
|
+
args: rootArgs2,
|
|
836
955
|
run: async (args) => {
|
|
837
|
-
const
|
|
838
|
-
const exitCode = await runCheck(paths, cwd);
|
|
956
|
+
const exitCode = await runCheck({ appRoot: args.root }, cwd2);
|
|
839
957
|
process.exit(exitCode);
|
|
840
958
|
}
|
|
841
959
|
});
|
|
842
|
-
var
|
|
960
|
+
var syncCheckCommand2 = defineCommand3({
|
|
843
961
|
name: "sync-check",
|
|
844
962
|
description: "Validate source <-> doc correspondence",
|
|
845
|
-
args:
|
|
963
|
+
args: rootArgs2,
|
|
846
964
|
run: async (args) => {
|
|
847
|
-
const
|
|
848
|
-
const result = await runSyncCheck(paths, cwd);
|
|
965
|
+
const result = await runSyncCheck({ appRoot: args.root }, cwd2);
|
|
849
966
|
console.log(formatSyncCheckReport(result));
|
|
850
967
|
process.exit(result.exitCode);
|
|
851
968
|
}
|
|
852
969
|
});
|
|
853
|
-
var
|
|
970
|
+
var scaffoldCommand2 = defineCommand3({
|
|
854
971
|
name: "scaffold",
|
|
855
|
-
description: "Generate doc
|
|
856
|
-
args:
|
|
857
|
-
type:
|
|
972
|
+
description: "Generate app doc from schema template",
|
|
973
|
+
args: rootArgs2.extend({
|
|
974
|
+
type: arg3(z3.enum(APP_TYPES), {
|
|
858
975
|
positional: true,
|
|
859
|
-
description: `Scaffold type (${
|
|
976
|
+
description: `Scaffold type (${APP_TYPES.join(", ")})`
|
|
860
977
|
}),
|
|
861
|
-
parent:
|
|
978
|
+
parent: arg3(z3.string(), {
|
|
862
979
|
positional: true,
|
|
863
|
-
description: "
|
|
980
|
+
description: "App name"
|
|
864
981
|
}),
|
|
865
|
-
name:
|
|
982
|
+
name: arg3(z3.string().optional(), {
|
|
866
983
|
positional: true,
|
|
867
984
|
description: "Item name (required for most types)"
|
|
868
985
|
})
|
|
869
986
|
}),
|
|
870
987
|
run: async (args) => {
|
|
871
|
-
const paths = requireRoot(args);
|
|
872
|
-
const root = isModuleType(args.type) ? paths.modulesRoot : paths.appRoot;
|
|
873
|
-
if (!root) {
|
|
874
|
-
console.error(
|
|
875
|
-
`--${isModuleType(args.type) ? "modules-root" : "app-root"} is required for scaffold type "${args.type}".`
|
|
876
|
-
);
|
|
877
|
-
process.exit(2);
|
|
878
|
-
}
|
|
879
988
|
const exitCode = await runScaffold(
|
|
880
989
|
args.type,
|
|
881
990
|
args.parent,
|
|
882
991
|
args.name,
|
|
883
|
-
root,
|
|
884
|
-
|
|
992
|
+
args.root,
|
|
993
|
+
cwd2
|
|
885
994
|
);
|
|
886
995
|
process.exit(exitCode);
|
|
887
996
|
}
|
|
888
997
|
});
|
|
889
|
-
var
|
|
998
|
+
var appCommand = defineCommand3({
|
|
999
|
+
name: "app",
|
|
1000
|
+
description: "App-compose management",
|
|
1001
|
+
subCommands: {
|
|
1002
|
+
check: checkCommand2,
|
|
1003
|
+
"sync-check": syncCheckCommand2,
|
|
1004
|
+
scaffold: scaffoldCommand2
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
// src/cli.ts
|
|
1009
|
+
var cwd3 = process.cwd();
|
|
1010
|
+
var initCommand = defineCommand4({
|
|
890
1011
|
name: "init",
|
|
891
1012
|
description: "Set up consumer repo with framework skills",
|
|
892
|
-
args:
|
|
893
|
-
force:
|
|
1013
|
+
args: z4.object({
|
|
1014
|
+
force: arg4(z4.boolean().default(false), {
|
|
894
1015
|
alias: "f",
|
|
895
1016
|
description: "Overwrite existing skills"
|
|
896
1017
|
})
|
|
897
1018
|
}),
|
|
898
1019
|
run: (args) => {
|
|
899
|
-
const exitCode = runInit(
|
|
1020
|
+
const exitCode = runInit(cwd3, args.force);
|
|
900
1021
|
process.exit(exitCode);
|
|
901
1022
|
}
|
|
902
1023
|
});
|
|
903
|
-
var main =
|
|
1024
|
+
var main = defineCommand4({
|
|
904
1025
|
name: "erp-kit",
|
|
905
|
-
description: "
|
|
1026
|
+
description: "ERP module framework CLI",
|
|
906
1027
|
subCommands: {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
init: initCommand
|
|
911
|
-
mock: mockCommand
|
|
1028
|
+
module: moduleCommand,
|
|
1029
|
+
app: appCommand,
|
|
1030
|
+
mock: mockCommand,
|
|
1031
|
+
init: initCommand
|
|
912
1032
|
}
|
|
913
1033
|
});
|
|
914
1034
|
void runMain(main);
|