@dforge-core/dforge-mcp 0.2.44 → 0.2.46-next

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +3 -2
  3. package/dist/server.js +109 -84
  4. package/package.json +48 -46
  5. package/resources/docs/conventions.md +17 -14
  6. package/resources/docs/dsl-reference.md +15 -5
  7. package/resources/schemas/entity.schema.json +1 -1
  8. package/resources/schemas/registers.schema.json +8 -3
  9. package/scripts/install-skills.mjs +1 -0
  10. package/skills/dforge-1c-import/SKILL.md +374 -0
  11. package/skills/dforge-1c-import/references/environment.md +97 -0
  12. package/skills/dforge-1c-import/references/loading.md +161 -0
  13. package/skills/dforge-1c-import/references/measurements.md +210 -0
  14. package/skills/dforge-1c-import/references/print-forms.md +148 -0
  15. package/skills/dforge-1c-import/scripts/1c.py +178 -0
  16. package/skills/dforge-1c-import/scripts/README.md +110 -0
  17. package/skills/dforge-1c-import/scripts/lib/analyze.py +83 -0
  18. package/skills/dforge-1c-import/scripts/lib/attrs.py +278 -0
  19. package/skills/dforge-1c-import/scripts/lib/autospec.py +964 -0
  20. package/skills/dforge-1c-import/scripts/lib/binding.py +257 -0
  21. package/skills/dforge-1c-import/scripts/lib/bsl.py +811 -0
  22. package/skills/dforge-1c-import/scripts/lib/callgraph.py +717 -0
  23. package/skills/dforge-1c-import/scripts/lib/cli.py +53 -0
  24. package/skills/dforge-1c-import/scripts/lib/columnmap.py +346 -0
  25. package/skills/dforge-1c-import/scripts/lib/coverage.py +242 -0
  26. package/skills/dforge-1c-import/scripts/lib/custom.py +83 -0
  27. package/skills/dforge-1c-import/scripts/lib/delta.py +741 -0
  28. package/skills/dforge-1c-import/scripts/lib/deps.py +136 -0
  29. package/skills/dforge-1c-import/scripts/lib/docusage.py +105 -0
  30. package/skills/dforge-1c-import/scripts/lib/dump1c.py +251 -0
  31. package/skills/dforge-1c-import/scripts/lib/explore.py +55 -0
  32. package/skills/dforge-1c-import/scripts/lib/external.py +201 -0
  33. package/skills/dforge-1c-import/scripts/lib/extract_code.py +251 -0
  34. package/skills/dforge-1c-import/scripts/lib/forms.py +239 -0
  35. package/skills/dforge-1c-import/scripts/lib/genmodule.py +1432 -0
  36. package/skills/dforge-1c-import/scripts/lib/handlers.py +202 -0
  37. package/skills/dforge-1c-import/scripts/lib/handmap.py +142 -0
  38. package/skills/dforge-1c-import/scripts/lib/i18n_coverage.py +87 -0
  39. package/skills/dforge-1c-import/scripts/lib/idioms.py +273 -0
  40. package/skills/dforge-1c-import/scripts/lib/integrity.py +264 -0
  41. package/skills/dforge-1c-import/scripts/lib/inventory.py +535 -0
  42. package/skills/dforge-1c-import/scripts/lib/layers.py +106 -0
  43. package/skills/dforge-1c-import/scripts/lib/meta.py +315 -0
  44. package/skills/dforge-1c-import/scripts/lib/migrate.py +1299 -0
  45. package/skills/dforge-1c-import/scripts/lib/mxl.py +368 -0
  46. package/skills/dforge-1c-import/scripts/lib/naming.py +251 -0
  47. package/skills/dforge-1c-import/scripts/lib/paths.py +90 -0
  48. package/skills/dforge-1c-import/scripts/lib/peek.py +70 -0
  49. package/skills/dforge-1c-import/scripts/lib/posted.py +218 -0
  50. package/skills/dforge-1c-import/scripts/lib/posting.py +655 -0
  51. package/skills/dforge-1c-import/scripts/lib/printgen.py +239 -0
  52. package/skills/dforge-1c-import/scripts/lib/rebuild.py +194 -0
  53. package/skills/dforge-1c-import/scripts/lib/recorded.py +170 -0
  54. package/skills/dforge-1c-import/scripts/lib/recorders.py +94 -0
  55. package/skills/dforge-1c-import/scripts/lib/reftargets.py +95 -0
  56. package/skills/dforge-1c-import/scripts/lib/reportgen.py +157 -0
  57. package/skills/dforge-1c-import/scripts/lib/roles.py +33 -0
  58. package/skills/dforge-1c-import/scripts/lib/seedstate.py +488 -0
  59. package/skills/dforge-1c-import/scripts/lib/sizing.py +184 -0
  60. package/skills/dforge-1c-import/scripts/lib/spec.py +400 -0
  61. package/skills/dforge-1c-import/scripts/lib/statements.py +774 -0
  62. package/skills/dforge-1c-import/scripts/lib/subsystems.py +480 -0
  63. package/skills/dforge-1c-import/scripts/lib/terms.py +451 -0
  64. package/skills/dforge-1c-import/scripts/lib/terms_uk.py +555 -0
  65. package/skills/dforge-1c-import/scripts/lib/totals.py +387 -0
  66. package/skills/dforge-1c-import/scripts/lib/typemap.py +73 -0
  67. package/skills/dforge-1c-import/scripts/lib/unpack.py +136 -0
  68. package/skills/dforge-1c-import/scripts/lib/validate.py +508 -0
  69. package/skills/dforge-1c-import/scripts/project-template/contours.py +47 -0
  70. package/skills/dforge-1c-import/scripts/project-template/sources.py +14 -0
  71. package/skills/dforge-mcp-author/SKILL.md +8 -2
  72. package/skills/dforge-mcp-author/examples/column-security/entities/product.json +1 -1
  73. package/skills/dforge-mcp-author/examples/column-security/manifest.json +0 -1
  74. package/skills/dforge-mcp-author/examples/column-security/security/roles.json +2 -2
  75. package/skills/dforge-mcp-author/references/action-dsl.md +20 -7
  76. package/skills/dforge-mcp-author/references/conventions.md +7 -1
  77. package/skills/dforge-mcp-author/references/field-types.md +3 -2
  78. package/skills/dforge-mcp-author/references/formulas.md +5 -1
  79. package/skills/dforge-mcp-author/references/print-templates.md +5 -2
  80. package/skills/dforge-mcp-author/references/queries.md +1 -1
  81. package/skills/dforge-mcp-author/references/registers.md +18 -3
  82. package/skills/dforge-mcp-author/references/reports.md +4 -1
  83. package/skills/dforge-mcp-author/references/schema-import.md +1 -1
  84. package/skills/dforge-mcp-author/references/security.md +1 -1
  85. package/skills/dforge-mcp-author/references/translations.md +38 -15
  86. package/skills/dforge-mcp-author/references/validation-checklist.md +2 -3
  87. package/skills/dforge-module-build/SKILL.md +10 -1
  88. package/skills/dforge-module-ship/SKILL.md +31 -3
  89. package/skills/dforge-mcp-author/examples/column-security/translations/en-US.json +0 -35
