klypix-mcp 1.86.0 โ 1.86.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/FORMAT.md +11 -0
- package/README.md +22 -0
- package/package.json +2 -2
- package/src/brain-evidence.mjs +10 -2
- package/src/brain-note.mjs +6 -2
- package/src/global-brain-hook.mjs +546 -61
- package/src/klypix-core.mjs +9 -3
- package/src/klypix-format.mjs +754 -136
package/FORMAT.md
CHANGED
|
@@ -143,6 +143,17 @@ references are read with both HEAD and working-file changes considered. These fi
|
|
|
143
143
|
describe source provenance and change detection, never factual verification.
|
|
144
144
|
`verify` is retained text, not executable configuration. All these optional fields
|
|
145
145
|
survive the format codec and capture lifecycle; an explicit empty amendment clears them.
|
|
146
|
+
A cleared `verify` is stored as the empty string `""`, and a reader treats any string `verify`,
|
|
147
|
+
the empty one included, as authoritative: `""` means "no probe" and nothing is derived from the
|
|
148
|
+
card's prose.
|
|
149
|
+
A card without a `verify` field may still carry one in its text as a line-end
|
|
150
|
+
`verify: <command>` suffix (or a `verify: <command>` line of its own); the reference reader
|
|
151
|
+
derives the field with the same grammar the capture hook uses (lowercase key followed by
|
|
152
|
+
whitespace, trailing position, a command-shaped value with no prose after it), reading a
|
|
153
|
+
hard-wrapped card as its unwrapped sentences, so prose such as `npm run verify:mcp` or
|
|
154
|
+
"every agent verify: the tag" never becomes a probe. A probe stops before a following line that
|
|
155
|
+
opens like a new sentence (a plain capitalised word), because such a break may have been typed
|
|
156
|
+
rather than wrapped.
|
|
146
157
|
|
|
147
158
|
The optional **`author`** answers the question a team actually asks: `createdBy` says
|
|
148
159
|
*what* wrote a card, `author` says *whose*. It is resolved from `git config user.name`
|
package/README.md
CHANGED
|
@@ -381,6 +381,28 @@ same JSON on stdin, or `--evidence '<JSON array>'` and `--verify '<text>'`.
|
|
|
381
381
|
On Claude Code, decisions are captured automatically at turn end from inline `๐ง BRAIN [Area]:`
|
|
382
382
|
markers in the transcript, deduped, under a capture lock.
|
|
383
383
|
|
|
384
|
+
A marker can end with optional suffixes, in any order: `closes: <card title or [[wikilink]]>`,
|
|
385
|
+
`ev: <file[:line]>, PR#<n>`, `verify: <command>` and `q: <the question this answers?>`. They count
|
|
386
|
+
only as one run at the end of the line, written lowercase as `key: value`, and each value must
|
|
387
|
+
have its key's shape: references for `ev:`, a command for `verify:`, a question (question word
|
|
388
|
+
first, `?` last) for `q:`. Anything else is kept as card text, so "a Q: and A: layout", "the ev:
|
|
389
|
+
field" or "every agent verify: the tag" never cuts a note short. A malformed segment after a
|
|
390
|
+
well-formed one also stays in the text, and the other suffixes still count; the hook tells the
|
|
391
|
+
agent on its next prompt. On `โ` and `~` markers a `closes:` is plain text, because those markers
|
|
392
|
+
close nothing. A `closes:` that will not act keeps the sentence as written: it names no live card,
|
|
393
|
+
or it names more than four. A `closes:` that does not come after the end of a sentence, a
|
|
394
|
+
`[[wikilink]]` or another suffix closes only a card it names by title. One whose named card is
|
|
395
|
+
already closed closes nothing else. A `~` update too thin to replace its card (fewer than six
|
|
396
|
+
content words and under half the card's) is appended to that card as a dated `(~ amended โฆ)` line
|
|
397
|
+
instead. The card keeps its date, colour and probe, and previews show the newest amendment first.
|
|
398
|
+
The correction lands and nothing is lost.
|
|
399
|
+
|
|
400
|
+
The Stop hook reads the whole transcript every time, so a `~` or `โ` line applies once. A marker
|
|
401
|
+
that an older hook (1.85 or 1.86.0) already captured is not captured again after an upgrade. If
|
|
402
|
+
1.86.0 cut the note short, the stub is restored to the full text in place. When a marker does not
|
|
403
|
+
do what it says, the next prompt tells the agent. A receipt left by a session that has ended goes
|
|
404
|
+
to the next session that starts in the project.
|
|
405
|
+
|
|
384
406
|
On every other host, capture is explicit: `brain_note` runs the same capture engine as the hooks โ
|
|
385
407
|
dedup, supersession, round-trip re-adoption receipts, `โ` resolve, `~` update in place, `+` skill,
|
|
386
408
|
`closes:` โ and stamps which agent wrote the card. A `โ` question preference ranks only candidates
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "klypix-mcp",
|
|
3
|
-
"version": "1.86.
|
|
3
|
+
"version": "1.86.1",
|
|
4
4
|
"mcpName": "io.github.dahshanlabs/klypix-mcp",
|
|
5
5
|
"description": "Active state management for multi-agent coding: a shared, versioned project brain over MCP.",
|
|
6
6
|
"type": "module",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"bench": "node bin/klypix-mcp.mjs bench",
|
|
85
85
|
"test:bench": "node test/bench.mjs",
|
|
86
86
|
"pretest": "node test/publish-workflow.mjs",
|
|
87
|
-
"test": "node test/publish-verdict.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/install-version.mjs && node test/install-rename-backoff.mjs && node test/project-binding-rebind.mjs && node test/context-gateway.mjs && node test/repo-state.mjs && node test/released-tag-guard.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/capture-gap.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/orphan-gardener.mjs && node test/brief-and-recall.mjs && node test/guard-cards.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/retrieval-fusion.mjs && node test/eval-retrieval.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/partial-notes.mjs && node test/lifecycle-prefix.mjs && node test/close-link-safety.mjs && node test/resolve-ledger.mjs && node test/plan-fulfillment.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-security.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/enrichment.mjs && node test/hook-fallback.mjs && node test/eval-hook-lane.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/evidence-anchors.mjs && node test/brain-evidence.mjs && node test/presence-visibility.mjs && node test/undeclared-active.mjs && node test/presence-liveness.mjs && node test/observed-scope.mjs && node test/release-lease.mjs && node test/release-reconcile.mjs && node test/release-ancestry.mjs && node test/release-claim-join.mjs && node test/release-claims.mjs && node test/release-handshake.mjs && node test/completion-guard.mjs && node test/merge-brains.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/capture-write-failure.mjs && node test/a2a-smoke.mjs && node test/one-command-setup.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/canvas-groups.mjs && node test/git-tools.mjs && node test/uninstall.mjs && node test/current-guidance.mjs && node test/status-shape.mjs && node test/status-hook.mjs",
|
|
87
|
+
"test": "node test/publish-verdict.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/install-version.mjs && node test/install-rename-backoff.mjs && node test/project-binding-rebind.mjs && node test/context-gateway.mjs && node test/repo-state.mjs && node test/released-tag-guard.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/capture-gap.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/orphan-gardener.mjs && node test/brief-and-recall.mjs && node test/guard-cards.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/retrieval-fusion.mjs && node test/eval-retrieval.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/partial-notes.mjs && node test/lifecycle-prefix.mjs && node test/close-link-safety.mjs && node test/resolve-ledger.mjs && node test/plan-fulfillment.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-security.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/enrichment.mjs && node test/hook-fallback.mjs && node test/eval-hook-lane.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/marker-suffix-grammar.mjs && node test/evidence-anchors.mjs && node test/brain-evidence.mjs && node test/presence-visibility.mjs && node test/undeclared-active.mjs && node test/presence-liveness.mjs && node test/observed-scope.mjs && node test/release-lease.mjs && node test/release-reconcile.mjs && node test/release-ancestry.mjs && node test/release-claim-join.mjs && node test/release-claims.mjs && node test/release-handshake.mjs && node test/completion-guard.mjs && node test/merge-brains.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/capture-write-failure.mjs && node test/a2a-smoke.mjs && node test/one-command-setup.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/canvas-groups.mjs && node test/git-tools.mjs && node test/uninstall.mjs && node test/current-guidance.mjs && node test/status-shape.mjs && node test/status-hook.mjs",
|
|
88
88
|
"test:memory": "node test/memory-runtime.mjs",
|
|
89
89
|
"test:memory:soak": "node --expose-gc test/memory-soak.mjs",
|
|
90
90
|
"runtime": "node bin/klypix-runtime.mjs",
|
package/src/brain-evidence.mjs
CHANGED
|
@@ -80,7 +80,7 @@ function workingTreeStatus(root, relative, deadline) {
|
|
|
80
80
|
} catch (error) { return error.status === 1 ? 'changed' : 'unverified'; }
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
export function prepareBrainEvidence({ projectRoot, evidence, verify, marker = '', text = '' } = {}) {
|
|
83
|
+
export function prepareBrainEvidence({ projectRoot, evidence, verify, marker = '', text = '', deriveVerify = null } = {}) {
|
|
84
84
|
const bad = error => ({ ok: false, error });
|
|
85
85
|
if (marker === 'โ' && (evidence !== undefined || verify !== undefined)) {
|
|
86
86
|
return bad('A resolve marker archives existing evidence. To record new evidence, write a milestone with closes, or amend the card with marker ~.');
|
|
@@ -88,7 +88,15 @@ export function prepareBrainEvidence({ projectRoot, evidence, verify, marker = '
|
|
|
88
88
|
if (verify !== undefined && (typeof verify !== 'string' || verify.length > 2000 || /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(verify))) {
|
|
89
89
|
return bad('verify must be a string of at most 2000 characters; it is recorded, never executed.');
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
// The question is "would the READER derive a verify from this text?" โ so
|
|
92
|
+
// the callers pass the reader itself (klypix-format parseVerifySuffix; this
|
|
93
|
+
// module may not import it). Then "every agent verify: the tag" is not
|
|
94
|
+
// refused (the reader derives nothing) and an own-line `verify: <command>`
|
|
95
|
+
// is (the reader derives it). Without a deriver: a lowercase `verify: value`.
|
|
96
|
+
const inlineVerify = () => (typeof deriveVerify === 'function'
|
|
97
|
+
? Boolean(deriveVerify(String(text)))
|
|
98
|
+
: /(?:^|\s)verify:\s+\S/.test(String(text)));
|
|
99
|
+
if (marker === '~' && typeof verify === 'string' && !verify.trim() && inlineVerify()) {
|
|
92
100
|
return bad('To clear verification, remove the inline verify: suffix from the amended text too.');
|
|
93
101
|
}
|
|
94
102
|
if (evidence !== undefined && (!Array.isArray(evidence) || evidence.length > 16)) return bad('evidence must be an array of at most 16 references.');
|
package/src/brain-note.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import fs from 'fs';
|
|
|
22
22
|
import path from 'path';
|
|
23
23
|
import { prepareBrainEvidence } from './brain-evidence.mjs';
|
|
24
24
|
import { looksLikeUnfilledDraft } from './capture-gap.mjs';
|
|
25
|
-
import { captureIntoBrain, tidyBrain, atomicWrite, noteToCaptureInput, formatCaptureReceipts } from './klypix-format.mjs';
|
|
25
|
+
import { captureIntoBrain, tidyBrain, atomicWrite, noteToCaptureInput, formatCaptureReceipts, parseVerifySuffix } from './klypix-format.mjs';
|
|
26
26
|
import { brainCaptureLockPath, withAdvisoryWriteLock } from './brain-write-lock.mjs';
|
|
27
27
|
|
|
28
28
|
const MARKERS = { '': '', '?': '?', '!': '!', 'โ': 'โ', '~': '~', question: '?', milestone: '!', resolve: 'โ', update: '~', decision: '', done: 'โ' };
|
|
@@ -73,7 +73,7 @@ if (!opts.text) { console.error('brain-note: nothing to write โ pass a note as
|
|
|
73
73
|
if (!fs.existsSync(file)) { console.error(`brain-note: no brain at ${file} (run from a project with ./brain.klypix, or pass a path).`); process.exit(1); }
|
|
74
74
|
|
|
75
75
|
if (looksLikeUnfilledDraft(opts.text)) { console.error('brain-note refused (brain unchanged): complete or discard the draft rationale before capturing it.'); process.exit(1); }
|
|
76
|
-
const metadata = prepareBrainEvidence({ projectRoot: path.dirname(file), evidence: opts.evidence, verify: opts.verify, marker: opts.marker, text: opts.text });
|
|
76
|
+
const metadata = prepareBrainEvidence({ projectRoot: path.dirname(file), evidence: opts.evidence, verify: opts.verify, marker: opts.marker, text: opts.text, deriveVerify: parseVerifySuffix });
|
|
77
77
|
if (!metadata.ok) { console.error(`brain-note refused (brain unchanged): ${metadata.error}`); process.exit(1); }
|
|
78
78
|
const input = noteToCaptureInput({ text: opts.text, area: opts.area, marker: opts.marker, closes: opts.closes, evidence: metadata.evidence, verify: metadata.verify, createdVia: 'cli' });
|
|
79
79
|
try {
|
|
@@ -95,6 +95,10 @@ try {
|
|
|
95
95
|
const s = res.stats || {};
|
|
96
96
|
const bits = [`${s.added || 0} added`];
|
|
97
97
|
for (const k of ['resolved', 'updated', 'closed', 'superseded']) if (s[k]) bits.push(`${s[k]} ${k}`);
|
|
98
|
+
// A thin ~ is appended to its card (1.86.1), and one the card already says
|
|
99
|
+
// changes nothing โ neither may read as a plain "1 updated" / "0 added".
|
|
100
|
+
if (Array.isArray(s.updateAmended) && s.updateAmended.length) bits.push(`${s.updateAmended.length} appended as a dated amendment (too thin to replace its card)`);
|
|
101
|
+
if (Array.isArray(s.updateUnchanged) && s.updateUnchanged.length) bits.push(`${s.updateUnchanged.length} unchanged (the card already says it)`);
|
|
98
102
|
if (s.reAdopted) bits.push(`${s.reAdopted} re-adopted`);
|
|
99
103
|
if (s.linked) bits.push(`${s.linked} linked`);
|
|
100
104
|
console.error(`โ brain-note โ ${path.basename(file)} (${bits.join(' ยท ')})`);
|