memgineering 0.23.0 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,32 @@ language the reader wants. The bilingual rule the monorepo applies to
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.23.1] — 2026-09-11
15
+
16
+ ### Fixed
17
+
18
+ - **The rules a hook shows now include the sentence that says what to do.**
19
+ `memgineering guard` quoted each rule's `summary`, which is the note's text
20
+ cut at 200 characters — so a rule long enough to describe a problem before
21
+ prescribing an answer arrived with the answer removed. Measured across 24
22
+ sessions: of six rules planted as global rules, five were cut immediately
23
+ before their prescription, and every session holding one of them walked into
24
+ the trap its own rule described. The hook now quotes the rule as written, and
25
+ when it still has to shorten one it elides the MIDDLE, keeping the opening and
26
+ the closing instruction.
27
+ - **A short list of rules is now read further than a long one.** The width is a
28
+ budget shared across the whole block rather than a fixed cap per line, so two
29
+ standing decisions arrive in full where ten are abbreviated. Ten rules cost
30
+ exactly what ten rules cost before — nothing here prints more than it used to.
31
+
32
+ ### Added
33
+
34
+ - **A hosted brain sends the rule, not just the card.** `POST /rules` gained a
35
+ `text` field carrying the rule as written; a client older than this ignores it
36
+ and keeps rendering `summary`, and a server older than this makes the new
37
+ client fall back to `summary` as well. Deploy the server first to get the
38
+ benefit; neither order breaks anything.
39
+
14
40
  ## [0.23.0] — 2026-09-09
15
41
 
16
42
  ### Added
@@ -2,7 +2,7 @@
2
2
  name: memgineering
3
3
  description: Use whenever the user refers to something they told you before, asks what was decided, tells you something worth keeping, or settles something that should hold next time. The memory lives in their own folder and outlives this session; check it before answering from guesswork, and write to it when you learn something durable.
4
4
  type: skill
5
- version: 0.23.0
5
+ version: 0.23.1
6
6
  ---
7
7
 
8
8
  # memgineering
package/dist/index.js CHANGED
@@ -497,6 +497,52 @@ var init_rule_budget = __esm({
497
497
  }
498
498
  });
499
499
 
500
+ // ../../packages/memory-engine/src/rule-text.ts
501
+ function ruleTextOf(entry) {
502
+ const intro = (entry.chunks ?? []).find((chunk) => chunk.label === null)?.text.trim();
503
+ if (intro !== void 0 && intro !== "") return intro;
504
+ return entry.memory.summary ?? entry.memory.title;
505
+ }
506
+ function ruleWidthFor(count) {
507
+ if (count <= 0) return RULE_WIDTH_MIN;
508
+ const share = Math.floor(RULE_BLOCK_BUDGET / count);
509
+ return Math.min(RULE_WIDTH_MAX, Math.max(RULE_WIDTH_MIN, share));
510
+ }
511
+ function rulePassage(text, max) {
512
+ const flat = text.replace(/\s+/g, " ").trim();
513
+ const chars = [...flat];
514
+ if (chars.length <= max) return flat;
515
+ if (max <= 1) return "\u2026";
516
+ const budget = max - 1;
517
+ if (budget < 2) return `${chars.slice(0, budget).join("")}\u2026`;
518
+ const tailBudget = Math.min(budget - 1, Math.max(1, Math.floor(budget * 0.6)));
519
+ const tail = [...trailingSentences(flat, tailBudget)];
520
+ const head = chars.slice(0, budget - tail.length).join("").trimEnd();
521
+ return `${head}\u2026${tail.join("")}`;
522
+ }
523
+ function trailingSentences(flat, budget) {
524
+ const starts = [...flat.matchAll(/[.!?。!?](?=\s)/g)].map(
525
+ (match) => match.index + match[0].length
526
+ );
527
+ let best = null;
528
+ for (let i = starts.length - 1; i >= 0; i -= 1) {
529
+ const candidate = flat.slice(starts[i]).trim();
530
+ if ([...candidate].length > budget) break;
531
+ if (candidate !== "") best = candidate;
532
+ }
533
+ if (best !== null) return best;
534
+ return [...flat].slice(-budget).join("");
535
+ }
536
+ var RULE_WIDTH_MIN, RULE_WIDTH_MAX, RULE_BLOCK_BUDGET;
537
+ var init_rule_text = __esm({
538
+ "../../packages/memory-engine/src/rule-text.ts"() {
539
+ "use strict";
540
+ RULE_WIDTH_MIN = 200;
541
+ RULE_WIDTH_MAX = 600;
542
+ RULE_BLOCK_BUDGET = RULE_WIDTH_MIN * 10;
543
+ }
544
+ });
545
+
500
546
  // ../../packages/memory-engine/src/boundary.ts