package/CHANGELOG.md CHANGED
@@ -4,6 +4,79 @@ All notable changes to `@dforge-core/dforge-mcp`. This project uses semver-ish
4
4
  `0.1.0-rc.N` pre-release tags; the published version is set at publish time via
5
5
  the release workflow, so committed `package.json` versions are placeholders.
6
6
 
7
+ ## Unreleased
8
+
9
+ **`dforge-1c-import`: porting a 1C infobase.** A fifth skill. It carries the decisions a 1C port turns on, each with the
10
+ measurement that settles it: a register's kind, where a posting takes each value, what to carry, physical columns through
11
+ DBNames, vendor against client. It adds `references/loading.md` (copying the rows: swapped uuid halves, the empty reference
12
+ and the empty date as values in register keys and periods, the year offset, enums by `_EnumOrder`, composite references,
13
+ register state, triggers, dangling references, verification) and `references/print-forms.md` (MXL templates, the print
14
+ procedure, `Строка`/`Формат` in SQL, reproducing 1C's slips, datasets as queries). `scripts/` ships the Python toolkit
15
+ from a real port: it reads the configuration and DBNames from MS SQL without 1C, generates and validates modules, copies and
16
+ reconciles the rows, checks postings and reads print templates, pointed at a port by `DF1C_ROOT` / `DF1C_PROJECT` and
17
+ started from `scripts/project-template/`. `install-skills` installs it with the other four.
18
+
19
+ ## 0.2.45 — 2026-10-25
20
+
21
+ **English is the base locale, and it lives in the definitions.** Install never reads
22
+ `translations/en.json` / `en-*.json`, so the English file the tools wrote was dead weight: its labels
23
+ were never applied, and the English UI kept showing whatever the definitions said. `dforge_module_create`
24
+ (preset `full`) no longer writes `translations/en-US.json`; `dforge_translation_sync` defaults to the
25
+ `supportedLocales` entries only, refuses an English locale, and warns about a leftover English file;
26
+ `dforge_module_validate` warns about one instead of requiring role labels in it; `dforge_module_plan`
27
+ defaults to no extra locales. The sync seeds a setting from its `description` and a view, menu, folder or
28
+ action from `label`, else `description` — what install shows. `references/translations.md` gains the
29
+ table of where each object's English text lives, and the column-security example drops its
30
+ `en-US.json` for short labels in the definitions.
31
+
32
+ **File map keys use `/` on Windows too.** Every tool that builds a `files` key from a path (field,
33
+ entity, view, menu, action, role-right, seed, diagram and translation tools, the refactors) returned
34
+ `translations\de-DE.json`-style keys on Windows; they now return `translations/de-DE.json` on every OS.
35
+
36
+ **DSL builtins.** `attachText(content, fileName, encoding?, mimeType?)` / `attachBase64(base64, fileName, mimeType?)` build a file for `sendEmail()`'s fourth argument, keeping its own type (an `.ics` as `text/calendar`); a scheduled job can attach them, unlike a `printPdf()` file. Follows metadata 0.0.45; `dforge_action_check` knows both. **Needs `@dforge-core/dforge-cli` with them** (0.2.36).
37
+
38
+ **Schemas follow metadata 0.0.46.** `dforge://schema/registers` accepts `importedKeyField` on an accounting register; `dforge://schema/entity` accepts the `filesize` field type.
39
+
40
+ **Registers and actions docs.** `getAccountBalance` / `getAccountTurnover` take `exclude` as one document or an array, and an accounting register's `importedKeyField` leaves out postings loaded beside a document. An action in `ui/actions.json` can ship `paramSets` (saved parameter values; a `=` formula or a template reads the record) and a `previewTemplate` (a PDF beside the run dialog's params). **Needs `@dforge-core/dforge-cli` 0.2.36.**
41
+
42
+ **DSL docs.** `[owner_id].[email]` / `[owner_id].[full_name]` read the user behind a `user` column (platform #1499); `dforge://reference/dsl` says so.
43
+
44
+ **Registers reference.** Which kind folds rows per dimension tuple (only `balance`); the account-read
45
+ `options` in the reads table; the balance at a document's moment within one second (`before` plus
46
+ `exclude`, the caller ordering what the platform does not); `importedKeyField` marked as being for a
47
+ history loaded from another system; how long a cache rebuild at install can take. Common mistakes gain an
48
+ empty dimension (no `allowEmpty` on any kind; `=COALESCE(...)` or `skipPostingWhenEmpty`), `headerMap`
49
+ taking columns only and why each must be locked, one constant for two resources, a month missing from the
50
+ period calendar counting as closed (`DF003`), and the `registers/column-not-numeric` warning. The engine
51
+ writes an analytic slot's `entity` as `module.entity` (platform #1544), and a loader writes the same; a
52
+ filter matches either spelling, `groupBy` splits old bare slots from new ones until a migration and
53
+ `rebuild-totals`.
54
+
55
+ **DSL entity codes.** A data built-in's `module.entity` is looked up by module code, hyphen included, and
56
+ otherwise as a schema (platform #1543), so `insert('cdy-tax.x', …)` and `insert('cdy_tax.x', …)` both
57
+ work. The conventions doc notes that install refuses a schema or code another module already holds.
58
+ Roles and settings are translated (the conventions doc called roles ignored); a print template's name is
59
+ its `description`.
60
+
61
+ **DSL, formulas, fields.** An inline action stops after 10 seconds; an assignment inside a one-line block
62
+ fails; indexing after a call or an index compiles since platform #1537. `TODAY()` / `NOW()` follow the
63
+ request's time zone, a `datetime` column may pin `params.timeZone`, a number-literal formula becomes a
64
+ column `DEFAULT`, and a bare `$[setting]` formula fills a column an insert leaves out. A zero divisor
65
+ fails wherever a formula runs in SQL.
66
+
67
+ **Reports and print.** A stored procedure's result-column `baseDatatypeCd` codes (`timestamp`, not
68
+ `datetime`); `ROWS 1` on a one-row table function, since install's `SET search_path` stops inlining; every
69
+ `logic/reports/*.sql` runs at every install (and "must be owner of function"); casting columns for
70
+ `RETURN QUERY`. Print: `%B` prints English names, `format_date` returns a string, timestamps follow the
71
+ column's or the request's zone, Scriban's `blank` on a date, stray spaces from an empty reference hop.
72
+
73
+ **Seed, demo and ship.** Seed rows are re-applied on every install and ship in the package (fictional
74
+ data only); `demo-data/` inserts but does not post, so an accounting demo runs the module's actions; a
75
+ same-version install is skipped yet reports success. `dforge-module-ship` covers an install blocked by an
76
+ open transaction (`pg_stat_activity`), a client timeout during a cache rebuild, and the local dev stack
77
+ (restart `dforge-api`, the `<tenant>._.localhost` URL, an expired CLI token, a platform binary behind the
78
+ tenant). Option labels: plain English in the definition, translated as strings.
79
+
7
80
  ## 0.2.44 — 2026-10-21
