@fenglimg/fabric-shared 1.6.0 → 1.8.0-rc.1

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.
@@ -0,0 +1,90 @@
1
+ // src/errors/fabric-error.ts
2
+ var FabricError = class extends Error {
3
+ actionHint;
4
+ fixable;
5
+ details;
6
+ constructor(message, opts) {
7
+ super(message);
8
+ if (!opts.actionHint || opts.actionHint.length === 0) {
9
+ throw new Error("FabricError: actionHint is required and must be non-empty");
10
+ }
11
+ this.name = this.constructor.name;
12
+ this.actionHint = opts.actionHint;
13
+ this.fixable = opts.fixable ?? false;
14
+ this.details = opts.details;
15
+ Object.setPrototypeOf(this, new.target.prototype);
16
+ }
17
+ toJSON() {
18
+ return {
19
+ name: this.name,
20
+ code: this.code,
21
+ message: this.message,
22
+ actionHint: this.actionHint,
23
+ fixable: this.fixable,
24
+ ...this.details !== void 0 ? { details: this.details } : {}
25
+ };
26
+ }
27
+ };
28
+
29
+ // src/errors/config-error.ts
30
+ var ConfigError = class extends FabricError {
31
+ httpStatus = 400;
32
+ };
33
+ var ConfigPathInvalidError = class extends ConfigError {
34
+ code = "config_path_invalid";
35
+ };
36
+ var GenericConfigError = class extends ConfigError {
37
+ code = "config_error";
38
+ };
39
+
40
+ // src/errors/rule-error.ts
41
+ var RuleError = class extends FabricError {
42
+ httpStatus = 422;
43
+ };
44
+ var RuleValidationError = class extends RuleError {
45
+ code = "rule_validation_error";
46
+ };
47
+
48
+ // src/errors/io-error.ts
49
+ var IOFabricError = class extends FabricError {
50
+ httpStatus = 500;
51
+ };
52
+ var PathEscapeError = class extends IOFabricError {
53
+ code = "PATH_OUTSIDE_PROJECT_ROOT";
54
+ httpStatus = 403;
55
+ };
56
+ var GenericIOError = class extends IOFabricError {
57
+ code = "io_error";
58
+ };
59
+
60
+ // src/errors/mcp-error.ts
61
+ var MCPError = class extends FabricError {
62
+ };
63
+ var McpToolError = class extends MCPError {
64
+ code = "mcp_tool_error";
65
+ httpStatus = 500;
66
+ };
67
+
68
+ // src/errors/init-error.ts
69
+ var InitError = class extends FabricError {
70
+ httpStatus = 500;
71
+ };
72
+ var InitFrameworkUnknownError = class extends InitError {
73
+ code = "init_framework_unknown";
74
+ };
75
+
76
+ export {
77
+ FabricError,
78
+ ConfigError,
79
+ ConfigPathInvalidError,
80
+ GenericConfigError,
81
+ RuleError,
82
+ RuleValidationError,
83
+ IOFabricError,
84
+ PathEscapeError,
85
+ GenericIOError,
86
+ MCPError,
87
+ McpToolError,
88
+ InitError,
89
+ InitFrameworkUnknownError
90
+ };
@@ -32,7 +32,7 @@ var enMessages = {
32
32
  "cli.bootstrap.description": "Install Fabric bootstrap prompts for supported AI clients.",
33
33
  "cli.bootstrap.install.description": "Copy Fabric bootstrap templates into native client locations.",
34
34
  "cli.bootstrap.install.args.clients.description": "Optional comma-separated client filter, for example claude,cursor,codex.",
35
- "cli.bootstrap.install.no-targets": "No bootstrap targets detected. Pass --clients claude,cursor,windsurf,roo,gemini,codex to install explicitly.",
35
+ "cli.bootstrap.install.no-targets": "No bootstrap targets detected. Pass --clients claude,cursor,codex to install explicitly.",
36
36
  "cli.bootstrap.install.installed": "Installed {path}",
37
37
  "cli.bootstrap.install.skipped-header": "Skipped {path}: Fabric Bootstrap header already present.",
38
38
  "cli.bootstrap.install.prepended": "Prepended {path}",
@@ -40,28 +40,23 @@ var enMessages = {
40
40
  "cli.config.description": "Manage Fabric MCP client configuration.",
41
41
  "cli.config.clients.claude": "Claude Code CLI",
42
42
  "cli.config.install.description": "Install Fabric MCP server entries into detected client configs.",
43
- "cli.config.install.args.clients.description": "Optional comma-separated client filter, for example cursor,codex,gemini.",
43
+ "cli.config.install.args.clients.description": "Optional comma-separated client filter, for example cursor,codex.",
44
44
  "cli.config.install.args.dry-run.description": "Preview detected write operations without modifying files.",
45
- "cli.config.errors.unknown-client": 'Unknown client "{client}". Use a comma-separated list such as cursor,codex,gemini.',
45
+ "cli.config.errors.unknown-client": 'Unknown client "{client}". Use a comma-separated list such as cursor,codex.',
46
46
  "cli.config.errors.expected-object": "Expected object in {path}",
47
47
  "cli.config.install.no-configs": "No Fabric MCP client config detected. Create the client directory or set clientPaths in fabric.config.json.",
48
48
  "cli.config.install.no-config-path": "Skipping {client}: no config path detected.",
49
49
  "cli.config.install.dry-run": "[dry-run] {client}: would write {path}",
50
50
  "cli.config.install.wrote": "{client}: wrote {path}",
51
- "cli.doctor.description": "Run Fabric doctor checks and optional compliance audit reporting.",
51
+ "cli.doctor.description": "Run Fabric target-state diagnostics.",
52
+ "doctor.section.fixable": "Fixable errors:",
53
+ "doctor.section.manual": "Manual errors:",
54
+ "doctor.section.warnings": "Warnings:",
52
55
  "cli.doctor.args.target.description": "Target project path. Defaults to CLI arg, EXTERNAL_FIXTURE_PATH, fabric.config.json, then cwd.",
53
- "cli.doctor.args.audit.description": "Print fab_get_rules compliance violations for AI edit intents.",
54
- "cli.doctor.args.fix.description": "Migrate the legacy root ledger into .fabric/.intent-ledger.jsonl when needed.",
55
- "cli.doctor.args.window-minutes.description": "Audit lookback window in minutes for matching fab_get_rules calls. Default 5.",
56
- "cli.doctor.errors.invalid-window": "Invalid audit window: {value}",
57
- "cli.doctor.audit.preview-only": "auditMode is off in fabric.config.json; running fab doctor --audit as a manual preview only.",
58
- "cli.doctor.audit.none": "No AI edit intents recorded yet for compliance audit.",
59
- "cli.doctor.audit.clean": "All {count} audited edit paths have a preceding fab_get_rules call within {window}.",
60
- "cli.doctor.audit.violations": "{count} audited edit paths are missing a preceding fab_get_rules call within {window}.",
61
- "cli.doctor.audit.table.path": "Path",
62
- "cli.doctor.audit.table.edit": "Edit time",
63
- "cli.doctor.audit.table.rules": "Last rules",
64
- "cli.doctor.audit.table.intent": "Intent",
56
+ "cli.doctor.args.fix.description": "Repair deterministic derived Fabric state, including meta, rule-test index, bootstrap, and events ledger.",
57
+ "cli.doctor.args.json.description": "Print the doctor report as JSON.",
58
+ "cli.doctor.args.strict.description": "Treat warnings as failures.",
59
+ "cli.doctor.args.force.description": "Run even if a serve process appears to hold the lock.",
65
60
  "cli.hooks.description": "Manage Fabric Git hook templates.",
66
61
  "cli.hooks.install.description": "Install the Fabric Husky pre-commit hook template.",
67
62
  "cli.hooks.install.args.target.description": "Target project path, default is the current working directory.",
@@ -94,6 +89,11 @@ var enMessages = {
94
89
  "cli.init.mcp.install.invalid": 'Invalid --mcp-install value "{value}" \u2014 falling back to global',
95
90
  "cli.init.mcp.local.installing": "Running {manager} add -D @fenglimg/fabric-server...",
96
91
  "cli.init.mcp.local.installed": "Installed to devDependencies",
92
+ "cli.init.mcp.scope.description": "Claude MCP config scope: project (writes .mcp.json) or user (writes ~/.claude.json)",
93
+ "cli.init.mcp.scope.invalid": 'Invalid --scope value "{value}" \u2014 falling back to project',
94
+ "cli.init.mcp.scope.project": "Writes .mcp.json in project root (per Claude Code spec)",
95
+ "cli.init.mcp.scope.user": "Writes ~/.claude.json (user-scoped, applies to all projects)",
96
+ "cli.init.wizard.mcp-scope": "Claude MCP config scope (project/.mcp.json or user/~/.claude.json) [{defaultValue}]",
97
97
  "cli.init.created-path": "{label} {path}",
98
98
  "cli.init.skipped-existing-path": "{label} {path}: already exists.",
99
99
  "cli.init.force.overwritten": "Overwritten",
@@ -217,6 +217,7 @@ var enMessages = {
217
217
  "cli.serve.args.host.description": "Listen host, default 127.0.0.1. Set FABRIC_AUTH_TOKEN to enable Bearer auth for non-localhost binding.",
218
218
  "cli.serve.args.target.description": "Target project path. Defaults to CLI arg, EXTERNAL_FIXTURE_PATH, fabric.config.json, then cwd.",
219
219
  "cli.serve.args.debug.description": "Print target resolution details to stderr.",
220
+ "cli.serve.args.force.description": "Force-acquire the serve lock even if another serve process appears to be running.",
220
221
  "cli.serve.ready.title": "Fabric Dashboard",
221
222
  "cli.serve.warning.host-fallback": "--host {host} requires FABRIC_AUTH_TOKEN; falling back to 127.0.0.1 for safety",
222
223
  "cli.serve.error.port-in-use": "Port {port} in use - try --port {nextPort}",
@@ -230,49 +231,28 @@ var enMessages = {
230
231
  "cli.sync-meta.drift-detected": "Fabric metadata drift detected. Run fab sync-meta to update.",
231
232
  "cli.sync-meta.updated": "{label} {path}",
232
233
  "dashboard.app.nav.aria-label": "Dashboard views",
233
- "dashboard.app.nav.module-a.label": "Rule Topology",
234
- "dashboard.app.nav.module-a.label-bilingual": "\u89C4\u5219\u547D\u4E2D Rule Topology",
235
- "dashboard.app.nav.module-a.subtitle": "coverage + hit reasons",
236
- "dashboard.app.nav.module-b.label": "Cognitive Forensic",
237
- "dashboard.app.nav.module-b.label-bilingual": "\u8BA4\u77E5\u626B\u63CF Cognitive Forensic",
238
- "dashboard.app.nav.module-b.subtitle": "placeholder",
239
- "dashboard.app.nav.module-c.label": "Semantic Timeline",
240
- "dashboard.app.nav.module-c.label-bilingual": "\u8BED\u4E49\u65F6\u95F4\u7EBF Semantic Timeline",
241
- "dashboard.app.nav.module-c.subtitle": "placeholder",
242
- "dashboard.app.nav.module-d.label": "Historical Ledger",
243
- "dashboard.app.nav.module-d.label-bilingual": "\u5386\u53F2\u8BB0\u5F55 Historical Ledger",
244
- "dashboard.app.nav.module-d.subtitle": "placeholder",
245
- "dashboard.app.nav.rules.label": "Rules Tree",
246
- "dashboard.app.nav.rules.label-bilingual": "\u89C4\u5219\u6811 Rules Tree",
247
- "dashboard.app.nav.rules.subtitle": "rule structure",
248
- "dashboard.app.nav.locks.label": "Human Lock",
249
- "dashboard.app.nav.locks.label-bilingual": "\u4EBA\u5DE5\u4FDD\u62A4 Human Lock",
250
- "dashboard.app.nav.locks.subtitle": "protected regions",
251
- "dashboard.app.nav.timeline.label": "Intent Timeline",
252
- "dashboard.app.nav.timeline.label-bilingual": "\u610F\u56FE\u65F6\u95F4\u7EBF Intent Timeline",
253
- "dashboard.app.nav.timeline.subtitle": "change log",
254
- "dashboard.app.nav.history.label": "History Replay",
255
- "dashboard.app.nav.history.label-bilingual": "\u5386\u53F2\u56DE\u653E History Replay",
256
- "dashboard.app.nav.history.subtitle": "time travel",
257
- "dashboard.app.nav.doctor.label": "Doctor",
258
- "dashboard.app.nav.doctor.label-bilingual": "\u8BCA\u65AD\u53F0 Doctor",
259
- "dashboard.app.nav.doctor.subtitle": "health checks",
260
- "dashboard.app.nav.section.modules-status": "Module Status",
261
- "dashboard.app.nav.section.diagnostics": "Diagnostics",
234
+ "dashboard.app.nav.readiness.label": "Readiness",
235
+ "dashboard.app.nav.readiness.label-bilingual": "\u51C6\u5907\u60C5\u51B5 Readiness",
236
+ "dashboard.app.nav.readiness.subtitle": "project status",
237
+ "dashboard.app.nav.rules-explain.label": "Rules Explain",
238
+ "dashboard.app.nav.rules-explain.label-bilingual": "\u89C4\u5219\u89E3\u6790 Rules Explain",
239
+ "dashboard.app.nav.rules-explain.subtitle": "topology & context",
240
+ "dashboard.app.nav.timeline.label": "Timeline",
241
+ "dashboard.app.nav.timeline.label-bilingual": "\u65F6\u95F4\u7EBF Timeline",
242
+ "dashboard.app.nav.timeline.subtitle": "audit & history",
243
+ "dashboard.app.nav.health.label": "Health",
244
+ "dashboard.app.nav.health.label-bilingual": "\u7CFB\u7EDF\u5065\u5EB7 Health",
245
+ "dashboard.app.nav.health.subtitle": "doctor & diagnostics",
246
+ "dashboard.app.nav.section.insights": "Insights",
262
247
  "dashboard.app.nav.drift-check": "Drift Check",
263
248
  "dashboard.app.nav.modules.read-only": "read-only dashboard",
264
249
  "dashboard.app.header.connected": "CONNECTED",
265
250
  "dashboard.app.header.connecting": "CONNECTING",
266
251
  "dashboard.app.live-region.received": "Received {type}",
267
- "dashboard.app.breadcrumb.topology": "rule-topology",
268
- "dashboard.app.breadcrumb.forensic": "cognitive-forensic",
269
- "dashboard.app.breadcrumb.semantic": "semantic-timeline",
270
- "dashboard.app.breadcrumb.ledger": "historical-ledger",
271
- "dashboard.app.breadcrumb.rules": "rules-tree",
272
- "dashboard.app.breadcrumb.locks": "human-lock",
273
- "dashboard.app.breadcrumb.timeline": "intent-timeline",
274
- "dashboard.app.breadcrumb.history": "history-replay",
275
- "dashboard.app.breadcrumb.doctor": "doctor",
252
+ "dashboard.app.breadcrumb.readiness": "readiness",
253
+ "dashboard.app.breadcrumb.rules-explain": "rules-explain",
254
+ "dashboard.app.breadcrumb.timeline": "timeline",
255
+ "dashboard.app.breadcrumb.health": "health",
276
256
  "dashboard.rule-topology.title": "Rule Topology",
277
257
  "dashboard.rule-topology.subtitle": "See which rules match the current path and why",
278
258
  "dashboard.rule-topology.path.placeholder": "Sample path for rules context",
@@ -427,7 +407,30 @@ var enMessages = {
427
407
  "dashboard.lock-card.diff.no-changes": "no changes",
428
408
  "dashboard.lock-card.diff.with-bytes": "+{added} / -{removed} \xB7 {bytes} bytes",
429
409
  "dashboard.lock-card.diff.without-bytes": "+{added} / -{removed}",
430
- "dashboard.approve-button.retry": "Retry"
410
+ "dashboard.approve-button.retry": "Retry",
411
+ "dashboard.readiness.filter.analysis": "Project Analysis",
412
+ "dashboard.readiness.loading": "Loading scan data...",
413
+ "dashboard.readiness.summary.framework": "Framework",
414
+ "dashboard.readiness.summary.files": "Files",
415
+ "dashboard.readiness.summary.status": "Fabric Status",
416
+ "dashboard.readiness.card.evidence": "Readiness Evidence",
417
+ "dashboard.readiness.card.recommendations": "Recommendations & Next Steps",
418
+ "dashboard.readiness.readme.description": "Quality of project documentation",
419
+ "dashboard.readiness.contributing.description": "Contribution guidelines for AI/Human",
420
+ "dashboard.readiness.fully-ready": "Project is fully ready.",
421
+ "dashboard.readiness.init-prompt": "Run this command to initialize:",
422
+ "dashboard.rules-explain.analyze": "Analyze Path",
423
+ "dashboard.rules-explain.detail.topology-type": "Topology Type",
424
+ "dashboard.timeline.history-replay.title": "History Replay",
425
+ "dashboard.timeline.close": "Close",
426
+ "dashboard.health.ledger-path.label": "Event Ledger Path",
427
+ "dashboard.health.ledger-path.detail": "Append-only timeline source",
428
+ "dashboard.health.boundary.title": "Control Plane Boundaries",
429
+ "dashboard.health.boundary.description": "The Web Dashboard operates as a Viewer. All rules, metadata, and fixes must be managed via the CLI.",
430
+ "dashboard.health.boundary.cli-action": "CLI Action Required:",
431
+ "dashboard.health.boundary.cli-prompt": "You have {count} fixable issues. Run the following command in your terminal to repair metadata automatically.",
432
+ "dashboard.health.runtime.connected": "MCP Runtime Connected",
433
+ "dashboard.health.runtime.disconnected": "MCP Runtime Disconnected"
431
434
  };
432
435
 
433
436
  // src/i18n/locales/zh-CN.ts
@@ -464,7 +467,7 @@ var zhCNMessages = {
464
467
  "cli.bootstrap.description": "\u4E3A\u652F\u6301\u7684 AI \u5BA2\u6237\u7AEF\u5B89\u88C5 Fabric \u5F15\u5BFC\u63D0\u793A\u6A21\u677F\u3002",
465
468
  "cli.bootstrap.install.description": "\u5C06 Fabric \u5F15\u5BFC\u6A21\u677F\u590D\u5236\u5230\u5404\u5BA2\u6237\u7AEF\u7684\u539F\u751F\u4F4D\u7F6E\u3002",
466
469
  "cli.bootstrap.install.args.clients.description": "\u53EF\u9009\u7684\u9017\u53F7\u5206\u9694\u5BA2\u6237\u7AEF\u8FC7\u6EE4\u5668\uFF0C\u4F8B\u5982 claude,cursor,codex\u3002",
467
- "cli.bootstrap.install.no-targets": "\u672A\u68C0\u6D4B\u5230\u53EF\u5B89\u88C5\u7684 bootstrap \u76EE\u6807\u3002\u53EF\u663E\u5F0F\u4F20\u5165 --clients claude,cursor,windsurf,roo,gemini,codex\u3002",
470
+ "cli.bootstrap.install.no-targets": "\u672A\u68C0\u6D4B\u5230\u53EF\u5B89\u88C5\u7684 bootstrap \u76EE\u6807\u3002\u53EF\u663E\u5F0F\u4F20\u5165 --clients claude,cursor,codex\u3002",
468
471
  "cli.bootstrap.install.installed": "\u5DF2\u5B89\u88C5 {path}",
469
472
  "cli.bootstrap.install.skipped-header": "\u5DF2\u8DF3\u8FC7 {path}\uFF1AFabric Bootstrap \u5934\u90E8\u5DF2\u5B58\u5728\u3002",
470
473
  "cli.bootstrap.install.prepended": "\u5DF2\u524D\u7F6E\u5199\u5165 {path}",
@@ -472,28 +475,23 @@ var zhCNMessages = {
472
475
  "cli.config.description": "\u7BA1\u7406 Fabric MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u3002",
473
476
  "cli.config.clients.claude": "Claude Code CLI",
474
477
  "cli.config.install.description": "\u5C06 Fabric MCP \u670D\u52A1\u7AEF\u6761\u76EE\u5B89\u88C5\u5230\u68C0\u6D4B\u5230\u7684\u5BA2\u6237\u7AEF\u914D\u7F6E\u4E2D\u3002",
475
- "cli.config.install.args.clients.description": "\u53EF\u9009\u7684\u9017\u53F7\u5206\u9694\u5BA2\u6237\u7AEF\u8FC7\u6EE4\u5668\uFF0C\u4F8B\u5982 cursor,codex,gemini\u3002",
478
+ "cli.config.install.args.clients.description": "\u53EF\u9009\u7684\u9017\u53F7\u5206\u9694\u5BA2\u6237\u7AEF\u8FC7\u6EE4\u5668\uFF0C\u4F8B\u5982 cursor,codex\u3002",
476
479
  "cli.config.install.args.dry-run.description": "\u4EC5\u9884\u89C8\u5C06\u8981\u53D1\u751F\u7684\u5199\u5165\u64CD\u4F5C\uFF0C\u4E0D\u4FEE\u6539\u6587\u4EF6\u3002",
477
- "cli.config.errors.unknown-client": "\u672A\u77E5\u5BA2\u6237\u7AEF\u201C{client}\u201D\u3002\u8BF7\u4F7F\u7528\u9017\u53F7\u5206\u9694\u5217\u8868\uFF0C\u4F8B\u5982 cursor,codex,gemini\u3002",
480
+ "cli.config.errors.unknown-client": "\u672A\u77E5\u5BA2\u6237\u7AEF\u201C{client}\u201D\u3002\u8BF7\u4F7F\u7528\u9017\u53F7\u5206\u9694\u5217\u8868\uFF0C\u4F8B\u5982 cursor,codex\u3002",
478
481
  "cli.config.errors.expected-object": "{path} \u4E2D\u5E94\u4E3A\u5BF9\u8C61\u3002",
479
482
  "cli.config.install.no-configs": "\u672A\u68C0\u6D4B\u5230 Fabric MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u3002\u8BF7\u521B\u5EFA\u5BA2\u6237\u7AEF\u76EE\u5F55\uFF0C\u6216\u5728 fabric.config.json \u4E2D\u8BBE\u7F6E clientPaths\u3002",
480
483
  "cli.config.install.no-config-path": "\u8DF3\u8FC7 {client}\uFF1A\u672A\u68C0\u6D4B\u5230\u914D\u7F6E\u8DEF\u5F84\u3002",
481
484
  "cli.config.install.dry-run": "[dry-run] {client}\uFF1A\u5C06\u5199\u5165 {path}",
482
485
  "cli.config.install.wrote": "{client}\uFF1A\u5DF2\u5199\u5165 {path}",
483
- "cli.doctor.description": "\u8FD0\u884C Fabric doctor \u68C0\u67E5\uFF0C\u5E76\u53EF\u9009\u8F93\u51FA\u5408\u89C4\u5BA1\u8BA1\u62A5\u544A\u3002",
486
+ "cli.doctor.description": "\u8FD0\u884C Fabric \u76EE\u6807\u6001\u8BCA\u65AD\u3002",
487
+ "doctor.section.fixable": "\u53EF\u4FEE\u590D\u9519\u8BEF\uFF1A",
488
+ "doctor.section.manual": "\u9700\u624B\u52A8\u4FEE\u590D\uFF1A",
489
+ "doctor.section.warnings": "\u8B66\u544A\uFF1A",
484
490
  "cli.doctor.args.target.description": "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\u3002\u9ED8\u8BA4\u4F9D\u6B21\u4F7F\u7528 CLI \u53C2\u6570\u3001EXTERNAL_FIXTURE_PATH\u3001fabric.config.json\u3001\u5F53\u524D\u76EE\u5F55\u3002",
485
- "cli.doctor.args.audit.description": "\u8F93\u51FA AI \u7F16\u8F91\u610F\u56FE\u7F3A\u5C11 fab_get_rules \u524D\u7F6E\u8C03\u7528\u7684\u8FDD\u89C4\u9879\u3002",
486
- "cli.doctor.args.fix.description": "\u9700\u8981\u65F6\u5C06\u65E7\u7684\u6839\u76EE\u5F55 ledger \u8FC1\u79FB\u5230 .fabric/.intent-ledger.jsonl\u3002",
487
- "cli.doctor.args.window-minutes.description": "\u5339\u914D fab_get_rules \u8C03\u7528\u7684\u56DE\u770B\u65F6\u95F4\u7A97\u53E3\uFF0C\u5355\u4F4D\u4E3A\u5206\u949F\u3002\u9ED8\u8BA4 5 \u5206\u949F\u3002",
488
- "cli.doctor.errors.invalid-window": "\u65E0\u6548\u7684\u5BA1\u8BA1\u65F6\u95F4\u7A97\u53E3\uFF1A{value}",
489
- "cli.doctor.audit.preview-only": "fabric.config.json \u4E2D auditMode \u4E3A off\uFF1B\u5F53\u524D fab doctor --audit \u4EC5\u6267\u884C\u624B\u52A8\u9884\u89C8\uFF0C\u4E0D\u4F1A\u6539\u53D8\u9000\u51FA\u7801\u3002",
490
- "cli.doctor.audit.none": "\u5F53\u524D\u8FD8\u6CA1\u6709\u53EF\u7528\u4E8E\u5408\u89C4\u5BA1\u8BA1\u7684 AI \u7F16\u8F91\u610F\u56FE\u8BB0\u5F55\u3002",
491
- "cli.doctor.audit.clean": "\u5DF2\u5BA1\u8BA1\u7684 {count} \u4E2A\u7F16\u8F91\u8DEF\u5F84\u90FD\u5728 {window} \u5185\u5B58\u5728\u524D\u7F6E fab_get_rules \u8C03\u7528\u3002",
492
- "cli.doctor.audit.violations": "\u6709 {count} \u4E2A\u5DF2\u5BA1\u8BA1\u7F16\u8F91\u8DEF\u5F84\u5728 {window} \u5185\u7F3A\u5C11\u524D\u7F6E fab_get_rules \u8C03\u7528\u3002",
493
- "cli.doctor.audit.table.path": "\u8DEF\u5F84",
494
- "cli.doctor.audit.table.edit": "\u7F16\u8F91\u65F6\u95F4",
495
- "cli.doctor.audit.table.rules": "\u6700\u8FD1\u89C4\u5219\u8C03\u7528",
496
- "cli.doctor.audit.table.intent": "\u610F\u56FE",
491
+ "cli.doctor.args.fix.description": "\u4FEE\u590D\u786E\u5B9A\u6027\u6D3E\u751F\u7684 Fabric \u72B6\u6001\uFF0C\u5305\u62EC meta\u3001rule-test index\u3001bootstrap \u548C events ledger\u3002",
492
+ "cli.doctor.args.json.description": "\u4EE5 JSON \u8F93\u51FA doctor \u62A5\u544A\u3002",
493
+ "cli.doctor.args.strict.description": "\u5C06 warning \u4E5F\u89C6\u4E3A\u5931\u8D25\u3002",
494
+ "cli.doctor.args.force.description": "\u5373\u4F7F serve \u8FDB\u7A0B\u6301\u6709\u9501\uFF0C\u4E5F\u5F3A\u5236\u8FD0\u884C\u3002",
497
495
  "cli.hooks.description": "\u7BA1\u7406 Fabric Git \u94A9\u5B50\u6A21\u677F\u3002",
498
496
  "cli.hooks.install.description": "\u5B89\u88C5 Fabric Husky pre-commit \u94A9\u5B50\u6A21\u677F\u3002",
499
497
  "cli.hooks.install.args.target.description": "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002",
@@ -526,6 +524,11 @@ var zhCNMessages = {
526
524
  "cli.init.mcp.install.invalid": "\u65E0\u6548\u7684 --mcp-install \u503C\u201C{value}\u201D\uFF0C\u5C06\u56DE\u9000\u5230 global",
527
525
  "cli.init.mcp.local.installing": "\u6B63\u5728\u8FD0\u884C {manager} add -D @fenglimg/fabric-server...",
528
526
  "cli.init.mcp.local.installed": "\u5DF2\u5B89\u88C5\u5230 devDependencies",
527
+ "cli.init.mcp.scope.description": "Claude MCP \u914D\u7F6E\u8303\u56F4\uFF1Aproject\uFF08\u5199\u5165 .mcp.json\uFF09\u6216 user\uFF08\u5199\u5165 ~/.claude.json\uFF09",
528
+ "cli.init.mcp.scope.invalid": "\u65E0\u6548\u7684 --scope \u503C\u201C{value}\u201D\uFF0C\u5C06\u56DE\u9000\u5230 project",
529
+ "cli.init.mcp.scope.project": "\u5199\u5165\u9879\u76EE\u6839\u76EE\u5F55\u7684 .mcp.json\uFF08\u7B26\u5408 Claude Code \u89C4\u8303\uFF09",
530
+ "cli.init.mcp.scope.user": "\u5199\u5165 ~/.claude.json\uFF08\u7528\u6237\u8303\u56F4\uFF0C\u9002\u7528\u4E8E\u6240\u6709\u9879\u76EE\uFF09",
531
+ "cli.init.wizard.mcp-scope": "Claude MCP \u914D\u7F6E\u8303\u56F4\uFF08project/.mcp.json \u6216 user/~/.claude.json\uFF09[{defaultValue}]",
529
532
  "cli.init.created-path": "{label} {path}",
530
533
  "cli.init.skipped-existing-path": "{label} {path}\uFF1A\u5DF2\u5B58\u5728\u3002",
531
534
  "cli.init.force.overwritten": "\u5DF2\u8986\u76D6",
@@ -649,6 +652,7 @@ var zhCNMessages = {
649
652
  "cli.serve.args.host.description": "\u76D1\u542C\u4E3B\u673A\uFF0C\u9ED8\u8BA4 127.0.0.1\u3002\u82E5\u9700\u7ED1\u5B9A\u5230\u975E localhost\uFF0C\u8BF7\u8BBE\u7F6E FABRIC_AUTH_TOKEN \u4EE5\u542F\u7528 Bearer \u9274\u6743\u3002",
650
653
  "cli.serve.args.target.description": "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\u3002\u9ED8\u8BA4\u4F9D\u6B21\u4F7F\u7528 CLI \u53C2\u6570\u3001EXTERNAL_FIXTURE_PATH\u3001fabric.config.json\u3001\u5F53\u524D\u76EE\u5F55\u3002",
651
654
  "cli.serve.args.debug.description": "\u5C06\u76EE\u6807\u89E3\u6790\u7EC6\u8282\u8F93\u51FA\u5230 stderr\u3002",
655
+ "cli.serve.args.force.description": "\u5373\u4F7F\u53E6\u4E00\u4E2A serve \u8FDB\u7A0B\u6B63\u5728\u8FD0\u884C\uFF0C\u4E5F\u5F3A\u5236\u83B7\u53D6 serve \u9501\u3002",
652
656
  "cli.serve.ready.title": "Fabric \u4EEA\u8868\u76D8",
653
657
  "cli.serve.warning.host-fallback": "--host {host} \u9700\u8981 FABRIC_AUTH_TOKEN\uFF1B\u4E3A\u5B89\u5168\u8D77\u89C1\u5DF2\u56DE\u9000\u5230 127.0.0.1",
654
658
  "cli.serve.error.port-in-use": "\u7AEF\u53E3 {port} \u5DF2\u88AB\u5360\u7528\uFF0C\u53EF\u5C1D\u8BD5 --port {nextPort}",
@@ -662,49 +666,28 @@ var zhCNMessages = {
662
666
  "cli.sync-meta.drift-detected": "\u68C0\u6D4B\u5230 Fabric \u5143\u6570\u636E\u6F02\u79FB\u3002\u8BF7\u8FD0\u884C fab sync-meta \u8FDB\u884C\u66F4\u65B0\u3002",
663
667
  "cli.sync-meta.updated": "{label} {path}",
664
668
  "dashboard.app.nav.aria-label": "\u4EEA\u8868\u76D8\u89C6\u56FE\u5BFC\u822A",
665
- "dashboard.app.nav.module-a.label": "\u89C4\u5219\u62D3\u6251",
666
- "dashboard.app.nav.module-a.label-bilingual": "\u89C4\u5219\u547D\u4E2D Rule Topology",
667
- "dashboard.app.nav.module-a.subtitle": "\u8986\u76D6\u70ED\u529B\u56FE + \u547D\u4E2D\u539F\u56E0",
668
- "dashboard.app.nav.module-b.label": "\u8BA4\u77E5\u626B\u63CF",
669
- "dashboard.app.nav.module-b.label-bilingual": "\u8BA4\u77E5\u626B\u63CF Cognitive Forensic",
670
- "dashboard.app.nav.module-b.subtitle": "\u5360\u4F4D",
671
- "dashboard.app.nav.module-c.label": "\u8BED\u4E49\u65F6\u95F4\u7EBF",
672
- "dashboard.app.nav.module-c.label-bilingual": "\u8BED\u4E49\u65F6\u95F4\u7EBF Semantic Timeline",
673
- "dashboard.app.nav.module-c.subtitle": "\u5360\u4F4D",
674
- "dashboard.app.nav.module-d.label": "\u5386\u53F2\u8BB0\u5F55",
675
- "dashboard.app.nav.module-d.label-bilingual": "\u5386\u53F2\u8BB0\u5F55 Historical Ledger",
676
- "dashboard.app.nav.module-d.subtitle": "\u5360\u4F4D",
677
- "dashboard.app.nav.rules.label": "\u89C4\u5219\u6811",
678
- "dashboard.app.nav.rules.label-bilingual": "\u89C4\u5219\u6811 Rules Tree",
679
- "dashboard.app.nav.rules.subtitle": "\u89C4\u5219\u7ED3\u6784",
680
- "dashboard.app.nav.locks.label": "\u4EBA\u5DE5\u4FDD\u62A4",
681
- "dashboard.app.nav.locks.label-bilingual": "\u4EBA\u5DE5\u4FDD\u62A4 Human Lock",
682
- "dashboard.app.nav.locks.subtitle": "\u53D7\u4FDD\u62A4\u533A\u57DF",
683
- "dashboard.app.nav.timeline.label": "\u610F\u56FE\u65F6\u95F4\u7EBF",
684
- "dashboard.app.nav.timeline.label-bilingual": "\u610F\u56FE\u65F6\u95F4\u7EBF Intent Timeline",
685
- "dashboard.app.nav.timeline.subtitle": "\u53D8\u66F4\u8BB0\u5F55",
686
- "dashboard.app.nav.history.label": "\u5386\u53F2\u56DE\u653E",
687
- "dashboard.app.nav.history.label-bilingual": "\u5386\u53F2\u56DE\u653E History Replay",
688
- "dashboard.app.nav.history.subtitle": "\u65F6\u95F4\u56DE\u6EAF",
689
- "dashboard.app.nav.doctor.label": "\u8BCA\u65AD\u53F0",
690
- "dashboard.app.nav.doctor.label-bilingual": "\u8BCA\u65AD\u53F0 Doctor",
691
- "dashboard.app.nav.doctor.subtitle": "\u72B6\u6001\u68C0\u67E5",
692
- "dashboard.app.nav.section.modules-status": "\u6A21\u5757\u72B6\u6001",
693
- "dashboard.app.nav.section.diagnostics": "\u8BCA\u65AD",
669
+ "dashboard.app.nav.readiness.label": "\u51C6\u5907\u60C5\u51B5",
670
+ "dashboard.app.nav.readiness.label-bilingual": "\u51C6\u5907\u60C5\u51B5 Readiness",
671
+ "dashboard.app.nav.readiness.subtitle": "\u9879\u76EE\u72B6\u6001",
672
+ "dashboard.app.nav.rules-explain.label": "\u89C4\u5219\u89E3\u6790",
673
+ "dashboard.app.nav.rules-explain.label-bilingual": "\u89C4\u5219\u89E3\u6790 Rules Explain",
674
+ "dashboard.app.nav.rules-explain.subtitle": "\u62D3\u6251\u4E0E\u4E0A\u4E0B\u6587",
675
+ "dashboard.app.nav.timeline.label": "\u65F6\u95F4\u7EBF",
676
+ "dashboard.app.nav.timeline.label-bilingual": "\u65F6\u95F4\u7EBF Timeline",
677
+ "dashboard.app.nav.timeline.subtitle": "\u5BA1\u8BA1\u4E0E\u5386\u53F2",
678
+ "dashboard.app.nav.health.label": "\u7CFB\u7EDF\u5065\u5EB7",
679
+ "dashboard.app.nav.health.label-bilingual": "\u7CFB\u7EDF\u5065\u5EB7 Health",
680
+ "dashboard.app.nav.health.subtitle": "\u8BCA\u65AD\u53F0",
681
+ "dashboard.app.nav.section.insights": "\u6D1E\u5BDF",
694
682
  "dashboard.app.nav.drift-check": "\u6F02\u79FB\u68C0\u67E5",
695
683
  "dashboard.app.nav.modules.read-only": "\u53EA\u8BFB\u4EEA\u8868\u76D8",
696
684
  "dashboard.app.header.connected": "\u5DF2\u8FDE\u63A5",
697
685
  "dashboard.app.header.connecting": "\u8FDE\u63A5\u4E2D",
698
686
  "dashboard.app.live-region.received": "\u5DF2\u6536\u5230 {type}",
699
- "dashboard.app.breadcrumb.topology": "rule-topology",
700
- "dashboard.app.breadcrumb.forensic": "cognitive-forensic",
701
- "dashboard.app.breadcrumb.semantic": "semantic-timeline",
702
- "dashboard.app.breadcrumb.ledger": "historical-ledger",
703
- "dashboard.app.breadcrumb.rules": "rules-tree",
704
- "dashboard.app.breadcrumb.locks": "human-lock",
705
- "dashboard.app.breadcrumb.timeline": "intent-timeline",
706
- "dashboard.app.breadcrumb.history": "history-replay",
707
- "dashboard.app.breadcrumb.doctor": "doctor",
687
+ "dashboard.app.breadcrumb.readiness": "readiness",
688
+ "dashboard.app.breadcrumb.rules-explain": "rules-explain",
689
+ "dashboard.app.breadcrumb.timeline": "timeline",
690
+ "dashboard.app.breadcrumb.health": "health",
708
691
  "dashboard.rule-topology.title": "\u89C4\u5219\u547D\u4E2D",
709
692
  "dashboard.rule-topology.subtitle": "\u67E5\u770B\u5F53\u524D\u8DEF\u5F84\u4F1A\u547D\u4E2D\u54EA\u4E9B\u89C4\u5219\uFF0C\u4EE5\u53CA\u4E3A\u4EC0\u4E48\u4F1A\u547D\u4E2D",
710
693
  "dashboard.rule-topology.path.placeholder": "\u7528\u4E8E\u89C4\u5219\u4E0A\u4E0B\u6587\u7684\u6837\u672C\u8DEF\u5F84",
@@ -859,7 +842,30 @@ var zhCNMessages = {
859
842
  "dashboard.lock-card.diff.no-changes": "\u65E0\u53D8\u66F4",
860
843
  "dashboard.lock-card.diff.with-bytes": "+{added} / -{removed} \xB7 {bytes} \u5B57\u8282",
861
844
  "dashboard.lock-card.diff.without-bytes": "+{added} / -{removed}",
862
- "dashboard.approve-button.retry": "\u91CD\u8BD5"
845
+ "dashboard.approve-button.retry": "\u91CD\u8BD5",
846
+ "dashboard.readiness.filter.analysis": "\u9879\u76EE\u5206\u6790",
847
+ "dashboard.readiness.loading": "\u6B63\u5728\u52A0\u8F7D\u626B\u63CF\u6570\u636E...",
848
+ "dashboard.readiness.summary.framework": "\u6846\u67B6",
849
+ "dashboard.readiness.summary.files": "\u6587\u4EF6",
850
+ "dashboard.readiness.summary.status": "Fabric \u72B6\u6001",
851
+ "dashboard.readiness.card.evidence": "\u51C6\u5907\u60C5\u51B5\u51ED\u8BC1",
852
+ "dashboard.readiness.card.recommendations": "\u5EFA\u8BAE\u4E0E\u540E\u7EED\u6B65\u9AA4",
853
+ "dashboard.readiness.readme.description": "\u9879\u76EE\u6587\u6863\u7684\u8D28\u91CF",
854
+ "dashboard.readiness.contributing.description": "AI\u4E0E\u4EBA\u7C7B\u534F\u4F5C\u7684\u8D21\u732E\u6307\u5357",
855
+ "dashboard.readiness.fully-ready": "\u9879\u76EE\u5DF2\u5B8C\u5168\u51C6\u5907\u5C31\u7EEA\u3002",
856
+ "dashboard.readiness.init-prompt": "\u8FD0\u884C\u6B64\u547D\u4EE4\u8FDB\u884C\u521D\u59CB\u5316\uFF1A",
857
+ "dashboard.rules-explain.analyze": "\u5206\u6790\u8DEF\u5F84",
858
+ "dashboard.rules-explain.detail.topology-type": "\u62D3\u6251\u7C7B\u578B",
859
+ "dashboard.timeline.history-replay.title": "\u5386\u53F2\u56DE\u653E",
860
+ "dashboard.timeline.close": "\u5173\u95ED",
861
+ "dashboard.health.ledger-path.label": "\u4E8B\u4EF6\u8D26\u672C\u8DEF\u5F84",
862
+ "dashboard.health.ledger-path.detail": "\u53EA\u80FD\u8FFD\u52A0\u5199\u5165\u7684\u65F6\u95F4\u7EBF\u6570\u636E\u6E90",
863
+ "dashboard.health.boundary.title": "\u63A7\u5236\u5E73\u9762\u8FB9\u754C",
864
+ "dashboard.health.boundary.description": "Web \u63A7\u5236\u53F0\u4F5C\u4E3A\u7EAF\u67E5\u770B\u5668 (Viewer) \u8FD0\u884C\u3002\u6240\u6709\u89C4\u5219\u3001\u5143\u6570\u636E\u548C\u4FEE\u590D\u64CD\u4F5C\u90FD\u5FC5\u987B\u901A\u8FC7 CLI \u8FDB\u884C\u7BA1\u7406\u3002",
865
+ "dashboard.health.boundary.cli-action": "\u9700\u8981\u6267\u884C CLI \u64CD\u4F5C\uFF1A",
866
+ "dashboard.health.boundary.cli-prompt": "\u68C0\u6D4B\u5230 {count} \u4E2A\u53EF\u4FEE\u590D\u7684\u95EE\u9898\u3002\u8BF7\u5728\u7EC8\u7AEF\u4E2D\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u4EE5\u81EA\u52A8\u4FEE\u590D\u5143\u6570\u636E\u3002",
867
+ "dashboard.health.runtime.connected": "MCP \u8FD0\u884C\u65F6\u5DF2\u8FDE\u63A5",
868
+ "dashboard.health.runtime.disconnected": "MCP \u8FD0\u884C\u65F6\u5DF2\u65AD\u5F00"
863
869
  };
864
870
 
865
871
  // src/i18n/create-translator.ts
@@ -911,9 +917,8 @@ function detectNodeLocale() {
911
917
 
912
918
  // src/i18n/protected-tokens.ts
913
919
  var PROTECTED_TOKENS = [
914
- "fab_get_rules",
915
- "fab_append_intent",
916
- "fab_update_registry",
920
+ "fab_plan_context",
921
+ "fab_get_rule_sections",
917
922
  "fabric_rules",
918
923
  "ledger_entry",
919
924
  "ledger_entry_id",
@@ -927,6 +932,7 @@ var PROTECTED_TOKENS = [
927
932
  ".fabric/init-context.json",
928
933
  ".fabric/forensic.json",
929
934
  ".fabric/.intent-ledger.jsonl",
935
+ ".fabric/events.jsonl",
930
936
  "@HUMAN",
931
937
  "shadow constraints",
932
938
  "Shadow Mirroring",
@@ -0,0 +1,176 @@
1
+ // src/detector.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ function detectFramework(root) {
5
+ const evidence = [];
6
+ const creatorConfigPath = join(root, "project.config.json");
7
+ if (existsSync(creatorConfigPath)) {
8
+ const version = readCreatorVersion(creatorConfigPath);
9
+ return {
10
+ kind: "cocos-creator",
11
+ version,
12
+ subkind: inferCocosSubkind(root, version),
13
+ evidence: version === "unknown" ? ["project.config.json"] : [`project.config.json: creator.version=${version}`],
14
+ framework: "cocos-creator",
15
+ confidence: "HIGH",
16
+ ast_evidence: [],
17
+ co_packages: collectProjectFileEvidence(root, ["package.json", "tsconfig.json"])
18
+ };
19
+ }
20
+ const packageJsonPath = join(root, "package.json");
21
+ if (existsSync(packageJsonPath)) {
22
+ const packageJson = readPackageJson(packageJsonPath);
23
+ const creatorVersion = packageJson.creator?.version;
24
+ if (typeof creatorVersion === "string" && creatorVersion.trim().length > 0) {
25
+ const deps2 = collectDependencyVersions(packageJson);
26
+ return {
27
+ kind: "cocos-creator",
28
+ version: creatorVersion,
29
+ subkind: inferCocosSubkind(root, creatorVersion),
30
+ evidence: [`package.json: creator.version=${creatorVersion}`],
31
+ framework: "cocos-creator",
32
+ confidence: "HIGH",
33
+ ast_evidence: [],
34
+ co_packages: collectCoPackages(deps2, "cocos-creator", root)
35
+ };
36
+ }
37
+ const deps = collectDependencyVersions(packageJson);
38
+ for (const [dependencyName, kind] of [
39
+ ["next", "next"],
40
+ ["vite", "vite"],
41
+ ["react", "react"],
42
+ ["vue", "vue"]
43
+ ]) {
44
+ if (deps.has(dependencyName)) {
45
+ const version = deps.get(dependencyName) ?? "unknown";
46
+ evidence.push(`package.json dependency: ${dependencyName}@${version}`);
47
+ return {
48
+ kind,
49
+ version,
50
+ subkind: inferPackageSubkind(kind),
51
+ evidence,
52
+ framework: kind,
53
+ confidence: determinePackageConfidence(kind, deps, root),
54
+ ast_evidence: [],
55
+ co_packages: collectCoPackages(deps, kind, root)
56
+ };
57
+ }
58
+ }
59
+ evidence.push("package.json");
60
+ }
61
+ if (existsSync(join(root, "Cargo.toml"))) {
62
+ return {
63
+ kind: "rust",
64
+ version: "unknown",
65
+ subkind: "cargo-project",
66
+ evidence: ["Cargo.toml"],
67
+ framework: "rust",
68
+ confidence: "HIGH",
69
+ ast_evidence: [],
70
+ co_packages: collectProjectFileEvidence(root, ["Cargo.lock"])
71
+ };
72
+ }
73
+ if (existsSync(join(root, "pyproject.toml"))) {
74
+ return {
75
+ kind: "python",
76
+ version: "unknown",
77
+ subkind: "pyproject",
78
+ evidence: ["pyproject.toml"],
79
+ framework: "python",
80
+ confidence: "HIGH",
81
+ ast_evidence: [],
82
+ co_packages: collectProjectFileEvidence(root, ["uv.lock", "poetry.lock", "requirements.txt"])
83
+ };
84
+ }
85
+ return {
86
+ kind: "unknown",
87
+ version: "unknown",
88
+ subkind: "unknown",
89
+ evidence,
90
+ framework: "unknown",
91
+ confidence: "LOW",
92
+ ast_evidence: [],
93
+ co_packages: []
94
+ };
95
+ }
96
+ function readPackageJson(packageJsonPath) {
97
+ try {
98
+ return JSON.parse(readFileSync(packageJsonPath, "utf8"));
99
+ } catch {
100
+ return {};
101
+ }
102
+ }
103
+ function readCreatorVersion(creatorConfigPath) {
104
+ try {
105
+ const creatorConfig = JSON.parse(readFileSync(creatorConfigPath, "utf8"));
106
+ return creatorConfig.creator?.version ?? "unknown";
107
+ } catch {
108
+ return "unknown";
109
+ }
110
+ }
111
+ function collectDependencyVersions(packageJson) {
112
+ return new Map([
113
+ ...Object.entries(packageJson.dependencies ?? {}),
114
+ ...Object.entries(packageJson.devDependencies ?? {}),
115
+ ...Object.entries(packageJson.peerDependencies ?? {}),
116
+ ...Object.entries(packageJson.optionalDependencies ?? {})
117
+ ]);
118
+ }
119
+ function inferCocosSubkind(root, version) {
120
+ const majorVersion = Number.parseInt(version.split(".")[0] ?? "", 10);
121
+ if (majorVersion === 2) {
122
+ return "javascript-traditional";
123
+ }
124
+ if (majorVersion >= 3) {
125
+ return "typescript-component";
126
+ }
127
+ return existsSync(join(root, "tsconfig.json")) ? "typescript-component" : "javascript-traditional";
128
+ }
129
+ function inferPackageSubkind(kind) {
130
+ switch (kind) {
131
+ case "next":
132
+ return "next-application";
133
+ case "vite":
134
+ return "vite-application";
135
+ case "react":
136
+ return "react-application";
137
+ case "vue":
138
+ return "vue-application";
139
+ default:
140
+ return "unknown";
141
+ }
142
+ }
143
+ function determinePackageConfidence(kind, deps, root) {
144
+ const coPackages = collectCoPackages(deps, kind, root);
145
+ return coPackages.length > 0 ? "HIGH" : "MEDIUM";
146
+ }
147
+ function collectCoPackages(deps, kind, root) {
148
+ const expectedPackagesByFramework = {
149
+ next: ["react", "react-dom", "typescript"],
150
+ vite: ["@vitejs/plugin-react", "@vitejs/plugin-vue", "typescript", "react", "vue"],
151
+ react: ["react-dom", "@types/react", "@types/react-dom"],
152
+ vue: ["@vitejs/plugin-vue", "typescript"],
153
+ "cocos-creator": ["typescript"]
154
+ };
155
+ const expectedProjectFilesByFramework = {
156
+ next: ["next.config.js", "next.config.mjs", "next.config.ts", "tsconfig.json"],
157
+ vite: ["vite.config.js", "vite.config.mjs", "vite.config.ts", "tsconfig.json"],
158
+ react: ["tsconfig.json"],
159
+ vue: ["vue.config.js", "vite.config.ts", "tsconfig.json"],
160
+ "cocos-creator": ["project.config.json", "tsconfig.json"]
161
+ };
162
+ return [
163
+ ...compactStrings((expectedPackagesByFramework[kind] ?? []).map((packageName) => deps.has(packageName) ? packageName : null)),
164
+ ...collectProjectFileEvidence(root, expectedProjectFilesByFramework[kind] ?? [])
165
+ ];
166
+ }
167
+ function collectProjectFileEvidence(root, relativePaths) {
168
+ return relativePaths.filter((relativePath) => existsSync(join(root, relativePath)));
169
+ }
170
+ function compactStrings(values) {
171
+ return [...new Set(values.filter((value) => value !== null && value !== void 0 && value.length > 0))];
172
+ }
173
+
174
+ export {
175
+ detectFramework
176
+ };