501
547
  function normalizeForMatch(value) {
502
548
  return value.normalize("NFC").replace(ZERO_WIDTH, "").replace(WHITESPACE_RUN, " ").trim();
@@ -4230,6 +4276,7 @@ var init_src = __esm({
4230
4276
  init_schema();
4231
4277
  init_layout();
4232
4278
  init_rule_budget();
4279
+ init_rule_text();
4233
4280
  init_boundary();
4234
4281
  init_markdown_mapping();
4235
4282
  init_build();
@@ -15797,26 +15844,31 @@ async function collectRules(vault, contextDir, now = /* @__PURE__ */ new Date())
15797
15844
  ...unreachableDefault ? { unreachableDefault } : {}
15798
15845
  };
15799
15846
  }
15800
- var RULE_WIDTH = 200;
15847
+ var RULE_WIDTH = RULE_WIDTH_MIN;
15801
15848
  function renderRuleLine(text, width = RULE_WIDTH) {
15802
- return quoteBlock(oneLine(text, width));
15849
+ return quoteBlock(rulePassage(oneLineWhole(text), width));
15803
15850
  }
15804
- function renderRuleLines(rules) {
15805
- return rules.map(({ entry }) => renderRuleLine(entry.memory.summary ?? entry.memory.title));
15851
+ function renderRuleLines(rules, width = ruleWidthFor(rules.length)) {
15852
+ return rules.map(({ entry }) => renderRuleLine(ruleTextOf(entry), width));
15806
15853
  }
15807
15854
  function renderRuleGroups(rules, project) {
15808
15855
  const globals = rules.filter((rule) => rule.lane === "global");
15809
15856
  const scoped = rules.filter((rule) => rule.lane === "project");
15857
+ const width = ruleWidthFor(rules.length);
15810
15858
  const out = [];
15811
15859
  if (globals.length > 0 && scoped.length > 0) {
15812
- out.push("Everywhere:", ...renderRuleLines(globals), "");
15813
- out.push(`In ${oneLine(project ?? "this project", WIDTH.title)}:`, ...renderRuleLines(scoped));
15860
+ out.push("Everywhere:", ...renderRuleLines(globals, width), "");
15861
+ out.push(
15862
+ `In ${oneLine(project ?? "this project", WIDTH.title)}:`,
15863
+ ...renderRuleLines(scoped, width)
15864
+ );
15814
15865
  return out;
15815
15866
  }
15816
- return renderRuleLines(rules);
15867
+ return renderRuleLines(rules, width);
15817
15868
  }
15818
15869
 
15819
15870
  // src/commands/guard.ts
15871
+ init_src();
15820
15872
  init_deadline();
15821
15873
  init_cloud_brain();
15822
15874
  function guardCommand() {
@@ -16015,7 +16067,8 @@ async function hostedRuleLines(where, contextDir) {
16015
16067
  return { lines: grouped, total };
16016
16068
  }
16017
16069
  function groupHostedLines(rules, project) {
16018
- const line = (r) => renderRuleLine(r.summary ?? r.title);
16070
+ const width = ruleWidthFor(rules.length);
16071
+ const line = (r) => renderRuleLine(r.text ?? r.summary ?? r.title, width);
16019
16072
  const globals = rules.filter((r) => r.lane === "global");
16020
16073
  const scoped = rules.filter((r) => r.lane === "project");
16021
16074
  if (globals.length === 0 || scoped.length === 0) return rules.map(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memgineering",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "private": false,
5
5
  "description": "One memory for the AI you connect. Recall, remember, and revise a brain your agents share — stored in your own folder.",
6
6
  "license": "Apache-2.0",