8
81
 
9
82
  **Schemas follow metadata 0.0.44.** `dforge://schema/translations` describes `storedProcedures`
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP server for dForge module authoring. Exposes 35 composable tools and the canonical schemas so AI agents (Claude Code, Cursor, Zed, etc.) can drive the full module lifecycle — scaffold → entities → actions → views → security → install — through structured tool calls instead of free-form JSON generation.
4
4
 
5
- Ships with **four Claude Skills** (`skills/`): a router plus one skill per lifecycle stage — design (Phase 0), build (Phases 1-5), and ship (Phase 6). The router directory carries 25 reference files (field types, flags, traits, formulas, DSL, security, etc.) and two annotated example modules.
5
+ Ships with **five Claude Skills** (`skills/`): a router plus one skill per lifecycle stage — design (Phase 0), build (Phases 1-5), and ship (Phase 6) — and `dforge-1c-import` for porting a 1C infobase, with its Python toolkit. The router directory carries 25 reference files (field types, flags, traits, formulas, DSL, security, etc.) and two annotated example modules.
6
6
 
7
7
  **New here?** Start with **[docs/creating-modules.md](docs/creating-modules.md)** — three ways to scaffold a module (terminal CLI, VS Code sidebar, AI wizard) and when to pick each.
8
8
 
@@ -143,7 +143,7 @@ The field/entity tools validate against the `@dforge-core/metadata` registry: an
143
143
  | Tool | Behavior |
144
144
  |---|---|
145
145
  | `dforge_setting_add` | Configurable module-level setting |
146
- | `dforge_translation_sync` | Generate/refresh `translations/<locale>.json` from the module's own contents. Never overwrites existing translated text; fills the role labels that are **completeness-enforced at install**. Defaults to en-US + every `supportedLocales` entry |
146
+ | `dforge_translation_sync` | Generate/refresh `translations/<locale>.json` from the module's own contents. Never overwrites existing translated text; fills the role labels that are **completeness-enforced at install**. Defaults to every `supportedLocales` entry; writes no English file (install skips `en*.json` — English lives in the definitions) |
147
147
  | `dforge_seed_add` | Write a seed file for one entity. Enforces numeric `{entity}_id` PKs, parent-before-child load order, `audit-full` System-user columns, and FKs that point at seeded parents |
148
148
 
149
149
  **Security (Phase 5)**
@@ -206,6 +206,7 @@ Authoring guidance ships as **four** skills, split by lifecycle stage. Each stag
206
206
  | `dforge-module-design` | 0a–0d | Identity, intake guardrails, design doc, pre-scaffold validation. Ends at `readyToScaffold: true`. |
207
207
  | `dforge-module-build` | 1–5 | Entities, behavior, views/menus, polish, security. Loading-policy table, core-rules cheat sheet, backtrack protocol. |
208
208
  | `dforge-module-ship` | 6 | Validate → pre-pack review → version audit → pack → install-fix loop. |
209
+ | `dforge-1c-import` | — | Porting a 1C:Enterprise infobase: register kinds, posting bindings, what to carry, the row copy and its conversions, print forms. `scripts/` holds the Python toolkit (read the infobase from MS SQL without 1C, generate modules, load, reconcile, check postings, read MXL templates). |
209
210
 
210
211
  The handoff is deterministic rather than vibes-based: `dforge_module_plan({ action: "check" })` returns `currentPhase` **and** `nextSkill`, derived from the Phase 0 artifacts, the phase ledger in `docs/phase.json`, and evidence read from the module itself (entities without fields, entities without a view, entities without a Select grant).
211
212
 
package/dist/server.js CHANGED
@@ -31002,7 +31002,7 @@ var import_node_crypto = require("crypto");
31002
31002
  var fs2 = __toESM(require("fs"));
31003
31003
  var path2 = __toESM(require("path"));
31004
31004
 
