klypix-mcp 1.79.0 β 1.80.0
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/package.json +1 -1
- package/src/klypix-core.mjs +24 -1
- package/src/klypix-format.mjs +31 -2
package/package.json
CHANGED
package/src/klypix-core.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
brainLensData, lensToMarkdown, deathDateOfCard,
|
|
33
33
|
statusContextToMarkdown, findFulfillmentCandidates,
|
|
34
34
|
splitQueryTokens, scoreCardsAgainstQuery, correctionOverlaysFor,
|
|
35
|
-
isFastDecayCard, isUnresolvedOpenCard, DECAY_STALE_MS, formatDecayAge,
|
|
35
|
+
isFastDecayCard, isUnresolvedOpenCard, isSkillCard, DECAY_STALE_MS, formatDecayAge,
|
|
36
36
|
isPlanCard, planFulfillmentFor, PLAN_PAIR_SIM_BRAIN, isAgconfTwinId,
|
|
37
37
|
readPendingShips, clearPendingShips, pendingShipCards, formatCaptureReceipts,
|
|
38
38
|
} from './klypix-format.mjs';
|
|
@@ -494,9 +494,29 @@ export async function opBrainTaskContext({
|
|
|
494
494
|
};
|
|
495
495
|
});
|
|
496
496
|
const maxChars = Math.max(800, Math.min(5000, Number(budgetChars) || 2800));
|
|
497
|
+
// Standing rules (2026-08-24 audit): π οΈ skills are "apply every session",
|
|
498
|
+
// but the ranker's `score <= 0` gate drops a zero-overlap rule BEFORE its +1
|
|
499
|
+
// skill boost applies β so the capsule delivered task hits and zero standing
|
|
500
|
+
// rules, and the rule that would have warned the founder about a same-day
|
|
501
|
+
// billing trap never reached any session. This block is UNCONDITIONAL:
|
|
502
|
+
// relevance-ordered when the ranker scored a rule, newest-first otherwise,
|
|
503
|
+
// deduped against the hit list, and prepended so the maxChars tail-cut can
|
|
504
|
+
// never be the reason a rule silently vanished.
|
|
505
|
+
const skillPool = struct.cards.filter((c) => c.type !== 'container'
|
|
506
|
+
&& !/^archive$/i.test(c.area || '') && isSkillCard(c));
|
|
507
|
+
const scoreById = new Map(candidatePool.map((hit) => [hit.card.id, hit.score]));
|
|
508
|
+
const standing = skillPool
|
|
509
|
+
.filter((c) => !hitIds.has(c.id))
|
|
510
|
+
.sort((a, b) => (scoreById.get(b.id) || 0) - (scoreById.get(a.id) || 0)
|
|
511
|
+
|| (b.createdAt || 0) - (a.createdAt || 0))
|
|
512
|
+
.slice(0, 3);
|
|
497
513
|
const lines = [
|
|
498
514
|
`## Compact task context (${entries.length} relevant brain card${entries.length === 1 ? '' : 's'} Β· lexical-fast)`,
|
|
499
515
|
];
|
|
516
|
+
if (standing.length) {
|
|
517
|
+
lines.push(`### π οΈ Standing rules (${skillPool.length} in the brain β apply always; full set in the brief)`);
|
|
518
|
+
for (const c of standing) lines.push(`- [${flat(c.area) || 'Notes'}] ${clip(c.text, 220)}`);
|
|
519
|
+
}
|
|
500
520
|
if (!entries.length) {
|
|
501
521
|
lines.push('No high-confidence task-specific card matched. Continue from repository evidence; use brain_ask only if deeper project history is needed.');
|
|
502
522
|
} else {
|
|
@@ -521,6 +541,9 @@ export async function opBrainTaskContext({
|
|
|
521
541
|
context: {
|
|
522
542
|
mode: 'lexical-fast',
|
|
523
543
|
hits: entries,
|
|
544
|
+
...(standing.length ? {
|
|
545
|
+
standingRules: standing.map((c) => ({ id: c.id, area: flat(c.area) || 'Notes', text: clip(c.text, 220) })),
|
|
546
|
+
} : {}),
|
|
524
547
|
sufficient: entries.length > 0,
|
|
525
548
|
durationMs,
|
|
526
549
|
brain: path.basename(t.file),
|
package/src/klypix-format.mjs
CHANGED
|
@@ -1630,8 +1630,11 @@ export function structToBrief(struct, { recentDays = 14, maxRecent = 40, maxMile
|
|
|
1630
1630
|
// π οΈ Skills β reusable how-tos / gotchas / procedures (the '+' marker).
|
|
1631
1631
|
// Standing reference, NOT a point-in-time event: always shown, never
|
|
1632
1632
|
// recency-decayed, and excluded from open/milestones/recent so a skill never
|
|
1633
|
-
// masquerades as (or ages out like) a decision.
|
|
1634
|
-
|
|
1633
|
+
// masquerades as (or ages out like) a decision. Newest first: the render
|
|
1634
|
+
// below can only show maxSkills of them, and an unsorted slice pinned the
|
|
1635
|
+
// OLDEST rules forever while every rule learned since reached no session
|
|
1636
|
+
// (2026-08-24 audit β the founder's same-day billing rule was invisible).
|
|
1637
|
+
const skills = rest.filter(isSkillCard).sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
|
|
1635
1638
|
const open = rest.filter(isOpenCard);
|
|
1636
1639
|
const miles = rest.filter(isMilestoneCard);
|
|
1637
1640
|
const plain = rest.filter(c => !open.includes(c) && !miles.includes(c) && !skills.includes(c));
|
|
@@ -1916,6 +1919,32 @@ export function structToUltraBrief(struct, { freshness = null, briefPath = '.cla
|
|
|
1916
1919
|
// line that falls off the bottom of the preview-sized budget.
|
|
1917
1920
|
const overdueById = findOverdueOpenCards(struct).byId;
|
|
1918
1921
|
const openSorted = open.slice().sort((a, b) => (overdueById.has(b.id) ? 1 : 0) - (overdueById.has(a.id) ? 1 : 0));
|
|
1922
|
+
// π οΈ Standing rules tier (2026-08-24): the ultra brief used to render
|
|
1923
|
+
// skills as a COUNT in the tail β so the one surface every session reads
|
|
1924
|
+
// carried zero of the rules that are supposed to "fire every session".
|
|
1925
|
+
// Newest first (the most recently learned trap is the likeliest live one),
|
|
1926
|
+
// BEFORE the open list: opens are greedy to the budget floor, so anything
|
|
1927
|
+
// placed after them never lands. Small cap β this is a reminder tier, the
|
|
1928
|
+
// full set stays in the brief file. BUDGET FENCE (adversarial review of
|
|
1929
|
+
// 10f43e1, reproduced live at the 1800-char SessionStart default): this
|
|
1930
|
+
// tier must never be the reason a β° OVERDUE line fell off β price the
|
|
1931
|
+
// opens header, every overdue line, and the overflow line FIRST, and hold
|
|
1932
|
+
// that budget back from the skills tier. Rules yield to deadlines.
|
|
1933
|
+
if (skills.length) {
|
|
1934
|
+
const openHeader = `## Open questions & goals (${open.length}${overdueById.size ? `, ${overdueById.size} β° overdue` : ''})`;
|
|
1935
|
+
const overdueLines = openSorted.filter(c => overdueById.has(c.id)).map(c => `- β° OVERDUE ${fr(c)}${head(c)}`);
|
|
1936
|
+
const reserve = open.length
|
|
1937
|
+
? ['', openHeader, ...overdueLines, `- β¦and ${open.length} more β in the full brief.`]
|
|
1938
|
+
.reduce((s, l) => s + l.length + 1, 0)
|
|
1939
|
+
: 0;
|
|
1940
|
+
const pushIfFenced = (l) => (used + l.length + 1 > budget - reserve) ? false : (push(l), true);
|
|
1941
|
+
if (pushIfFenced('') && pushIfFenced(`## π οΈ Standing rules (${skills.length} β newest first, apply always)`)) {
|
|
1942
|
+
const newest = skills.slice().sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
|
|
1943
|
+
let shown = 0;
|
|
1944
|
+
for (const c of newest.slice(0, 5)) { if (!pushIfFenced(`- ${fr(c)}${head(c)}`)) break; shown++; }
|
|
1945
|
+
if (shown < skills.length) pushIfFenced(`- β¦and ${skills.length - shown} more standing rule(s) β in the full brief.`);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1919
1948
|
if (open.length && pushIf('') && pushIf(`## Open questions & goals (${open.length}${overdueById.size ? `, ${overdueById.size} β° overdue` : ''})`)) {
|
|
1920
1949
|
let shown = 0;
|
|
1921
1950
|
for (const c of openSorted) { if (!pushIf(`- ${overdueById.has(c.id) ? 'β° OVERDUE ' : ''}${fr(c)}${head(c)}`)) break; shown++; }
|