@shanyucoder/flowgrid 0.1.5 → 0.1.8
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/README.md +1 -1
- package/adapters/laravel/registries/codegen.registry.json +9 -9
- package/bin/flowgrid.mjs +326 -115
- package/bin/lib/agent-mcp.mjs +4 -0
- package/bin/lib/agent-profiles.mjs +30 -6
- package/bin/lib/audit-run.mjs +1 -1
- package/bin/lib/cli-update.mjs +48 -8
- package/bin/lib/doctor.mjs +90 -3
- package/bin/lib/harness-overlay.mjs +12 -5
- package/bin/lib/harness-sync.mjs +17 -3
- package/bin/lib/init-adapters.mjs +75 -0
- package/bin/lib/init-scaffold.mjs +9 -0
- package/bin/lib/inject-consumer-scripts.mjs +115 -0
- package/bin/lib/merge-stack-config.mjs +89 -0
- package/bin/lib/project-gitignore.mjs +1 -0
- package/bin/lib/repo-maps-align.mjs +203 -0
- package/dist/graph/config/load-config.js +7 -2
- package/dist/graph/config/load-config.js.map +1 -1
- package/dist/graph/mcp/tools.js +1 -1
- package/dist/graph/mcp/tools.js.map +1 -1
- package/dist/graph/registry/load-registries.d.ts +1 -0
- package/dist/graph/registry/load-registries.js +14 -1
- package/dist/graph/registry/load-registries.js.map +1 -1
- package/engines/cases/render-cases.mjs +67 -5
- package/engines/docs/lib/render-api-summary-markdown.mjs +28 -0
- package/engines/docs/lib/render-bundle-markdown.mjs +77 -2
- package/engines/docs/lib/render-data-model-markdown.mjs +171 -0
- package/engines/docs/lib/render-design-tables.mjs +89 -7
- package/engines/docs/lib/render-template.mjs +6 -0
- package/engines/docs/render-docs.mjs +6 -2
- package/engines/docs/vitepress/config.ts +7 -7
- package/engines/docs/vitepress/surfaces-nav.mjs +45 -14
- package/engines/openapi/check-backend-spec.mjs +2 -2
- package/engines/openapi/lib/markdown-table.mjs +8 -0
- package/engines/openapi/lib/render-backend-spec-markdown.mjs +78 -0
- package/engines/registry-sync/be-capabilities-sync.mjs +118 -0
- package/engines/registry-sync/fe-design-sync.mjs +258 -0
- package/engines/registry-sync/run-registry-sync.mjs +107 -0
- package/engines/shared/e2e-output-layout.mjs +68 -0
- package/engines/shared/flowgrid-e2e-root.mjs +19 -0
- package/engines/shared/resolve-flowgrid-context.mjs +176 -0
- package/engines/spec/lib/audit-api-gaps.mjs +1 -1
- package/engines/spec/lib/audit-bundle-gaps.mjs +92 -3
- package/engines/spec/lib/audit-db-tables.mjs +529 -0
- package/engines/spec/lib/audit-e2e-coverage.mjs +88 -30
- package/engines/spec/lib/bundle-schema.mjs +4 -1
- package/engines/spec/split-bundle.mjs +11 -1
- package/engines/testcase/runners/generate-api.mjs +23 -23
- package/engines/testcase/runners/generate.mjs +19 -17
- package/engines/testcase/runners/lib/bootstrap-context.mjs +44 -0
- package/engines/testcase/runners/lib/write-files.mjs +37 -9
- package/harness/agents/antigravity/rules/antigravity-mcp.mdc +12 -0
- package/harness/agents/gemini/rules/gemini-mcp.mdc +11 -0
- package/harness/agents/gemini_antigravity/rules/antigravity-mcp.mdc +8 -6
- package/harness/be/skills/{grill-api → audit-api}/SKILL.md +8 -7
- package/harness/common/extracts/artifact-graph.md +2 -2
- package/harness/common/extracts/artifactgraph-phase-hooks.md +2 -2
- package/harness/common/extracts/docs-mark-detect.md +2 -2
- package/harness/common/extracts/entity-relationship.md +23 -0
- package/harness/common/rules/flowgrid-ux-common.mdc +3 -3
- package/harness/common/skills/configure-repo-maps/SKILL.md +4 -2
- package/harness/docs/extracts/agent-execution-protocol.md +1 -1
- package/harness/docs/extracts/api-codegen-readiness.md +34 -0
- package/harness/docs/extracts/api-codegen-tags.md +30 -0
- package/harness/docs/extracts/api-contract.md +43 -0
- package/harness/docs/extracts/api-spec-sync.md +35 -0
- package/harness/docs/extracts/artifactgraph-hooks-docs.md +1 -1
- package/harness/docs/extracts/call-external.md +16 -0
- package/harness/docs/extracts/common-scope.md +9 -10
- package/harness/docs/extracts/db-audit-wizard.md +45 -0
- package/harness/docs/extracts/derived-data.md +18 -0
- package/harness/docs/extracts/design-leaf-signoff.md +16 -0
- package/harness/docs/extracts/extract-registry.docs.json +9 -1
- package/harness/docs/extracts/spec-core.md +7 -3
- package/harness/docs/extracts/spec-evolution.md +21 -0
- package/harness/docs/extracts/spec-prd-lite.md +19 -0
- package/harness/docs/extracts/spec-requirement.md +6 -2
- package/harness/docs/extracts/spec-ssot-prep.md +25 -0
- package/harness/docs/extracts/tpl-module.md +12 -0
- package/harness/docs/extracts/verify-gate.md +33 -0
- package/harness/docs/extracts/wire-spec-feedback.md +31 -0
- package/harness/docs/rules/agent-compliance.mdc +1 -1
- package/harness/docs/rules/team-flow-spec.mdc +3 -4
- package/harness/docs/skills/adopt/SKILL.md +2 -0
- package/harness/docs/skills/api/SKILL.md +4 -5
- package/harness/docs/skills/api-spec/SKILL.md +19 -6
- package/harness/docs/skills/api-update/SKILL.md +3 -3
- package/harness/docs/skills/architecture/SKILL.md +1 -1
- package/harness/docs/skills/business-process/SKILL.md +2 -0
- package/harness/docs/skills/common/SKILL.md +2 -2
- package/harness/docs/skills/common-spec/SKILL.md +10 -47
- package/harness/docs/skills/db-erd/SKILL.md +26 -0
- package/harness/docs/skills/grill/SKILL.md +28 -22
- package/harness/docs/skills/grill-api/SKILL.md +4 -6
- package/harness/docs/skills/grill-api-spec/SKILL.md +44 -24
- package/harness/docs/skills/grill-bqa/SKILL.md +28 -13
- package/harness/docs/skills/grill-common-spec/SKILL.md +10 -35
- package/harness/docs/skills/grill-dev/SKILL.md +7 -6
- package/harness/docs/skills/grill-docs/SKILL.md +10 -3
- package/harness/docs/skills/module/SKILL.md +3 -1
- package/harness/docs/skills/openapi/SKILL.md +2 -1
- package/harness/docs/skills/overview/SKILL.md +7 -1
- package/harness/docs/skills/spec/SKILL.md +42 -9
- package/harness/docs/skills/update-spec/SKILL.md +4 -1
- package/harness/fe/extracts/wire-audit-loop.md +72 -0
- package/harness/fe/extracts/wire-phase.md +45 -0
- package/harness/fe/rules/platform-design-vocabulary.mdc +1 -1
- package/harness/fe/rules/team-flow-prototype.mdc +8 -4
- package/harness/fe/skills/gen-common/SKILL.md +11 -84
- package/harness/fe/skills/grill-prototype/SKILL.md +51 -25
- package/harness/fe/skills/grill-test/SKILL.md +78 -20
- package/harness/fe/skills/grill-wire/SKILL.md +81 -0
- package/harness/fe/skills/prototype/SKILL.md +3 -2
- package/harness/fe/skills/wire/SKILL.md +7 -2
- package/harness/shared/AGENTS.md +3 -3
- package/harness/shared/SSOT_AGENT_PROTOCOL.md +3 -3
- package/harness/tests/extracts/grill-api-hook.md +69 -0
- package/harness/tests/extracts/grill-scenario-flow.md +39 -0
- package/harness/tests/extracts/grill-screen-tc.md +40 -0
- package/harness/tests/extracts/testcase-gen-cli.md +57 -0
- package/harness/tests/extracts/testcase-plan.md +29 -0
- package/harness/tests/extracts/tests-verify-gate.md +29 -0
- package/harness/tests/extracts/wire-test-handoff.md +37 -0
- package/harness/tests/skills/grill-testcase/SKILL.md +1 -0
- package/harness/tests/skills/test-api/SKILL.md +14 -6
- package/harness/tests/skills/testcase/SKILL.md +3 -1
- package/harness/tests/templates/TC.example-api.yaml +7 -1
- package/harness/tests/templates/TC.example.yaml +4 -1
- package/harness/tests/templates/tpl-testcase-plan.md +75 -0
- package/package.json +1 -1
- package/stacks/fastapi.json +1 -0
- package/stacks/laravel.json +1 -0
- package/stacks/nestjs.json +72 -0
- package/stacks/nextjs-nest.json +1 -0
- package/stacks/nuxt4-nest.json +1 -0
- package/templates/project-skeleton/architecture/03-business-process/FLOW-template.md +2 -0
- package/templates/project-skeleton/overview/index.md +68 -2
- package/templates/project-skeleton/overview/operational-areas/_template.md +37 -0
- package/templates/project-skeleton/surfaces/common/data-model/index.md +10 -2
- package/templates/shared/api-03-mock.stub.yaml +14 -0
- package/templates/shared/backend-api.bundle.yaml +3 -0
- package/templates/shared/backend-api.yaml +4 -0
- package/templates/shared/be-capabilities.registry.base.json +8 -0
- package/templates/shared/bundle-authoring.md +39 -3
- package/templates/shared/default-layout.ejs +131 -18
- package/templates/shared/design-spec.yaml +2 -3
- package/templates/shared/design.registry.base.json +38 -0
- package/templates/shared/feature.bundle.yaml +16 -9
- package/templates/shared/ir/generated/spec.md +281 -0
- package/templates/shared/qa-item.yaml +1 -1
- package/templates/shared/tpl-api-contract.md +133 -0
- package/templates/shared/tpl-screen-data-model.md +76 -0
- package/templates/tests-skeleton/cases/README.md +4 -0
- package/templates/tests-skeleton/catalog/locale.yaml +9 -0
- package/templates/tests-skeleton/tpl-testcase-plan.md +9 -0
- package/harness/docs/skills/api-integration/SKILL.md +0 -110
- package/harness/docs/skills/grill-integration-spec/SKILL.md +0 -51
package/bin/flowgrid.mjs
CHANGED
|
@@ -19,7 +19,21 @@ import {
|
|
|
19
19
|
applyDocsHubScaffold,
|
|
20
20
|
applyTestsHubScaffold,
|
|
21
21
|
promptHubRoot,
|
|
22
|
+
hubPathUnderProject,
|
|
22
23
|
} from './lib/init-scaffold.mjs';
|
|
24
|
+
import { promptFeAdapter, promptBeAdapter } from './lib/init-adapters.mjs';
|
|
25
|
+
import {
|
|
26
|
+
loadStackPresetJson,
|
|
27
|
+
mergeStackIntoFlowgridConfig,
|
|
28
|
+
resolveStackPresetKey,
|
|
29
|
+
} from './lib/merge-stack-config.mjs';
|
|
30
|
+
import { defaultE2eRootRel, DEFAULT_E2E_ROOT_REL } from '../engines/shared/flowgrid-e2e-root.mjs';
|
|
31
|
+
import {
|
|
32
|
+
resolveFlowgridContext,
|
|
33
|
+
flowgridContextToEnv,
|
|
34
|
+
ENV_DOCS_ROOT,
|
|
35
|
+
} from '../engines/shared/resolve-flowgrid-context.mjs';
|
|
36
|
+
import { injectFlowgridConsumerScripts } from './lib/inject-consumer-scripts.mjs';
|
|
23
37
|
|
|
24
38
|
function packageRootFromMeta(metaUrl) {
|
|
25
39
|
return path.resolve(path.dirname(fileURLToPath(metaUrl)), '..');
|
|
@@ -67,6 +81,23 @@ export async function main() {
|
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
const command = args[0];
|
|
84
|
+
|
|
85
|
+
if (command === 'api:check') {
|
|
86
|
+
try {
|
|
87
|
+
const { checkBackendSpecFiles, parseApiCheckArgs } = await import(
|
|
88
|
+
'../engines/openapi/check-backend-spec.mjs'
|
|
89
|
+
);
|
|
90
|
+
const ctx = resolveFlowgridContext({ argv: args.slice(1) });
|
|
91
|
+
const checkArgv = parseApiCheckArgs(ctx.argv);
|
|
92
|
+
const cwd = ctx.docsRoot ?? ctx.projectRoot;
|
|
93
|
+
await checkBackendSpecFiles(checkArgv, cwd);
|
|
94
|
+
process.exit(0);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error(pc.red(e.message ?? String(e)));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
70
101
|
const engineCommands = ['split', 'merge', 'check', 'split_all', 'normalize', 'render', 'publish', 'legacy_validate', 'extract_i18n', 'openapi_render', 'openapi_build_ui', 'openapi_gen'];
|
|
71
102
|
|
|
72
103
|
if (engineCommands.includes(command)) {
|
|
@@ -86,11 +117,14 @@ export async function main() {
|
|
|
86
117
|
if (testCommands.includes(command)) {
|
|
87
118
|
try {
|
|
88
119
|
const { runEngine } = await import('../dist/test/engines/run.js');
|
|
89
|
-
const {
|
|
90
|
-
|
|
120
|
+
const { flowgridContextToEnv, resolveFlowgridContext } = await import(
|
|
121
|
+
'../engines/shared/resolve-flowgrid-context.mjs'
|
|
122
|
+
);
|
|
123
|
+
const ctx = resolveFlowgridContext({ argv: args.slice(1) });
|
|
124
|
+
const root = ctx.projectRoot;
|
|
91
125
|
let engineRel = [];
|
|
92
|
-
const
|
|
93
|
-
const engineEnv =
|
|
126
|
+
const argv = ctx.argv;
|
|
127
|
+
const engineEnv = flowgridContextToEnv(ctx);
|
|
94
128
|
|
|
95
129
|
if (command === 'cases:render') engineRel = ['cases', 'render-cases.mjs'];
|
|
96
130
|
if (command === 'cases:check') engineRel = ['cases', 'check-plans.mjs'];
|
|
@@ -118,6 +152,17 @@ export async function main() {
|
|
|
118
152
|
}
|
|
119
153
|
}
|
|
120
154
|
|
|
155
|
+
if (command === 'registry:sync') {
|
|
156
|
+
try {
|
|
157
|
+
const { runRegistrySync } = await import('../engines/registry-sync/run-registry-sync.mjs');
|
|
158
|
+
const res = await runRegistrySync(args.slice(1));
|
|
159
|
+
process.exit(res.code ?? 0);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.error(pc.red('registry:sync failed: ' + (e.message ?? String(e))));
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
121
166
|
const codegenCommands = [
|
|
122
167
|
'api-gen', 'api-gen:dry', 'api-registry', 'api-unit-gen', 'api-unit-gen:dry', 'api-unit-registry',
|
|
123
168
|
'gen', 'gen:dry', 'registry', 'unit-gen', 'unit-gen:dry', 'unit-registry',
|
|
@@ -127,18 +172,19 @@ export async function main() {
|
|
|
127
172
|
|
|
128
173
|
if (codegenCommands.includes(command)) {
|
|
129
174
|
try {
|
|
130
|
-
const configPath = path.join(process.cwd(), '.flowgrid', 'config.json');
|
|
131
|
-
let config = {};
|
|
132
|
-
if (fs.existsSync(configPath)) {
|
|
133
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
134
|
-
}
|
|
135
175
|
const root = process.cwd();
|
|
136
|
-
const
|
|
176
|
+
const ctx = resolveFlowgridContext({ cwd: root, argv: args.slice(1) });
|
|
177
|
+
const config = ctx.config ?? {};
|
|
178
|
+
const argv = ctx.argv;
|
|
137
179
|
const isDry = command.endsWith(':dry');
|
|
180
|
+
const docsRoot = ctx.docsRoot;
|
|
181
|
+
if (docsRoot && !process.env[ENV_DOCS_ROOT]) {
|
|
182
|
+
process.env[ENV_DOCS_ROOT] = docsRoot;
|
|
183
|
+
}
|
|
184
|
+
Object.assign(process.env, flowgridContextToEnv(ctx, {}));
|
|
138
185
|
|
|
139
186
|
const beAdapter = config.backend?.adapter || 'fastapi';
|
|
140
187
|
const feAdapter = config.frontend?.adapter || 'nextjs';
|
|
141
|
-
const docsRoot = config.frontend?.docsRoot;
|
|
142
188
|
|
|
143
189
|
if (command.startsWith('api-')) {
|
|
144
190
|
const { runBeEngine } = await import('../dist/codegen/adapters/run-be.js');
|
|
@@ -196,22 +242,19 @@ export async function main() {
|
|
|
196
242
|
|
|
197
243
|
if (command === 'dev' || command === 'serve') {
|
|
198
244
|
const { spawn, spawnSync } = await import('node:child_process');
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
testsRoot = config.frontend?.testsRoot ?? config.backend?.testsRoot;
|
|
205
|
-
feAdapter = config.frontend?.adapter;
|
|
206
|
-
}
|
|
245
|
+
const ctx = resolveFlowgridContext({ argv: args.slice(1) });
|
|
246
|
+
const config = ctx.config ?? {};
|
|
247
|
+
const docsRoot = ctx.docsRoot;
|
|
248
|
+
const testsRoot = ctx.testsDocs;
|
|
249
|
+
let feAdapter = config.frontend?.adapter;
|
|
207
250
|
feAdapter = feAdapter || 'nextjs';
|
|
208
|
-
const projectRoot =
|
|
251
|
+
const projectRoot = ctx.projectRoot;
|
|
209
252
|
|
|
210
253
|
// ── gen-common: initial build (watch is handled by VitePress plugin) ──
|
|
211
254
|
const webAdapters = new Set(['nuxt4', 'nextjs']);
|
|
212
255
|
if (docsRoot && webAdapters.has(feAdapter)) {
|
|
213
256
|
const resolvedDocsRoot = path.resolve(docsRoot);
|
|
214
|
-
const packageRoot =
|
|
257
|
+
const packageRoot = packageRootFromMeta(import.meta.url);
|
|
215
258
|
const commonEngine = path.join(packageRoot, 'adapters', 'shared', 'common-gen.mjs');
|
|
216
259
|
const env = {
|
|
217
260
|
...process.env,
|
|
@@ -236,14 +279,14 @@ export async function main() {
|
|
|
236
279
|
// ── VitePress dev servers ────────────────────────────────────────
|
|
237
280
|
if (docsRoot) {
|
|
238
281
|
console.log(pc.blue(`[Docs] Starting VitePress in ${docsRoot}...`));
|
|
239
|
-
spawn('npx', ['vitepress', 'dev', docsRoot, ...
|
|
282
|
+
spawn('npx', ['vitepress', 'dev', docsRoot, ...ctx.argv], { stdio: 'inherit' });
|
|
240
283
|
}
|
|
241
284
|
if (testsRoot) {
|
|
242
285
|
console.log(pc.blue(`[Tests] Starting VitePress in ${testsRoot} (Port 5174)...`));
|
|
243
|
-
spawn('npx', ['vitepress', 'dev', testsRoot, '--port', '5174', ...
|
|
286
|
+
spawn('npx', ['vitepress', 'dev', testsRoot, '--port', '5174', ...ctx.argv], { stdio: 'inherit' });
|
|
244
287
|
}
|
|
245
288
|
if (!docsRoot && !testsRoot) {
|
|
246
|
-
spawn('npx', ['vitepress', 'dev', ...
|
|
289
|
+
spawn('npx', ['vitepress', 'dev', ...ctx.argv], { stdio: 'inherit' });
|
|
247
290
|
}
|
|
248
291
|
// Keep alive for spawned processes
|
|
249
292
|
setInterval(() => {}, 1000);
|
|
@@ -252,27 +295,97 @@ export async function main() {
|
|
|
252
295
|
|
|
253
296
|
if (command === 'build') {
|
|
254
297
|
const { spawnSync } = await import('node:child_process');
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
259
|
-
docsRoot = config.frontend?.docsRoot || config.backend?.docsRoot;
|
|
260
|
-
testsRoot = config.frontend?.testsRoot ?? config.backend?.testsRoot;
|
|
261
|
-
}
|
|
298
|
+
const ctx = resolveFlowgridContext({ argv: args.slice(1) });
|
|
299
|
+
const docsRoot = ctx.docsRoot;
|
|
300
|
+
const testsRoot = ctx.testsDocs;
|
|
262
301
|
if (docsRoot) {
|
|
263
302
|
console.log(pc.blue(`[Docs] Building VitePress in ${docsRoot}...`));
|
|
264
|
-
spawnSync('npx', ['vitepress', 'build', docsRoot, ...
|
|
303
|
+
spawnSync('npx', ['vitepress', 'build', docsRoot, ...ctx.argv], { stdio: 'inherit' });
|
|
265
304
|
}
|
|
266
305
|
if (testsRoot) {
|
|
267
306
|
console.log(pc.blue(`[Tests] Building VitePress in ${testsRoot}...`));
|
|
268
|
-
spawnSync('npx', ['vitepress', 'build', testsRoot, ...
|
|
307
|
+
spawnSync('npx', ['vitepress', 'build', testsRoot, ...ctx.argv], { stdio: 'inherit' });
|
|
269
308
|
}
|
|
270
309
|
if (!docsRoot && !testsRoot) {
|
|
271
|
-
spawnSync('npx', ['vitepress', 'build', ...
|
|
310
|
+
spawnSync('npx', ['vitepress', 'build', ...ctx.argv], { stdio: 'inherit' });
|
|
272
311
|
}
|
|
273
312
|
return;
|
|
274
313
|
}
|
|
275
314
|
|
|
315
|
+
if (command === 'repo-maps') {
|
|
316
|
+
const sub = args[1] || 'check';
|
|
317
|
+
const {
|
|
318
|
+
compareConfigAndRepoMaps,
|
|
319
|
+
writeRepoMapsFromConfig,
|
|
320
|
+
writeConfigFromRepoMaps,
|
|
321
|
+
} = await import('./lib/repo-maps-align.mjs');
|
|
322
|
+
const projectRoot = process.cwd();
|
|
323
|
+
const cmp = compareConfigAndRepoMaps(projectRoot);
|
|
324
|
+
|
|
325
|
+
if (sub === 'check') {
|
|
326
|
+
if (cmp.conflicts.length) {
|
|
327
|
+
for (const c of cmp.conflicts) {
|
|
328
|
+
console.log(pc.yellow(`conflict ${c.key}: config=${c.configPath} map=${c.mapPath}`));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (cmp.missingInMap.length) {
|
|
332
|
+
console.log(pc.gray(`missing in ${'platform-repos.local.json'}: ${cmp.missingInMap.join(', ')}`));
|
|
333
|
+
}
|
|
334
|
+
if (!cmp.conflicts.length && !cmp.missingInMap.length) {
|
|
335
|
+
console.log(pc.green('repo-maps: aligned with .flowgrid/config.json'));
|
|
336
|
+
} else {
|
|
337
|
+
console.log(pc.cyan('Fix: flowgrid repo-maps align (or repo-maps sync --from-config)'));
|
|
338
|
+
}
|
|
339
|
+
process.exit(cmp.conflicts.length ? 1 : 0);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (sub === 'sync') {
|
|
343
|
+
const from = args.includes('--from-map') ? 'map' : 'config';
|
|
344
|
+
if (from === 'config') {
|
|
345
|
+
const { file } = writeRepoMapsFromConfig(projectRoot);
|
|
346
|
+
console.log(pc.green(`Updated ${path.relative(projectRoot, file)} from config`));
|
|
347
|
+
} else {
|
|
348
|
+
writeConfigFromRepoMaps(projectRoot);
|
|
349
|
+
console.log(pc.green('Updated .flowgrid/config.json from platform-repos.local.json'));
|
|
350
|
+
}
|
|
351
|
+
process.exit(0);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (sub === 'align') {
|
|
355
|
+
if (!cmp.conflicts.length) {
|
|
356
|
+
if (cmp.missingInMap.length) {
|
|
357
|
+
const { file } = writeRepoMapsFromConfig(projectRoot);
|
|
358
|
+
console.log(pc.green(`Seeded ${path.relative(projectRoot, file)} from init config`));
|
|
359
|
+
} else {
|
|
360
|
+
console.log(pc.green('Already aligned — nothing to do'));
|
|
361
|
+
}
|
|
362
|
+
process.exit(0);
|
|
363
|
+
}
|
|
364
|
+
const { applyAlignChoices } = await import('./lib/repo-maps-align.mjs');
|
|
365
|
+
const choices = {};
|
|
366
|
+
for (const c of cmp.conflicts) {
|
|
367
|
+
const choice = await select({
|
|
368
|
+
message: `Chốt path cho hub "${c.key}" (config lúc init vs platform-repos.local):`,
|
|
369
|
+
options: [
|
|
370
|
+
{ value: 'config', label: `Giữ config → ${c.configPath}` },
|
|
371
|
+
{ value: 'map', label: `Giữ repo map → ${c.mapPath}` },
|
|
372
|
+
],
|
|
373
|
+
});
|
|
374
|
+
if (isCancel(choice)) {
|
|
375
|
+
cancel('Cancelled.');
|
|
376
|
+
process.exit(0);
|
|
377
|
+
}
|
|
378
|
+
choices[c.key] = choice;
|
|
379
|
+
}
|
|
380
|
+
applyAlignChoices(projectRoot, choices);
|
|
381
|
+
console.log(pc.green('Aligned config + platform-repos.local.json — run flowgrid harness sync if MCP paths changed'));
|
|
382
|
+
process.exit(0);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log('Usage: flowgrid repo-maps check | align | sync [--from-config|--from-map]');
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
|
|
276
389
|
if (command === 'update') {
|
|
277
390
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
278
391
|
const { runCliUpdate } = await import('./lib/cli-update.mjs');
|
|
@@ -331,7 +444,18 @@ export async function main() {
|
|
|
331
444
|
}
|
|
332
445
|
|
|
333
446
|
if (!keepAgents) {
|
|
334
|
-
const dirs = [
|
|
447
|
+
const dirs = [
|
|
448
|
+
'.agents',
|
|
449
|
+
'.antigravity',
|
|
450
|
+
'.gemini',
|
|
451
|
+
'.cursor',
|
|
452
|
+
'.claude',
|
|
453
|
+
'.codex',
|
|
454
|
+
'.opencode',
|
|
455
|
+
'.hermes',
|
|
456
|
+
'.kiro',
|
|
457
|
+
'.kilo',
|
|
458
|
+
];
|
|
335
459
|
for (const d of dirs) {
|
|
336
460
|
const p = path.join(process.cwd(), d);
|
|
337
461
|
if (fs.existsSync(p)) {
|
|
@@ -389,7 +513,7 @@ export async function main() {
|
|
|
389
513
|
|
|
390
514
|
if (command === 'doctor') {
|
|
391
515
|
try {
|
|
392
|
-
const packageRoot =
|
|
516
|
+
const packageRoot = packageRootFromMeta(import.meta.url);
|
|
393
517
|
const fix = args.includes('--fix');
|
|
394
518
|
const report = runDoctor({
|
|
395
519
|
projectRoot: process.cwd(),
|
|
@@ -406,7 +530,7 @@ export async function main() {
|
|
|
406
530
|
|
|
407
531
|
if (command === 'harness' && args[1] === 'sync') {
|
|
408
532
|
try {
|
|
409
|
-
const packageRoot =
|
|
533
|
+
const packageRoot = packageRootFromMeta(import.meta.url);
|
|
410
534
|
runHarnessSync(args.slice(2), { packageRoot });
|
|
411
535
|
process.exit(0);
|
|
412
536
|
} catch (e) {
|
|
@@ -443,7 +567,8 @@ export async function main() {
|
|
|
443
567
|
const agentsAns = await multiselect({
|
|
444
568
|
message: 'Select Agents/Skills to integrate:',
|
|
445
569
|
options: [
|
|
446
|
-
{ value: '
|
|
570
|
+
{ value: 'gemini', label: 'Gemini (CLI / VS Code extension) → .agents/' },
|
|
571
|
+
{ value: 'antigravity', label: 'Google Antigravity IDE → .antigravity/' },
|
|
447
572
|
{ value: 'cursor', label: 'Cursor' },
|
|
448
573
|
{ value: 'claude', label: 'Claude Code' },
|
|
449
574
|
{ value: 'codex', label: 'Codex CLI' },
|
|
@@ -459,6 +584,19 @@ export async function main() {
|
|
|
459
584
|
process.exit(0);
|
|
460
585
|
}
|
|
461
586
|
const selectedAgents = agentsAns;
|
|
587
|
+
if (selectedAgents.includes('gemini')) {
|
|
588
|
+
console.log(pc.gray('Gemini: harness → .agents/ (mcp.json + .agents/skills/).\n'));
|
|
589
|
+
}
|
|
590
|
+
if (selectedAgents.includes('antigravity')) {
|
|
591
|
+
console.log(
|
|
592
|
+
pc.gray(
|
|
593
|
+
'Antigravity IDE: harness → .antigravity/ (mcp_config.json + .antigravity/skills/) — không dùng .agents/.\n',
|
|
594
|
+
),
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (selectedAgents.length === 0) {
|
|
598
|
+
console.log(pc.yellow('Không chọn agent — bỏ qua sync harness/MCP (chỉ .flowgrid + scaffold).\n'));
|
|
599
|
+
}
|
|
462
600
|
|
|
463
601
|
// 2. Project Type
|
|
464
602
|
const selectedType = await select({
|
|
@@ -485,6 +623,7 @@ export async function main() {
|
|
|
485
623
|
let testsHubInRepo = false;
|
|
486
624
|
let scaffoldDocs = false;
|
|
487
625
|
let scaffoldTests = false;
|
|
626
|
+
let feE2eRoot = '';
|
|
488
627
|
const projectRoot = process.cwd();
|
|
489
628
|
let baseProfile = 'standard';
|
|
490
629
|
let goldenSample = '';
|
|
@@ -521,22 +660,12 @@ export async function main() {
|
|
|
521
660
|
});
|
|
522
661
|
if (isCancel(feAns)) { cancel('Cancelled.'); process.exit(0); }
|
|
523
662
|
feAdapter = feAns;
|
|
524
|
-
beAdapter =
|
|
663
|
+
beAdapter = await promptBeAdapter({ select, text, isCancel, cancel });
|
|
525
664
|
console.log(pc.cyan(`=> Selected Fullstack: Frontend (${feAdapter}) + Backend (${beAdapter})`));
|
|
526
665
|
} else if (selectedType === 'Frontend') {
|
|
527
|
-
|
|
528
|
-
message: 'Enter Frontend technology (e.g. nuxt4, nextjs, or custom):',
|
|
529
|
-
placeholder: baseProfile === 'custom' ? 'custom' : 'nuxt4'
|
|
530
|
-
});
|
|
531
|
-
if (isCancel(feAns)) { cancel('Cancelled.'); process.exit(0); }
|
|
532
|
-
feAdapter = feAns || (baseProfile === 'custom' ? 'custom' : 'nuxt4');
|
|
666
|
+
feAdapter = await promptFeAdapter({ select, text, isCancel, cancel });
|
|
533
667
|
} else if (selectedType === 'Backend') {
|
|
534
|
-
|
|
535
|
-
message: 'Enter Backend technology (e.g. nestjs, fastapi, laravel, dotnet, or custom):',
|
|
536
|
-
placeholder: baseProfile === 'custom' ? 'custom' : 'nestjs'
|
|
537
|
-
});
|
|
538
|
-
if (isCancel(beAns)) { cancel('Cancelled.'); process.exit(0); }
|
|
539
|
-
beAdapter = beAns || (baseProfile === 'custom' ? 'custom' : 'nestjs');
|
|
668
|
+
beAdapter = await promptBeAdapter({ select, text, isCancel, cancel });
|
|
540
669
|
}
|
|
541
670
|
|
|
542
671
|
const needsDocsHub =
|
|
@@ -584,6 +713,34 @@ export async function main() {
|
|
|
584
713
|
scaffoldTests = testsHub.scaffold;
|
|
585
714
|
}
|
|
586
715
|
|
|
716
|
+
const needsE2eRoot =
|
|
717
|
+
selectedType === 'Frontend' ||
|
|
718
|
+
selectedType === 'Fullstack' ||
|
|
719
|
+
(selectedType === 'Backend' && feTestRoot);
|
|
720
|
+
if (needsE2eRoot) {
|
|
721
|
+
const e2eDefault = defaultE2eRootRel(selectedType);
|
|
722
|
+
const e2eAns = await text({
|
|
723
|
+
message:
|
|
724
|
+
selectedType === 'Backend'
|
|
725
|
+
? 'Automation root (Playwright api-e2e / request specs; tùy chọn Newman; khác tests-docs YAML):'
|
|
726
|
+
: 'Playwright e2e-root (chứa *.spec.ts / Page Object; khác tests-docs hub YAML):',
|
|
727
|
+
defaultValue: e2eDefault,
|
|
728
|
+
placeholder: e2eDefault,
|
|
729
|
+
});
|
|
730
|
+
if (isCancel(e2eAns)) {
|
|
731
|
+
cancel('Cancelled.');
|
|
732
|
+
process.exit(0);
|
|
733
|
+
}
|
|
734
|
+
feE2eRoot = (e2eAns || e2eDefault).trim() || e2eDefault;
|
|
735
|
+
console.log(
|
|
736
|
+
pc.gray(
|
|
737
|
+
selectedType === 'Backend'
|
|
738
|
+
? ` (tests-docs = TC-*.yaml; automation = tests/api-e2e + audit e2e — mặc định BE: ${e2eDefault})`
|
|
739
|
+
: ` (tests-docs = kế hoạch TC-*.yaml; e2e-root = code Playwright — mặc định ${e2eDefault})`,
|
|
740
|
+
),
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
|
|
587
744
|
if (selectedType === 'Test') {
|
|
588
745
|
feTestRoot = '.';
|
|
589
746
|
testsHubInRepo = true;
|
|
@@ -639,6 +796,13 @@ export async function main() {
|
|
|
639
796
|
if (beAdapter) {
|
|
640
797
|
console.log(`- Backend Adapter: ${beAdapter}`);
|
|
641
798
|
}
|
|
799
|
+
if (feE2eRoot) {
|
|
800
|
+
const e2eLabel =
|
|
801
|
+
selectedType === 'Backend'
|
|
802
|
+
? 'Automation root (Playwright api-e2e / Newman)'
|
|
803
|
+
: 'Playwright e2e-root';
|
|
804
|
+
console.log(`- ${e2eLabel}: ${feE2eRoot} (testcase:gen / testcase:gen:api / audit e2e)`);
|
|
805
|
+
}
|
|
642
806
|
}
|
|
643
807
|
console.log(`- Setup Agents: ${selectedAgents.length > 0 ? selectedAgents.join(', ') : 'No'}`);
|
|
644
808
|
console.log(`- Destination folder: .flowgrid/`);
|
|
@@ -658,6 +822,8 @@ export async function main() {
|
|
|
658
822
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
659
823
|
}
|
|
660
824
|
|
|
825
|
+
const packageRoot = packageRootFromMeta(import.meta.url);
|
|
826
|
+
|
|
661
827
|
const projectConfig = {
|
|
662
828
|
type: selectedType,
|
|
663
829
|
agents: selectedAgents,
|
|
@@ -669,6 +835,7 @@ export async function main() {
|
|
|
669
835
|
adapter: feAdapter,
|
|
670
836
|
docsRoot: feDocRoot || undefined,
|
|
671
837
|
testsRoot: feTestRoot || undefined,
|
|
838
|
+
e2eRoot: feE2eRoot || defaultE2eRootRel(selectedType),
|
|
672
839
|
docsHubInRepo: feDocRoot ? docsHubInRepo : undefined,
|
|
673
840
|
testsHubInRepo: feTestRoot ? testsHubInRepo : undefined,
|
|
674
841
|
} : null,
|
|
@@ -676,21 +843,50 @@ export async function main() {
|
|
|
676
843
|
adapter: beAdapter,
|
|
677
844
|
docsRoot: (!feAdapter && feDocRoot) ? feDocRoot : undefined,
|
|
678
845
|
testsRoot: (!feAdapter && feTestRoot) ? feTestRoot : undefined,
|
|
846
|
+
e2eRoot: (!feAdapter && feTestRoot)
|
|
847
|
+
? (feE2eRoot || defaultE2eRootRel('Backend'))
|
|
848
|
+
: undefined,
|
|
679
849
|
docsHubInRepo: (!feAdapter && feDocRoot) ? docsHubInRepo : undefined,
|
|
680
850
|
testsHubInRepo: (!feAdapter && feTestRoot) ? testsHubInRepo : undefined,
|
|
681
851
|
} : null
|
|
682
852
|
};
|
|
853
|
+
|
|
854
|
+
const stackKey = resolveStackPresetKey({
|
|
855
|
+
type: selectedType,
|
|
856
|
+
feAdapter,
|
|
857
|
+
beAdapter,
|
|
858
|
+
});
|
|
859
|
+
const stackPreset = loadStackPresetJson(packageRoot, stackKey);
|
|
860
|
+
mergeStackIntoFlowgridConfig(projectConfig, stackPreset, process.cwd());
|
|
861
|
+
if (stackPreset?.stack) {
|
|
862
|
+
console.log(pc.gray(` + Stack preset: ${stackKey} (registries/commands for ArtifactGraph MCP)`));
|
|
863
|
+
}
|
|
864
|
+
|
|
683
865
|
fs.writeFileSync(path.join(targetDir, 'config.json'), JSON.stringify(projectConfig, null, 2));
|
|
684
866
|
console.log(" + Wrote config.json");
|
|
685
867
|
|
|
686
|
-
|
|
687
|
-
|
|
868
|
+
if (feDocRoot || feTestRoot || selectedType === 'Document' || selectedType === 'Test') {
|
|
869
|
+
try {
|
|
870
|
+
const { writeRepoMapsFromConfig } = await import('./lib/repo-maps-align.mjs');
|
|
871
|
+
writeRepoMapsFromConfig(process.cwd());
|
|
872
|
+
console.log(pc.gray(' + Seeded platform-repos.local.json from hub pointers (config SSOT)'));
|
|
873
|
+
} catch {
|
|
874
|
+
/* non-fatal */
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
688
878
|
const copyRecursive = (srcDir, destDir, log = false) => {
|
|
689
879
|
if (!fs.existsSync(srcDir)) return;
|
|
690
880
|
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
691
881
|
const items = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
692
882
|
for (const item of items) {
|
|
693
|
-
if (
|
|
883
|
+
if (
|
|
884
|
+
['node_modules', '.git', 'dist', '.agents', '.antigravity', '.gemini', '.cursor', '.flowgrid'].includes(
|
|
885
|
+
item.name,
|
|
886
|
+
)
|
|
887
|
+
) {
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
694
890
|
if (item.name.endsWith('.db')) continue;
|
|
695
891
|
|
|
696
892
|
const srcPath = path.join(srcDir, item.name);
|
|
@@ -749,6 +945,23 @@ export async function main() {
|
|
|
749
945
|
});
|
|
750
946
|
}
|
|
751
947
|
|
|
948
|
+
if (feE2eRoot && hubPathUnderProject(process.cwd(), feE2eRoot)) {
|
|
949
|
+
const e2eAbs = path.join(process.cwd(), feE2eRoot);
|
|
950
|
+
if (!fs.existsSync(e2eAbs)) {
|
|
951
|
+
fs.mkdirSync(e2eAbs, { recursive: true });
|
|
952
|
+
fs.writeFileSync(path.join(e2eAbs, '.gitkeep'), '');
|
|
953
|
+
console.log(pc.blue(` + Automation / e2e-root scaffold → ${feE2eRoot}/`));
|
|
954
|
+
}
|
|
955
|
+
if (selectedType === 'Backend' && testsHubInRepo) {
|
|
956
|
+
const apiE2eAbs = path.join(process.cwd(), 'tests', 'api-e2e');
|
|
957
|
+
if (!fs.existsSync(apiE2eAbs)) {
|
|
958
|
+
fs.mkdirSync(apiE2eAbs, { recursive: true });
|
|
959
|
+
fs.writeFileSync(path.join(apiE2eAbs, '.gitkeep'), '');
|
|
960
|
+
console.log(pc.blue(` + API hook specs scaffold → tests/api-e2e/`));
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
752
965
|
console.log(pc.blue(`[INFO] Syncing global templates & schemas...`));
|
|
753
966
|
const tplSrc = path.join(packageRoot, 'templates');
|
|
754
967
|
const tplDest = path.join(targetDir, 'templates');
|
|
@@ -779,6 +992,19 @@ export async function main() {
|
|
|
779
992
|
console.log(
|
|
780
993
|
pc.gray(` + Wrote MCP config: ${path.relative(process.cwd(), mcpConfigPath) || mcpConfigPath}`)
|
|
781
994
|
);
|
|
995
|
+
const skillsDir = path.join(agentDir, 'skills');
|
|
996
|
+
const skillCount = fs.existsSync(skillsDir)
|
|
997
|
+
? fs.readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).length
|
|
998
|
+
: 0;
|
|
999
|
+
const relAgent = path.relative(process.cwd(), agentDir) || profile.dirName;
|
|
1000
|
+
console.log(pc.gray(` + Skills: ${relAgent}/skills/ (${skillCount} skill folders)`));
|
|
1001
|
+
if (skillCount === 0) {
|
|
1002
|
+
console.log(
|
|
1003
|
+
pc.yellow(
|
|
1004
|
+
` ! Không có skill — thử: flowgrid harness sync --agent=${agent}`,
|
|
1005
|
+
),
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
782
1008
|
}
|
|
783
1009
|
writeRootAgentsMd(process.cwd(), selectedAgents);
|
|
784
1010
|
console.log(pc.gray(' + Wrote root AGENTS.md'));
|
|
@@ -803,63 +1029,14 @@ export async function main() {
|
|
|
803
1029
|
pkg.devDependencies['@braintree/sanitize-url'] = '^7.1.0';
|
|
804
1030
|
}
|
|
805
1031
|
|
|
806
|
-
pkg
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
pkg.scripts['flowgrid:audit:e2e'] = 'flowgrid audit e2e';
|
|
814
|
-
pkg.scripts['flowgrid:audit:legacy'] = 'flowgrid audit legacy';
|
|
815
|
-
pkg.scripts['flowgrid:harness-sync'] = 'flowgrid harness sync';
|
|
816
|
-
pkg.scripts['flowgrid:harness-sync:full-docs'] = 'flowgrid harness sync --full-docs';
|
|
817
|
-
pkg.scripts['flowgrid:doctor'] = 'flowgrid doctor';
|
|
818
|
-
pkg.scripts['flowgrid:doctor:fix'] = 'flowgrid doctor --fix';
|
|
819
|
-
|
|
820
|
-
const injectDocsHubScripts =
|
|
821
|
-
selectedType === 'Document' || (feDocRoot && docsHubInRepo);
|
|
822
|
-
if (injectDocsHubScripts) {
|
|
823
|
-
pkg.scripts['flowgrid:split'] = 'flowgrid split';
|
|
824
|
-
pkg.scripts['flowgrid:split_all'] = 'flowgrid split_all';
|
|
825
|
-
pkg.scripts['flowgrid:render'] = 'flowgrid render';
|
|
826
|
-
pkg.scripts['flowgrid:openapi'] = 'flowgrid openapi_render';
|
|
827
|
-
pkg.scripts['flowgrid:openapi-ui'] = 'flowgrid openapi_build_ui';
|
|
828
|
-
pkg.scripts['flowgrid:dev'] = 'flowgrid dev';
|
|
829
|
-
pkg.scripts['flowgrid:build'] = 'flowgrid build';
|
|
830
|
-
pkg.scripts['flowgrid:publish'] = 'flowgrid publish';
|
|
831
|
-
pkg.scripts['flow:dev'] = 'flowgrid dev';
|
|
832
|
-
pkg.scripts['flow:render'] = 'flowgrid render';
|
|
833
|
-
pkg.scripts['flow:publish'] = 'flowgrid publish';
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
if (selectedType === 'Frontend' || selectedType === 'Fullstack') {
|
|
837
|
-
pkg.scripts['flowgrid:gen'] = 'flowgrid gen';
|
|
838
|
-
pkg.scripts['flowgrid:unit'] = 'flowgrid unit-gen';
|
|
839
|
-
pkg.scripts['flowgrid:css'] = 'flowgrid gen-css';
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (selectedType === 'Backend' || selectedType === 'Fullstack') {
|
|
843
|
-
pkg.scripts['flowgrid:api-gen'] = 'flowgrid api-gen';
|
|
844
|
-
pkg.scripts['flowgrid:api-unit'] = 'flowgrid api-unit-gen';
|
|
845
|
-
pkg.scripts['flowgrid:openapi'] = 'flowgrid openapi_render';
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
if (selectedType === 'Frontend' || selectedType === 'Backend' || selectedType === 'Fullstack') {
|
|
849
|
-
pkg.scripts['flowgrid:contract'] = 'flowgrid contract-gen';
|
|
850
|
-
}
|
|
1032
|
+
injectFlowgridConsumerScripts(pkg, {
|
|
1033
|
+
selectedType,
|
|
1034
|
+
feDocRoot,
|
|
1035
|
+
feTestRoot,
|
|
1036
|
+
docsHubInRepo,
|
|
1037
|
+
testsHubInRepo,
|
|
1038
|
+
});
|
|
851
1039
|
|
|
852
|
-
const injectTestsHubScripts = selectedType === 'Test' || Boolean(feTestRoot);
|
|
853
|
-
if (injectTestsHubScripts) {
|
|
854
|
-
pkg.scripts['flowgrid:cases'] = 'flowgrid cases:render';
|
|
855
|
-
pkg.scripts['flowgrid:cases-check'] = 'flowgrid cases:check';
|
|
856
|
-
pkg.scripts['flowgrid:cases-cov'] = 'flowgrid cases:coverage';
|
|
857
|
-
pkg.scripts['flowgrid:cases-gate'] = 'flowgrid cases:gate';
|
|
858
|
-
pkg.scripts['flowgrid:e2e-gen'] = 'flowgrid testcase:gen';
|
|
859
|
-
pkg.scripts['flowgrid:api-e2e-gen'] = 'flowgrid testcase:gen:api';
|
|
860
|
-
pkg.scripts['flowgrid:e2e-reg'] = 'flowgrid e2e-registry';
|
|
861
|
-
}
|
|
862
|
-
|
|
863
1040
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
864
1041
|
console.log(pc.blue(' + Injected flowgrid:* & flow:* scripts into package.json'));
|
|
865
1042
|
} catch (e) {
|
|
@@ -888,6 +1065,20 @@ export async function main() {
|
|
|
888
1065
|
console.log(pc.yellow(' ! Không thể khởi tạo SQLite cache (chưa có specs/registry): ' + e.message));
|
|
889
1066
|
}
|
|
890
1067
|
|
|
1068
|
+
if (['Frontend', 'Backend', 'Fullstack'].includes(selectedType)) {
|
|
1069
|
+
try {
|
|
1070
|
+
const { runRegistrySync } = await import('../engines/registry-sync/run-registry-sync.mjs');
|
|
1071
|
+
console.log(pc.blue('\n[INFO] Quét base → DSL registry (registry:sync)...'));
|
|
1072
|
+
await runRegistrySync([], {
|
|
1073
|
+
projectRoot: process.cwd(),
|
|
1074
|
+
packageRoot: packageRootFromMeta(import.meta.url),
|
|
1075
|
+
config: projectConfig,
|
|
1076
|
+
});
|
|
1077
|
+
} catch (e) {
|
|
1078
|
+
console.log(pc.yellow(' ! registry:sync skipped: ' + e.message));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
891
1082
|
const dslBackupDir = path.join(process.cwd(), 'artifactgraph');
|
|
892
1083
|
if (!fs.existsSync(dslBackupDir)) {
|
|
893
1084
|
fs.mkdirSync(dslBackupDir, { recursive: true });
|
|
@@ -910,7 +1101,10 @@ export async function main() {
|
|
|
910
1101
|
const optionalAns = await multiselect({
|
|
911
1102
|
message: 'Optional toolkits to initialize now (none = skip, add later):',
|
|
912
1103
|
options: [
|
|
913
|
-
{
|
|
1104
|
+
{
|
|
1105
|
+
value: 'codegraph',
|
|
1106
|
+
label: 'Codegraph — semantic code intelligence (cần CLI `codegraph` đã cài)',
|
|
1107
|
+
},
|
|
914
1108
|
],
|
|
915
1109
|
required: false
|
|
916
1110
|
});
|
|
@@ -918,11 +1112,28 @@ export async function main() {
|
|
|
918
1112
|
console.log(pc.blue('\n[INFO] Initializing Codegraph...'));
|
|
919
1113
|
try {
|
|
920
1114
|
const { spawnSync } = await import('node:child_process');
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
1115
|
+
const shell = process.platform === 'win32';
|
|
1116
|
+
const where = process.platform === 'win32' ? 'where' : 'which';
|
|
1117
|
+
const hasCodegraph =
|
|
1118
|
+
spawnSync(where, ['codegraph'], { encoding: 'utf8', shell }).status === 0;
|
|
1119
|
+
if (!hasCodegraph) {
|
|
1120
|
+
console.log(
|
|
1121
|
+
pc.yellow(
|
|
1122
|
+
' ! CodeGraph CLI chưa có trên PATH — bỏ qua (init FlowGrid vẫn hoàn tất).',
|
|
1123
|
+
),
|
|
1124
|
+
);
|
|
1125
|
+
console.log(
|
|
1126
|
+
pc.gray(
|
|
1127
|
+
' Sau này: cài CodeGraph theo tài liệu team → trong repo này chạy `codegraph init` → MCP: `platform-dna codegraph:wire` (skill /configure-repo-maps).',
|
|
1128
|
+
),
|
|
1129
|
+
);
|
|
924
1130
|
} else {
|
|
925
|
-
|
|
1131
|
+
const res = spawnSync('codegraph', ['init'], { stdio: 'inherit', shell });
|
|
1132
|
+
if (res.status === 0) {
|
|
1133
|
+
console.log(' + Codegraph initialized successfully');
|
|
1134
|
+
} else {
|
|
1135
|
+
console.log(pc.yellow(` ! Codegraph init failed (status ${res.status})`));
|
|
1136
|
+
}
|
|
926
1137
|
}
|
|
927
1138
|
} catch (e) {
|
|
928
1139
|
console.log(pc.yellow(' ! Failed to run Codegraph init: ' + e.message));
|
package/bin/lib/agent-mcp.mjs
CHANGED
|
@@ -10,6 +10,7 @@ export function buildMcpEnv({
|
|
|
10
10
|
selectedType,
|
|
11
11
|
feDocRoot,
|
|
12
12
|
feTestRoot,
|
|
13
|
+
feE2eRoot,
|
|
13
14
|
feAdapter,
|
|
14
15
|
beAdapter,
|
|
15
16
|
defaultLanguage,
|
|
@@ -23,6 +24,9 @@ export function buildMcpEnv({
|
|
|
23
24
|
if (feTestRoot) {
|
|
24
25
|
env.FLOWGRID_TESTS_DOC = path.resolve(base, feTestRoot);
|
|
25
26
|
}
|
|
27
|
+
if (feE2eRoot) {
|
|
28
|
+
env.FLOWGRID_E2E_ROOT = path.resolve(base, feE2eRoot);
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
if (feAdapter) {
|
|
28
32
|
env.FLOWGRID_ADAPTER = feAdapter;
|