31005
- // node_modules/.pnpm/@dforge-core+dforge-cli@0.2.35/node_modules/@dforge-core/dforge-cli/dist/lib.mjs
31005
+ // node_modules/.pnpm/@dforge-core+dforge-cli@0.2.36/node_modules/@dforge-core/dforge-cli/dist/lib.mjs
31006
31006
  function buildManifest(opts, moduleId) {
31007
31007
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
31008
31008
  const manifest = {
@@ -31132,53 +31132,6 @@ function buildActions() {
31132
31132
  function buildSettings() {
31133
31133
  return {};
31134
31134
  }
31135
- function buildTranslations(opts) {
31136
- const entities = {};
31137
- for (const e of opts.entities) {
31138
- const fields = {
31139
- // identity trait → primary key column
31140
- [`${e.name}_id`]: { label: `${e.label} ID` },
31141
- [NAME_FIELD]: { label: "Name" }
31142
- };
31143
- if (e.traits === "identity+audit") {
31144
- fields.created_date = { label: "Created Date" };
31145
- fields.last_updated = { label: "Last Updated" };
31146
- }
31147
- const entityEntry = { label: e.label };
31148
- if (opts.description && opts.entities.length === 1) {
31149
- entityEntry.desc = opts.description;
31150
- }
31151
- entityEntry.fields = fields;
31152
- if (e.constraints?.length) {
31153
- entityEntry.constraints = Object.fromEntries(
31154
- e.constraints.map((c) => [c.name, { message: c.message }])
31155
- );
31156
- }
31157
- entities[e.name] = entityEntry;
31158
- }
31159
- return {
31160
- entities,
31161
- folders: {
31162
- // folders.json root folder key is the module code (see buildFolders).
31163
- [opts.code]: { label: opts.displayName }
31164
- },
31165
- roles: {
31166
- // Role labels are completeness-enforced in every locale incl. en-US.
31167
- [`${opts.code}.admin`]: { label: `${opts.displayName} Administrator` }
31168
- },
31169
- views: Object.fromEntries(
31170
- opts.entities.map((e) => [e.name, { label: e.label }])
31171
- ),
31172
- menus: {
31173
- [`${opts.code}_menu`]: {
31174
- label: opts.displayName,
31175
- items: Object.fromEntries(
31176
- opts.entities.map((e) => [e.name, { label: e.label }])
31177
- )
31178
- }
31179
- }
31180
- };
31181
- }
31182
31135
  function buildSeedData(entity) {
31183
31136
  const entityCode = typeof entity === "string" ? entity : entity?.name ?? "";
31184
31137
  return { entityCode, records: [] };
@@ -31290,7 +31243,7 @@ function buildZedTasks() {
31290
31243
  var fs = __toESM(require("fs"));
31291
31244
  var path = __toESM(require("path"));
31292
31245
 
31293
- // node_modules/.pnpm/@dforge-core+metadata@0.0.44/node_modules/@dforge-core/metadata/dist/index.js
31246
+ // node_modules/.pnpm/@dforge-core+metadata@0.0.46/node_modules/@dforge-core/metadata/dist/index.js
31294
31247
  var AggType = {
31295
31248
  Sum: "sum",
31296
31249
  Count: "count",
@@ -31310,6 +31263,7 @@ var fieldTypes = [
31310
31263
  { cd: "number", name: "Number", columnType: "D", baseDatatypeCd: "number", supportedAgg: "SCAM", maxLen: null, precision: null, useStringInFilter: false },
31311
31264
  { cd: "currency", name: "Currency", columnType: "D", baseDatatypeCd: "number", supportedAgg: "SCAM", maxLen: null, precision: 2, useStringInFilter: false },
31312
31265
  { cd: "percent", name: "Percent", columnType: "D", baseDatatypeCd: "number", supportedAgg: "CAM", maxLen: null, precision: 2, useStringInFilter: false },
31266
+ { cd: "filesize", name: "File Size", columnType: "D", baseDatatypeCd: "number", supportedAgg: "SCAM", maxLen: null, precision: null, useStringInFilter: false },
31313
31267
  { cd: "checkbox", name: "Checkbox", columnType: "D", baseDatatypeCd: "bool", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
31314
31268
  { cd: "date", name: "Date", columnType: "D", baseDatatypeCd: "date", supportedAgg: "CM", maxLen: null, precision: null, useStringInFilter: false },
31315
31269
  { cd: "datetime", name: "Date & Time", columnType: "D", baseDatatypeCd: "timestamp", supportedAgg: "CM", maxLen: null, precision: null, useStringInFilter: false },
@@ -31509,7 +31463,8 @@ var NUMERIC_TOTAL = 18;
31509
31463
  var TEXT_BACKED = /* @__PURE__ */ new Set(["textarea", "markdown", "richtext", "code"]);
31510
31464
  var DB_DATATYPE_OVERRIDE = {
31511
31465
  file: "jsonb",
31512
- image: "jsonb"
31466
+ image: "jsonb",
31467
+ filesize: "int8"
31513
31468
  };
31514
31469
  function deriveDbDatatype(fieldTypeCd, options = {}) {
31515
31470
  const ft = getFieldType(fieldTypeCd);
@@ -31541,7 +31496,8 @@ var ACCOUNTING_PROPERTIES = [
31541
31496
  "creditAccount",
31542
31497
  "debitAnalyticFields",
31543
31498
  "creditAnalyticFields",
31544
- "sideDimensions"
31499
+ "sideDimensions",
31500
+ "importedKeyField"
31545
31501
  ];
31546
31502
  var BALANCE_PROPERTIES = ["allowNegative", "autoCreate"];
31547
31503
  var REFUSED = {
@@ -31550,6 +31506,40 @@ var REFUSED = {
31550
31506
  info: ["journal", "journalDirection", "journalDate", ...ACCOUNTING_PROPERTIES, "totals", ...BALANCE_PROPERTIES],
31551
31507
  accounting: ["entity", ...INFO_PROPERTIES, "journalDirection", ...BALANCE_PROPERTIES]
31552
31508
  };
31509
+ var SUMMED = ["balance", "turnover", "accounting"];
31510
+ var slot = (path21, on, what, summed = false) => ({
31511
+ path: path21.split("."),
31512
+ on,
31513
+ what,
31514
+ ...summed ? { numericFor: SUMMED } : {}
31515
+ });
31516
+ var REGISTER_COLUMN_SLOTS = [
31517
+ slot("journalDirection", "journal", "Journal direction column"),
31518
+ slot("journalDate", "journal", "Journal date column"),
31519
+ slot("period", "entity", "Period column"),
31520
+ slot("recorder", "entity", "Recorder column"),
31521
+ slot("dimensions.*", "carrier", "Dimension"),
31522
+ slot("resources.*.field", "carrier", "Resource '{0}' column", true),
31523
+ slot("resources.*.debitField", "carrier", "Resource '{0}' column", true),
31524
+ slot("resources.*.creditField", "carrier", "Resource '{0}' column", true),
31525
+ slot("account", "journal", "Account column"),
31526
+ slot("debitAccount", "journal", "Debit account column"),
31527
+ slot("creditAccount", "journal", "Credit account column"),
31528
+ slot("analyticFields.*", "journal", "Analytic slot column"),
31529
+ slot("debitAnalyticFields.*", "journal", "Analytic slot column"),
31530
+ slot("creditAnalyticFields.*", "journal", "Analytic slot column"),
31531
+ slot("sideDimensions.*.debitField", "journal", "Dimension '{0}' debit column"),
31532
+ slot("sideDimensions.*.creditField", "journal", "Dimension '{0}' credit column"),
31533
+ slot("composition.account", "composition", "Composition 'account' column"),
31534
+ slot("composition.slot", "composition", "Composition 'slot' column"),
31535
+ slot("composition.analytic", "composition", "Composition 'analytic' column"),
31536
+ slot("totals.periodField", "totals", "Totals period column"),
31537
+ slot("totals.accountField", "totals", "Totals account column"),
31538
+ slot("totals.resources.*.inField", "totals", "Totals '{0}' inField", true),
31539
+ slot("totals.resources.*.outField", "totals", "Totals '{0}' outField", true),
31540
+ slot("totals.resources.*.debitField", "totals", "Totals '{0}' debitField", true),
31541
+ slot("totals.resources.*.creditField", "totals", "Totals '{0}' creditField", true)
31542
+ ];
31553
31543
 
31554
31544
  // src/tools/_helpers.ts
31555
31545
  function modulePaths(moduleDir) {
@@ -31648,7 +31638,7 @@ function jsonText(obj) {
31648
31638
  return JSON.stringify(obj, null, " ") + "\n";
31649
31639
  }
31650
31640
  function rel(root, abs) {
31651
- return path.relative(root, abs);
31641
+ return path.relative(root, abs).split(path.sep).join("/");
31652
31642
  }
31653
31643
  function loadManifest(moduleDir) {
31654
31644
  const paths = modulePaths(moduleDir);
@@ -31955,7 +31945,7 @@ var createModuleSchema = {
31955
31945
  version: external_exports.string().regex(/^\d+\.\d+\.\d+(-[\w.]+)?$/).default("0.1.0"),
31956
31946
  dbSchemaVersion: external_exports.string().regex(/^\d+\.\d+\.\d+(-[\w.]+)?$/).default("0.0.1"),
31957
31947
  dependencies: external_exports.array(external_exports.string()).default([]).describe("Other modules this one requires, by code (e.g. ['parties']). Leave empty unless it genuinely depends on another module. Do NOT list 'admin' or 'metadata' \u2014 they're system modules present in every tenant, so depending on them is redundant (the check always passes and never affects install order)."),
31958
- preset: external_exports.enum(["minimal", "minimal-plus", "full"]).default("minimal").describe("'minimal' = manifest+entity+UI+role only. 'full' = also settings/translations/seed-data/logic stubs. 'minimal-plus' behaves like 'minimal' here since MCP can't loop; pass all entities up-front."),
31948
+ preset: external_exports.enum(["minimal", "minimal-plus", "full"]).default("minimal").describe("'minimal' = manifest+entity+UI+role only. 'full' = also settings/seed-data/logic stubs. No translation file: English lives in the definitions, other locales come from dforge_translation_sync. 'minimal-plus' behaves like 'minimal' here since MCP can't loop; pass all entities up-front."),
31959
31949
  entities: external_exports.array(
31960
31950
  external_exports.object({
31961
31951
  name: external_exports.string().regex(/^[a-z][a-z0-9_]*$/).describe("Entity code, e.g. 'customer'. Lowercase, digits, underscore; first char a letter."),
@@ -32039,7 +32029,6 @@ function createModuleFiles(args) {
32039
32029
  if (buildZedTasks2) write(".zed/tasks.json", buildZedTasks2());
32040
32030
  if (opts.preset === "full") {
32041
32031
  write("settings.json", buildSettings());
32042
- write("translations/en-US.json", buildTranslations(opts));
32043
32032
  for (const e of opts.entities) {
32044
32033
  write(`seed-data/01-${e.name}.json`, buildSeedData());
32045
32034
  }
@@ -32132,9 +32121,9 @@ var planModuleSchema = {
32132
32121
  dependencies: external_exports.array(external_exports.string()).optional().describe(
32133
32122
  "[write_identity] Other dForge module codes this module depends on. admin + metadata are always implicit."
32134
32123
  ),
32135
- locales: external_exports.array(external_exports.string()).optional().describe("[write_identity] Locale codes, e.g. ['en-US', 'de-DE']. Default: ['en-US']."),
32124
+ locales: external_exports.array(external_exports.string()).optional().describe("[write_identity] Locales besides English, e.g. ['uk-UA', 'de-DE'] \u2014 recorded in CLAUDE.md only; declare them in manifest.supportedLocales and run dforge_translation_sync to create the files. English is the base: its text lives in the definitions and needs no file. Default: [] (English only)."),
32136
32125
  preset: external_exports.enum(["minimal", "minimal-plus", "full"]).optional().describe(
32137
- "[write_identity] Scaffold depth: minimal = entities + views + role; full = + settings + translations + seed-data stubs."
32126
+ "[write_identity] Scaffold depth: minimal = entities + views + role; full = + settings + seed-data stubs."
32138
32127
  ),
32139
32128
  // ── write_requirements / write_design fields (Phase 0b / 0c) ────────
32140
32129
  userConfirmed: external_exports.boolean().optional().describe(
@@ -32364,8 +32353,8 @@ function handleCheck(root) {
32364
32353
  `What's the module's display name? (e.g. "Purchase Orders", "HR Leave Requests")`,
32365
32354
  "What code should it use? (snake_case, letters + digits + underscores \u2014 becomes the DB schema name; suggest a default derived from the display name)",
32366
32355
  "Does this module depend on any other dForge modules \u2014 e.g. needing entities from 'crm' or 'parties'? (admin and metadata are platform-implicit; list any others, or say None)",
32367
- "English only, or any other locales the module needs to ship with translations for?",
32368
- "How complete should the initial scaffold be? (minimal = entities + default views + single admin role; minimal-plus = + settings/translation stubs; full = + logic stubs + seed data structure)"
32356
+ "English only, or which other locales should the module ship translations for (e.g. uk-UA)? English is the base and needs no translation file.",
32357
+ "How complete should the initial scaffold be? (minimal = entities + default views + single admin role; minimal-plus = + settings stubs; full = + logic stubs + seed data structure)"
32369
32358
  ],
32370
32359
  writeAction: "Once all answers are collected and reflected back, call dforge_module_plan({ action: 'write_identity', moduleDir, displayName, code, dependencies, locales, preset })."
32371
32360
  };
@@ -32439,11 +32428,12 @@ function handleCheck(root) {
32439
32428
  };
32440
32429
  }
32441
32430
  function handleWriteIdentity(root, args) {
32442
- const { displayName, code: code2, dependencies = [], locales = ["en-US"], preset = "minimal" } = args;
32431
+ const { displayName, code: code2, dependencies = [], preset = "minimal" } = args;
32432
+ const locales = (args.locales ?? []).filter((l) => !/^en(-|$)/i.test(l.trim()));
32443
32433
  if (!displayName) throw new Error("displayName is required for write_identity.");
32444
32434
  if (!code2) throw new Error("code is required for write_identity.");
32445
32435
  const depsText = dependencies.length > 0 ? dependencies.join(", ") : "None";
32446
- const localesText = locales.join(", ");
32436
+ const localesText = ["English (base, in the definitions)", ...locales].join(", ");
32447
32437
  const claudeMd = buildClaudeMd({ root, displayName, code: code2, depsText, localesText, preset });
32448
32438
  return {
32449
32439
  summary: `Phase 0a complete \u2014 CLAUDE.md prepared for module '${code2}'.`,
@@ -34622,7 +34612,7 @@ var path10 = __toESM(require("path"));
34622
34612
  var fs7 = __toESM(require("fs"));
34623
34613
  var path9 = __toESM(require("path"));
34624
34614
 
34625
- // node_modules/.pnpm/@dforge-core+metadata@0.0.44/node_modules/@dforge-core/metadata/dist/dsl/index.js
34615
+ // node_modules/.pnpm/@dforge-core+metadata@0.0.46/node_modules/@dforge-core/metadata/dist/dsl/index.js
34626
34616
  var PUNCT3 = ["===", "!=="];
34627
34617
  var PUNCT2 = [
34628
34618
  "==",
@@ -35476,7 +35466,7 @@ var BUILTINS = [
35476
35466
  name: "sendEmail",
35477
35467
  signature: "sendEmail(to, subjectOrTemplate, dataOrBody?, attachments?)",
35478
35468
  returns: "void",
35479
- doc: "Queue an outbound email. Three arguments, and the **second** picks the mode: `sendEmail(to, 'order.shipped', { ref: [code] })` sends a template, `sendEmail(to, 'Your order shipped', '<p>\u2026</p>')` sends raw HTML. A second argument with no spaces that contains `.` or `_` is read as a template code \u2014 so a raw subject like `'Shipped.'` silently becomes one; give a raw subject spaces. The optional **fourth** argument attaches files: a `printPdf()` result, or an array of them (`[a, b]`; pass a single file as itself, because a one-name `[a]` reads as a field reference in the DSL). Only a `printPdf()` result can be attached, and only by the user who generated it (or a workspace admin); at most 5 files and 10 MB in all per email (`Email:MaxAttachments`, `Email:MaxAttachmentBytes`). A file the sender may not attach fails the action. Not available in a scheduled job, whose emails with attachments are not queued."
35469
+ doc: "Queue an outbound email. Three arguments, and the **second** picks the mode: `sendEmail(to, 'order.shipped', { ref: [code] })` sends a template, `sendEmail(to, 'Your order shipped', '<p>\u2026</p>')` sends raw HTML. A second argument with no spaces that contains `.` or `_` is read as a template code \u2014 so a raw subject like `'Shipped.'` silently becomes one; give a raw subject spaces. The optional **fourth** argument attaches files: a `printPdf()`, `attachText()` or `attachBase64()` result, or an array of them (`[a, b]`; pass a single file as itself, because a one-name `[a]` reads as a field reference in the DSL). A `printPdf()` file can be attached only by the user who generated it (or a workspace admin) and not in a scheduled job; a file built with `attachText()` / `attachBase64()` has no owner and works everywhere, a scheduled job included. At most 5 files and 10 MB in all per email (`Email:MaxAttachments`, `Email:MaxAttachmentBytes`). A file the sender may not attach fails the action."
35480
35470
  },
35481
35471
  // External calls
35482
35472
  {
@@ -35529,6 +35519,18 @@ var BUILTINS = [
35529
35519
  returns: "string",
35530
35520
  doc: "`downloadText()` for bytes: decodes `base64` and hands it to the user as `fileName` (`application/octet-stream` unless `mimeType` says otherwise). Returns the signed download URL."
35531
35521
  },
35522
+ {
35523
+ name: "attachText",
35524
+ signature: "attachText(content, fileName, encoding?, mimeType?)",
35525
+ returns: "object",
35526
+ doc: "Build a text file to attach to an email: encodes `content` in `encoding` (`utf-8` by default) and returns a descriptor for `sendEmail()`'s fourth argument, e.g. `sendEmail(to, 'Interview', body, attachText(ics, 'invite.ics', 'utf-8', 'text/calendar'))`. The file keeps `mimeType` (`text/plain` by default). It is the script's own output, so no user's rights are involved and it works in a scheduled job. Only the emails of this run can name it. Max 10 MB per file and 10 files per run, shared with `downloadText()` / `downloadBase64()`."
35527
+ },
35528
+ {
35529
+ name: "attachBase64",
35530
+ signature: "attachBase64(base64, fileName, mimeType?)",
35531
+ returns: "object",
35532
+ doc: "`attachText()` for bytes given as base64 (`application/octet-stream` unless `mimeType` says otherwise)."
35533
+ },
35532
35534
  {
35533
35535
  name: "getSetting",
35534
35536
  signature: "getSetting(settingCd)",
@@ -36389,10 +36391,17 @@ function checkConstraintTranslations(ctx) {
36389
36391
  function checkTranslationCompleteness(ctx) {
36390
36392
  const { paths, err, warn, roles, supportedLocales } = ctx;
36391
36393
  const roleCodes = Object.keys(roles);
36392
- const localeFiles = fs7.existsSync(paths.translationsDir) ? fs7.readdirSync(paths.translationsDir).filter((f) => f.toLowerCase().endsWith(".json")).sort() : [];
36394
+ const allFiles = fs7.existsSync(paths.translationsDir) ? fs7.readdirSync(paths.translationsDir).filter((f) => f.toLowerCase().endsWith(".json")).sort() : [];
36395
+ const localeFiles = allFiles.filter((f) => !isEnglishLocale(f.slice(0, -5)));
36396
+ for (const file2 of allFiles.filter((f) => isEnglishLocale(f.slice(0, -5)))) {
36397
+ warn(
36398
+ `translations/${file2}`,
36399
+ "install never reads an English translation file \u2014 the English UI shows the definitions' own text (an entity's or field's `description`, a menu's, action's or report's `label`, a role's or setting's `description`). Move any wording you want from this file into the definitions, then delete it."
36400
+ );
36401
+ }
36393
36402
  for (const raw of supportedLocales) {
36394
36403
  const locale = raw.trim();
36395
- if (!locale) continue;
36404
+ if (!locale || isEnglishLocale(locale)) continue;
36396
36405
  if (!resolveTranslationFile(paths.translationsDir, locale)) {
36397
36406
  err(
36398
36407
  "translations",
@@ -36400,12 +36409,6 @@ function checkTranslationCompleteness(ctx) {
36400
36409
  );
36401
36410
  }
36402
36411
  }
36403
- if (roleCodes.length > 0 && localeFiles.length === 0) {
36404
- warn(
36405
- "translations",
36406
- `no translations/ files \u2014 ship at least translations/en-US.json with a roles block (a 'label' for each of: ${roleCodes.join(", ")}); role labels are completeness-enforced at install.`
36407
- );
36408
- }
36409
36412
  for (const file2 of localeFiles) {
36410
36413
  let tx;
36411
36414
  try {
@@ -36422,7 +36425,7 @@ function checkTranslationCompleteness(ctx) {
36422
36425
  if (missing.length > 0) {
36423
36426
  err(
36424
36427
  `translations/${file2}`,
36425
- `missing roles.<code>.label for: ${missing.join(", ")} \u2014 completeness is enforced in every locale (including en-US); install fails with "Label for role '<code>'."`
36428
+ `missing roles.<code>.label for: ${missing.join(", ")} \u2014 completeness is enforced in every non-English locale file; install fails with "Label for role '<code>'."`
36426
36429
  );
36427
36430
  }
36428
36431
  }
@@ -36891,7 +36894,7 @@ function str(obj, key) {
36891
36894
  var translationSyncSchema = {
36892
36895
  moduleDir: external_exports.string().describe("Path to the module root."),
36893
36896
  locales: external_exports.array(external_exports.string().regex(/^[a-z]{2}(-[A-Z]{2})?$/)).optional().describe(
36894
- "Locales to sync. Default: 'en-US' plus every entry in manifest.supportedLocales \u2014 i.e. exactly the set install requires."
36897
+ "Non-English locales to sync. Default: every entry in manifest.supportedLocales \u2014 exactly the set install requires. An English locale is refused: install never reads translations/en*.json, English text belongs in the definitions."
36895
36898
  ),
36896
36899
  prune: external_exports.boolean().default(false).describe(
36897
36900
  "Remove keys that no longer correspond to anything in the module (e.g. after an entity delete). Off by default \u2014 pruning discards translated text."
@@ -36907,8 +36910,8 @@ function buildSkeleton(moduleDir) {
36907
36910
  `traits.json \u2014 ${traitsError} Its traits can't be overlaid, so the entities that use one would be skipped as declaring an unknown trait and their columns would go unlabelled \u2014 which fails the install. Fix the file and sync again.`
36908
36911
  );
36909
36912
  }
36910
- const push = (section, pathParts, english, required2 = false, legacyParts, nonEnglishOnly = false) => {
36911
- entries.push({ pathParts, english, required: required2, legacyParts, nonEnglishOnly });
36913
+ const push = (section, pathParts, english, required2 = false, legacyParts) => {
36914
+ entries.push({ pathParts, english, required: required2, legacyParts });
36912
36915
  counts[section] = (counts[section] ?? 0) + 1;
36913
36916
  };
36914
36917
  for (const [name, relPath] of Object.entries(manifest.entities ?? {})) {
@@ -36990,10 +36993,10 @@ function buildSkeleton(moduleDir) {
36990
36993
  }
36991
36994
  const settings = readJsonOrDefault(paths.settings, {});
36992
36995
  for (const [code2, s] of Object.entries(settings)) {
36993
- push("settings", ["settings", code2, "label"], str(s, "label") ?? str(s, "description") ?? str(s, "desc") ?? titleize(code2));
36996
+ push("settings", ["settings", code2, "label"], str(s, "description") ?? str(s, "label") ?? str(s, "desc") ?? titleize(code2));
36994
36997
  }
36995
36998
  for (const text of actionMessageTexts(paths.logicDir, actions).keys()) {
36996
- push("messages", ["messages", text], text, false, void 0, true);
36999
+ push("messages", ["messages", text], text);
36997
37000
  }
36998
37001
  return { entries, counts };
36999
37002
  }
@@ -37022,9 +37025,20 @@ function pruneTo(existing, keep, trail = []) {
37022
37025
  function translationSync(args) {
37023
37026
  const { paths, manifest } = loadManifest(args.moduleDir);
37024
37027
  const declared = Array.isArray(manifest.supportedLocales) ? manifest.supportedLocales.filter((l) => typeof l === "string") : [];
37025
- const locales = args.locales ?? ["en-US", ...declared];
37028
+ const isEnglish = (l) => l.toLowerCase() === "en" || l.toLowerCase().startsWith("en-");
37029
+ const english = (args.locales ?? []).filter(isEnglish);
37030
+ if (english.length > 0) {
37031
+ throw new Error(
37032
+ `Refusing to sync ${english.join(", ")}: install never reads translations/en*.json. English is the base locale and lives in the definitions \u2014 an entity's or field's \`description\`, a menu's, action's or report's \`label\`, a role's or setting's \`description\`. Edit the text there.`
37033
+ );
37034
+ }
37035
+ const ignored = fs9.existsSync(paths.translationsDir) ? fs9.readdirSync(paths.translationsDir).filter((f) => f.toLowerCase().endsWith(".json") && isEnglish(f.slice(0, -5))) : [];
37036
+ const staleEnglish = ignored.length > 0 ? ` Leftover ${ignored.map((f) => `translations/${f}`).join(", ")} is never read by install \u2014 move any wording you want from it into the definitions, then delete it.` : "";
37037
+ const locales = args.locales ?? declared.filter((l) => !isEnglish(l));
37026
37038
  if (locales.length === 0) {
37027
- throw new Error("No locales to sync. Pass `locales`, or declare them in manifest.supportedLocales.");
37039
+ throw new Error(
37040
+ 'No locales to sync. An English-only module needs no translation files: its English text lives in the definitions. To translate, declare the other locales in manifest.supportedLocales (e.g. ["uk-UA"]).' + staleEnglish
37041
+ );
37028
37042
  }
37029
37043
  const { entries, counts } = buildSkeleton(args.moduleDir);
37030
37044
  if (entries.length === 0) {
@@ -37033,11 +37047,11 @@ function translationSync(args) {
37033
37047
  );
37034
37048
  }
37035
37049
  const keep = new Set(entries.map((e) => compositeKey(...e.pathParts)));
37036
- const isEnglish = (l) => l.toLowerCase() === "en" || l.toLowerCase().startsWith("en-");
37037
37050
  const files = {};
37038
37051
  const report = [];
37039
37052
  let totalAdded = 0;
37040
37053
  let missingRequired = 0;
37054
+ const seeded = [];
37041
37055
  for (const locale of locales) {
37042
37056
  const abs = path12.join(paths.translationsDir, `${locale}.json`);
37043
37057
  let existing = {};
@@ -37051,7 +37065,6 @@ function translationSync(args) {
37051
37065
  const next = args.prune ? pruneTo(existing, keep) : JSON.parse(JSON.stringify(existing));
37052
37066
  let added = 0;
37053
37067
  for (const entry of entries) {
37054
- if (entry.nonEnglishOnly && isEnglish(locale)) continue;
37055
37068
  const current = getIn(next, entry.pathParts);
37056
37069
  if (typeof current === "string" && current.trim() !== "") continue;
37057
37070
  const legacy = entry.legacyParts && getIn(existing, entry.legacyParts);
@@ -37061,10 +37074,21 @@ function translationSync(args) {
37061
37074
  }
37062
37075
  files[rel(paths.root, abs)] = jsonText(next);
37063
37076
  totalAdded += added;
37077
+ if (added > 0) seeded.push(locale);
37064
37078
  report.push(`${locale}: +${added} key(s)${added === 0 ? " (already complete)" : ""}`);
37065
37079
  }
37066
- const nonEnglish = locales.filter((l) => !isEnglish(l));
37067
- const warning = nonEnglish.length > 0 && totalAdded > 0 ? `Newly-added keys in ${nonEnglish.join(", ")} are seeded with the ENGLISH source text so the file is installable immediately \u2014 they still need real translation. (Untranslated values fall back to en-US at runtime, so this never breaks; it just shows English.)` : void 0;
37080
+ const warnings = [];
37081
+ if (totalAdded > 0) {
37082
+ warnings.push(
37083
+ `Newly-added keys in ${seeded.join(", ")} are seeded with the ENGLISH source text so the file is installable immediately \u2014 they still need real translation.`
37084
+ );
37085
+ }
37086
+ if (ignored.length > 0) {
37087
+ warnings.push(
37088
+ `${ignored.map((f) => `translations/${f}`).join(", ")} is never read by install \u2014 the English UI shows the definitions' own \`description\` / \`label\`. Move any wording you want from it into the definitions, then delete it.`
37089
+ );
37090
+ }
37091
+ const warning = warnings.length > 0 ? warnings.join(" ") : void 0;
37068
37092
  const sections = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ");
37069
37093
  return makeResult(
37070
37094
  `Synced ${locales.length} locale file(s) from ${entries.length} translatable keys (${sections}). ${report.join("; ")}.${missingRequired > 0 ? ` Filled ${missingRequired} install-blocking role label(s).` : ""}`,
@@ -38971,7 +38995,8 @@ var resources = [
38971
38995
  reference("registers", "Registers: accumulation (balance / turnover / info) and accounting (classic / compound) registers, registers.json, binding a document from its A / L columns (map, per-side debit/credit objects), and the reads. Load before authoring registers.json or an A / L column."),
38972
38996
  reference("reactions", "Field-change reactions: `reactions` on an entity (formula rules and server `call` rules) that suggest a stored, editable value when the user edits another field; a bridge module's reactions on its host's entity (`reaction:<code>` in deps); dependent references. Load before authoring any reaction."),
38973
38997
  reference("data-migration", "Migrating data from a legacy database."),
38974
- reference("manifest", "manifest.json shape: moduleId UUID, semver, entities map, locale-keyed translations, security block."),
38998
+ reference("manifest", "manifest.json shape: moduleId UUID, semver, entities map, supportedLocales (non-English only), security block."),
38999
+ reference("conventions", "Cross-cutting module rules: naming, structure, translations, upgrade migrations."),
38975
39000
  reference("validation-checklist", "Final pre-pack self-review checklist covering every file type."),
38976
39001
  // ── Canonical example module (skills/.../examples/simple-todo) ────────
38977
39002
  example("manifest.json", "Canonical manifest.json."),
@@ -39334,7 +39359,7 @@ server.registerTool(
39334
39359
  "dforge_translation_sync",
39335
39360
  {
39336
39361
  title: "Sync translation files",
39337
- description: "PHASE 4: Generate or refresh translations/<locale>.json from what the module actually contains \u2014 entity + field labels (including trait-provided columns), views, menus, roles, actions, folders, settings. Existing translated text is NEVER overwritten; missing keys are seeded with the English source so the file is immediately installable. Defaults to en-US plus every manifest.supportedLocales entry \u2014 exactly the set install requires. Run it after any entity/view/role change: role labels are completeness-enforced and a missing one FAILS install.",
39362
+ description: "PHASE 4: Generate or refresh translations/<locale>.json from what the module actually contains \u2014 entity + field labels (including trait-provided columns), views, menus, roles, actions, folders, settings. Existing translated text is NEVER overwritten; missing keys are seeded with the English source so the file is immediately installable. Defaults to every manifest.supportedLocales entry \u2014 exactly the set install requires. Writes no English file: install never reads translations/en*.json, English text lives in the definitions (entity/field/role/setting `description`, menu/action/report `label`). Run it after any entity/view/role change: role labels are completeness-enforced and a missing one FAILS install.",
39338
39363
  inputSchema: { ...translationSyncSchema, ...applyInput }
39339
39364
  },
39340
39365
  envelope(translationSync)
package/package.json CHANGED
@@ -1,47 +1,49 @@
1
1
  {
2
- "name": "@dforge-core/dforge-mcp",
3
- "version": "0.2.44",
4
- "description": "MCP server for dForge module authoring. Exposes 35 scaffold/patch/validate/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
5
- "license": "MIT",
6
- "homepage": "https://github.com/iash44/dForge-core",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/dforge-core/dforge-mcp.git"
10
- },
11
- "bin": {
12
- "dforge-mcp": "dist/server.js",
13
- "dforge-install-skills": "scripts/install-skills.mjs"
14
- },
15
- "files": [
16
- "dist/",
17
- "docs/",
18
- "resources/",
19
- "skills/",
20
- "scripts/install-skills.mjs",
21
- "README.md",
22
- "CHANGELOG.md"
23
- ],
24
- "main": "dist/server.js",
25
- "engines": {
26
- "node": ">=18"
27
- },
28
- "dependencies": {
29
- "@dforge-core/dforge-cli": "^0.2.35",
30
- "@dforge-core/metadata": "^0.0.44",
31
- "@modelcontextprotocol/sdk": "^1.30.0",
32
- "zod": "^4.4.3"
33
- },
34
- "devDependencies": {
35
- "@types/node": "^26.2.0",
36
- "tsup": "^8.5.1",
37
- "typescript": "^6.0.3",
38
- "vitest": "^4.1.10"
39
- },
40
- "scripts": {
41
- "build": "tsup",
42
- "sync-schemas": "node scripts/vendor-schemas.cjs",
43
- "typecheck": "tsc --noEmit",
44
- "test": "vitest run",
45
- "install-skills": "node scripts/install-skills.mjs"
46
- }
47
- }
2
+ "name": "@dforge-core/dforge-mcp",
3
+ "version": "0.2.46-next",
4
+ "description": "MCP server for dForge module authoring. Exposes 35 scaffold/patch/validate/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/iash44/dForge-core",
7
+ "packageManager": "pnpm@12.14.0+sha512.c18ba6d87993d3d164cb5e8948f32b2c06ee854487e06cd8d86781342fb3e5bb4634367034711e86802baa9c37bea906ce1929ad08d70884c97d801df13389a6",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/dforge-core/dforge-mcp.git"
11
+ },
12
+ "bin": {
13
+ "dforge-mcp": "dist/server.js",
14
+ "dforge-install-skills": "scripts/install-skills.mjs"
15
+ },
16
+ "files": [
17
+ "dist/",
18
+ "docs/",
19
+ "resources/",
20
+ "skills/",
21
+ "scripts/install-skills.mjs",
22
+ "README.md",
23
+ "CHANGELOG.md"
24
+ ],
25
+ "main": "dist/server.js",
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "sync-schemas": "node scripts/vendor-schemas.cjs",
32
+ "prepublishOnly": "node scripts/vendor-schemas.cjs && tsup",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "vitest run",
35
+ "install-skills": "node scripts/install-skills.mjs"
36
+ },
37
+ "dependencies": {
38
+ "@dforge-core/dforge-cli": "^0.2.36",
39
+ "@dforge-core/metadata": "^0.0.46",
40
+ "@modelcontextprotocol/sdk": "^1.30.0",
41
+ "zod": "^4.4.3"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^26.2.0",
45
+ "tsup": "^8.5.1",
46
+ "typescript": "^6.0.3",
47
+ "vitest": "^4.1.10"
48
+ }
49
+ }