hypomnema 1.3.4 → 1.4.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.ko.md +2 -2
- package/README.md +2 -2
- package/commands/feedback.md +4 -0
- package/docs/ARCHITECTURE.md +4 -2
- package/docs/CONTRIBUTING.md +56 -17
- package/hooks/hypo-auto-commit.mjs +6 -12
- package/hooks/hypo-session-record.mjs +5 -0
- package/hooks/hypo-session-start.mjs +7 -0
- package/hooks/hypo-shared.mjs +68 -1
- package/package.json +5 -2
- package/scripts/bump-version.mjs +20 -6
- package/scripts/check-readme-version.mjs +126 -0
- package/scripts/check-versions.mjs +171 -0
- package/scripts/crystallize.mjs +19 -32
- package/scripts/doctor.mjs +13 -4
- package/scripts/feedback.mjs +68 -1
- package/scripts/graph.mjs +5 -32
- package/scripts/init.mjs +2 -1
- package/scripts/lib/failure-type.mjs +33 -0
- package/scripts/lib/frontmatter.mjs +23 -2
- package/scripts/lib/schema-vocab.mjs +35 -0
- package/scripts/lib/template-schema-version.mjs +21 -0
- package/scripts/lib/wikilink.mjs +156 -0
- package/scripts/lint.mjs +86 -47
- package/scripts/rename.mjs +6 -30
- package/scripts/smoke-plugin.mjs +194 -0
- package/scripts/stats.mjs +22 -2
- package/scripts/upgrade.mjs +11 -3
- package/templates/SCHEMA.md +25 -1
- package/templates/hypo-config.md +1 -1
package/scripts/stats.mjs
CHANGED
|
@@ -45,16 +45,25 @@ function collectMdFiles(dir, acc = [], hypoDir = '', ignorePatterns = []) {
|
|
|
45
45
|
return acc;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Local top-level-only extractor, behavior-aligned with lib/frontmatter.mjs:
|
|
49
|
+
// skip indented / list lines, first-wins, strip a trailing `# comment`. Without
|
|
50
|
+
// the comment strip a `failure_type: overreach # note` would aggregate under the
|
|
51
|
+
// literal "overreach # note" key (FEAT-1). Kept local (not the shared import) to
|
|
52
|
+
// hold stats' dependency surface flat per the FEAT-1 scope split.
|
|
48
53
|
function parseFrontmatter(content) {
|
|
49
54
|
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
50
55
|
if (!m) return null;
|
|
51
56
|
const fm = {};
|
|
52
|
-
for (const line of m[1].split(
|
|
57
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
58
|
+
if (/^\s/.test(line) || /^-(\s|$)/.test(line)) continue; // nested / list item
|
|
53
59
|
const idx = line.indexOf(':');
|
|
54
60
|
if (idx < 0) continue;
|
|
55
|
-
|
|
61
|
+
const key = line.slice(0, idx).trim();
|
|
62
|
+
if (!key || Object.hasOwn(fm, key)) continue; // first-wins
|
|
63
|
+
fm[key] = line
|
|
56
64
|
.slice(idx + 1)
|
|
57
65
|
.trim()
|
|
66
|
+
.replace(/\s+#.*$/, '')
|
|
58
67
|
.replace(/^["']|["']$/g, '');
|
|
59
68
|
}
|
|
60
69
|
return fm;
|
|
@@ -92,6 +101,7 @@ const sources = existsSync(join(args.hypoDir, 'sources'))
|
|
|
92
101
|
: [];
|
|
93
102
|
|
|
94
103
|
const typeCounts = {};
|
|
104
|
+
const failureTypeCounts = {}; // FEAT-1: feedback pages carrying a failure_type
|
|
95
105
|
let missingFrontmatter = 0;
|
|
96
106
|
|
|
97
107
|
for (const f of pageFiles) {
|
|
@@ -108,6 +118,9 @@ for (const f of pageFiles) {
|
|
|
108
118
|
}
|
|
109
119
|
const t = fm.type || 'unknown';
|
|
110
120
|
typeCounts[t] = (typeCounts[t] || 0) + 1;
|
|
121
|
+
if (t === 'feedback' && fm.failure_type) {
|
|
122
|
+
failureTypeCounts[fm.failure_type] = (failureTypeCounts[fm.failure_type] || 0) + 1;
|
|
123
|
+
}
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
let adrCount = 0;
|
|
@@ -122,6 +135,9 @@ const lastActivity = getLastActivity(args.hypoDir);
|
|
|
122
135
|
|
|
123
136
|
const stats = {
|
|
124
137
|
pages: { total: pageFiles.length, byType: typeCounts, missingFrontmatter },
|
|
138
|
+
// Omit the key entirely when no feedback page is classified (OQ-4: no empty
|
|
139
|
+
// section noise) so consumers can branch on presence.
|
|
140
|
+
...(Object.keys(failureTypeCounts).length ? { failureTypes: failureTypeCounts } : {}),
|
|
125
141
|
projects: projects.length,
|
|
126
142
|
sources: sources.length,
|
|
127
143
|
adrs: adrCount,
|
|
@@ -139,6 +155,10 @@ if (args.json) {
|
|
|
139
155
|
if (missingFrontmatter) {
|
|
140
156
|
console.log(` missing frontmatter: ${missingFrontmatter}`);
|
|
141
157
|
}
|
|
158
|
+
const ftEntries = Object.entries(failureTypeCounts).sort((a, b) => b[1] - a[1]);
|
|
159
|
+
if (ftEntries.length) {
|
|
160
|
+
console.log(` failure types: ${ftEntries.map(([t, n]) => `${t} (${n})`).join(', ')}`);
|
|
161
|
+
}
|
|
142
162
|
console.log(`Projects: ${projects.length}`);
|
|
143
163
|
console.log(`Sources: ${sources.length}`);
|
|
144
164
|
console.log(`ADRs: ${adrCount}`);
|
package/scripts/upgrade.mjs
CHANGED
|
@@ -42,6 +42,7 @@ import { fileURLToPath } from 'url';
|
|
|
42
42
|
import { createHash } from 'crypto';
|
|
43
43
|
import { resolveHypoRoot, expandHome } from './lib/hypo-root.mjs';
|
|
44
44
|
import { parseFrontmatter } from './lib/frontmatter.mjs';
|
|
45
|
+
import { templateSchemaVersion } from './lib/template-schema-version.mjs';
|
|
45
46
|
import {
|
|
46
47
|
readPkgJson as readPkgJsonSafe,
|
|
47
48
|
writePkgJsonAtomic,
|
|
@@ -477,7 +478,14 @@ function writeMigrationReport(hypoDir, fromVersion, toVersion, { pluginMode = fa
|
|
|
477
478
|
// right to keep SCHEMA.md as user-owned (Option C); auto-stub of the 9 new
|
|
478
479
|
// fields is rejected because scope/tier/targets/sensitivity/reason/source
|
|
479
480
|
// are semantic decisions whose wrong defaults would project wrong behavior.
|
|
480
|
-
|
|
481
|
+
// Fire for any 1.x → 2.x major crossing, not just 1.0 → 2.0 exactly: the ADR
|
|
482
|
+
// 0031 feedback-field backfill that this body explains was introduced at 2.0
|
|
483
|
+
// and still applies to a user landing on any later 2.x (e.g. 2.1's additive
|
|
484
|
+
// failure_type — FEAT-1). Keying on the exact target string would silently
|
|
485
|
+
// drop this guidance the moment the template minor moves.
|
|
486
|
+
const fromV = parseVersion(fromVersion);
|
|
487
|
+
const toV = parseVersion(toVersion);
|
|
488
|
+
const isV1ToV2 = fromV?.major === 1 && toV?.major === 2;
|
|
481
489
|
const specificBody = isV1ToV2
|
|
482
490
|
? `## What changed in SCHEMA 2.0
|
|
483
491
|
|
|
@@ -755,7 +763,7 @@ function applyCommands(commandResults, force) {
|
|
|
755
763
|
...existing,
|
|
756
764
|
pkgRoot: PKG_ROOT,
|
|
757
765
|
pkgVersion,
|
|
758
|
-
schemaVersion: '2.
|
|
766
|
+
schemaVersion: templateSchemaVersion(PKG_ROOT) ?? '2.1',
|
|
759
767
|
commands: newSHAs,
|
|
760
768
|
});
|
|
761
769
|
return applied;
|
|
@@ -781,7 +789,7 @@ function writePluginModeMetadata() {
|
|
|
781
789
|
...existing,
|
|
782
790
|
pkgRoot: PKG_ROOT,
|
|
783
791
|
pkgVersion,
|
|
784
|
-
schemaVersion: '2.
|
|
792
|
+
schemaVersion: templateSchemaVersion(PKG_ROOT) ?? '2.1',
|
|
785
793
|
});
|
|
786
794
|
return true;
|
|
787
795
|
}
|
package/templates/SCHEMA.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: Wiki Schema
|
|
3
3
|
type: schema
|
|
4
4
|
updated: YYYY-MM-DD
|
|
5
|
-
version: 2.
|
|
5
|
+
version: 2.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Wiki Schema
|
|
@@ -118,11 +118,35 @@ memory_summary: <one line for the MEMORY.md index>
|
|
|
118
118
|
reason: <why this rule is needed>
|
|
119
119
|
source: session:YYYY-MM-DD | commit:<hash> | pr:<n> | https://...
|
|
120
120
|
|
|
121
|
+
# optional (2.1) — failure taxonomy for incident-driven corrections:
|
|
122
|
+
failure_type: <enum> # one of the 8 values below; OMIT for pure
|
|
123
|
+
# preferences / new conventions ("always do X")
|
|
124
|
+
|
|
121
125
|
# conditional — required when `targets` includes `claude-learned`:
|
|
122
126
|
global_summary: <one line for the <learned_behaviors> entry>
|
|
123
127
|
promote_to_global: true # explicit opt-in to global projection
|
|
124
128
|
```
|
|
125
129
|
|
|
130
|
+
`failure_type` (optional, added 2.1) classifies feedback that came from a real
|
|
131
|
+
failure incident so recurring mistake types become machine-aggregatable
|
|
132
|
+
(surfaced by `hypomnema stats`). Leave it off when the page records a preference
|
|
133
|
+
rather than a failure. The eight values, in classification-precedence order
|
|
134
|
+
(most specific first — a failure matching several takes the earliest):
|
|
135
|
+
|
|
136
|
+
| value | when |
|
|
137
|
+
|-------|------|
|
|
138
|
+
| `hallucination` | fabricated a fact / API / path |
|
|
139
|
+
| `false-completion` | declared "done" without running the required gate or test |
|
|
140
|
+
| `process-stall` | stopped instead of asking / continuing when it should have |
|
|
141
|
+
| `over-caution` | re-asked or re-gated despite standing authority |
|
|
142
|
+
| `overreach` | acted beyond the requested scope |
|
|
143
|
+
| `incompleteness` | started correctly but omitted a required step or scope |
|
|
144
|
+
| `instruction-miss` | ignored an explicit this-session instruction |
|
|
145
|
+
| `convention-violation` | broke a standing documented convention (not restated) |
|
|
146
|
+
|
|
147
|
+
`lint` rejects any value outside this set; an omitted `failure_type` is always
|
|
148
|
+
allowed.
|
|
149
|
+
|
|
126
150
|
Edit the feedback page only — never hand-edit the generated
|
|
127
151
|
`<!-- HYPO:FEEDBACK-SYNC:START … -->` managed blocks (sync detects tampering as a conflict).
|
|
128
152
|
|