add-coder 0.3.18 → 0.3.19

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/dist/index.js CHANGED
@@ -331,13 +331,34 @@ async function writeFiles2(projectRoot, files, options = {}) {
331
331
  }
332
332
  function parseSchemaBlocks(content) {
333
333
  const blocks = /* @__PURE__ */ new Map();
334
- const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
335
- let match;
336
- while ((match = blockRegex.exec(content)) !== null) {
337
- const [, type, name, inner] = match;
334
+ const lines = content.split("\n");
335
+ let i = 0;
336
+ while (i < lines.length) {
337
+ const head = lines[i].match(/^(model|enum)\s+(\w+)\s*\{/);
338
+ if (!head) {
339
+ i++;
340
+ continue;
341
+ }
342
+ const type = head[1];
343
+ const name = head[2];
344
+ const bodyLines = [lines[i]];
345
+ let depth = 1;
346
+ let j = i + 1;
347
+ while (j < lines.length && depth > 0) {
348
+ const line = lines[j];
349
+ bodyLines.push(line);
350
+ const commentless = line.split("//")[0];
351
+ for (const ch of commentless) {
352
+ if (ch === "{") depth++;
353
+ else if (ch === "}") depth--;
354
+ }
355
+ j++;
356
+ }
357
+ const body = bodyLines.join("\n");
358
+ const inner = bodyLines.slice(1, -1).join("\n");
338
359
  let fields = [];
339
360
  if (type === "enum") {
340
- fields = inner.split("\n").map((l) => l.replace(/#.*$/, "").trim()).filter((l) => l.length > 0);
361
+ fields = inner.split("\n").map((l) => l.replace(/\/\/.*$/, "").replace(/#.*$/, "").trim()).filter((l) => l.length > 0);
341
362
  } else {
342
363
  const fieldRegex = /^\s*(\w+)\s+(\w+(?:\([^)]*\))?(?:\[\])?\??)/gm;
343
364
  let fm;
@@ -345,7 +366,8 @@ function parseSchemaBlocks(content) {
345
366
  fields.push(`${fm[1]}:${fm[2]}`);
346
367
  }
347
368
  }
348
- blocks.set(`${type}:${name}`, { type, name, body: match[0], fields });
369
+ blocks.set(`${type}:${name}`, { type, name, body, fields });
370
+ i = j;
349
371
  }
350
372
  return blocks;
351
373
  }
@@ -1632,38 +1654,71 @@ function injectMissingModels(targetPath, models) {
1632
1654
  }
1633
1655
  function getBaseFieldLines(basePath, modelName) {
1634
1656
  const content = readFileSync7(basePath, "utf-8");
1635
- const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
1636
- let match;
1637
- while ((match = blockRegex.exec(content)) !== null) {
1638
- if (match[2] !== modelName) continue;
1639
- const fields = {};
1640
- for (const line of match[3].split("\n")) {
1657
+ const blocks = parseSchemaBlocks(content);
1658
+ const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
1659
+ if (!block) return {};
1660
+ const fields = {};
1661
+ for (const line of block.body.split("\n")) {
1662
+ const clean = line.replace(/\/\/.*$/, "").trim();
1663
+ if (!clean) continue;
1664
+ if (block.type === "enum") {
1665
+ if (/^\w+$/.test(clean)) fields[clean] = line.trim();
1666
+ } else {
1641
1667
  const fm = line.match(/^\s*(\w+)\s+/);
1642
1668
  if (fm) fields[fm[1]] = line.trim();
1643
1669
  }
1644
- return fields;
1645
1670
  }
1646
- return {};
1671
+ return fields;
1647
1672
  }
1648
1673
  function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
1649
1674
  const baseFields = getBaseFieldLines(basePath, modelName);
1650
- if (Object.keys(baseFields).length === 0) return 0;
1651
- let content = readFileSync7(targetPath, "utf-8");
1652
- let count = 0;
1675
+ if (Object.keys(baseFields).length === 0) {
1676
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u5728\u57FA\u51C6\u4E2D\u672A\u627E\u5230\u5B57\u6BB5\u5B9A\u4E49\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1677
+ return 0;
1678
+ }
1679
+ const content = readFileSync7(targetPath, "utf-8");
1680
+ const lines = content.split("\n");
1681
+ const blocks = parseSchemaBlocks(content);
1682
+ const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
1683
+ if (!target) {
1684
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A\u76EE\u6807\u4E2D\u4E0D\u5B58\u5728 ${modelName} \u5757\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1685
+ return 0;
1686
+ }
1687
+ const bodyLines = target.body.split("\n");
1688
+ const startIdx = lines.findIndex((l) => l === bodyLines[0]);
1689
+ if (startIdx < 0) {
1690
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A\u65E0\u6CD5\u5B9A\u4F4D ${modelName} \u5757\u4F4D\u7F6E\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1691
+ return 0;
1692
+ }
1693
+ const endIdx = startIdx + bodyLines.length - 1;
1694
+ const existingNames = new Set(
1695
+ target.fields.map((f) => f.split(":")[0])
1696
+ );
1697
+ const newFieldLines = [];
1653
1698
  for (const key of fieldKeys) {
1654
1699
  const fieldName = key.split(":")[0];
1655
1700
  const fieldLine = baseFields[fieldName];
1656
1701
  if (!fieldLine) continue;
1657
- const modelRegex = new RegExp(`(model\\s+${modelName}\\s*\\{)([^}]*?)(\\n\\s*\\})`, "m");
1658
- const m = content.match(modelRegex);
1659
- if (!m) continue;
1660
- if (new RegExp(`^\\s*${fieldName}\\s+`, "m").test(m[2])) continue;
1661
- content = content.replace(modelRegex, `$1$2
1662
- ${fieldLine}$3`);
1663
- count++;
1664
- }
1665
- if (count > 0) writeFileSync5(targetPath, content, "utf-8");
1666
- return count;
1702
+ if (existingNames.has(fieldName)) continue;
1703
+ newFieldLines.push(` ${fieldLine}`);
1704
+ }
1705
+ if (newFieldLines.length === 0) {
1706
+ if (fieldKeys.length > 0) {
1707
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u7684 ${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF08\u53EF\u80FD\u5DF2\u5B58\u5728\u6216\u5B9A\u4E49\u4E0D\u5339\u914D\uFF09`);
1708
+ }
1709
+ return 0;
1710
+ }
1711
+ let insertIdx = endIdx;
1712
+ for (let k = startIdx + 1; k < endIdx; k++) {
1713
+ if (/^\s*@@/.test(lines[k])) insertIdx = k;
1714
+ }
1715
+ const merged = [
1716
+ ...lines.slice(0, insertIdx),
1717
+ ...newFieldLines,
1718
+ ...lines.slice(insertIdx)
1719
+ ];
1720
+ writeFileSync5(targetPath, merged.join("\n"), "utf-8");
1721
+ return newFieldLines.length;
1667
1722
  }
1668
1723
  function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
1669
1724
  const baseFields = getBaseFieldLines(basePath, modelName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-coder",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "private": false,
5
5
  "description": "[codein2027](https://github.com/xiaomingming92/codein2027) - A complete scaffolding for building the ADD programming paradigm, the implementation layer for AI code governance. Core principle: Audit as Infrastructure. Breaks the black-box programming process and cross-session amnesia, evolving programming paradigms into an auditable, traceable, and convergent new era. npx-ready.\n\n[codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架——AI 代码治理的落地方案。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。npx 即用,人人可体验。",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "_version": "0.3.18",
2
+ "_version": "0.3.19",
3
3
  "adapters/claude/hooks/doc-format-guard.sh": "ec59ad3f",
4
4
  "adapters/claude/hooks/lib/common.sh": "4f3e64ad",
5
5
  "adapters/claude/hooks/lib/context-inject.sh": "cfc8c65e",