hypomnema 1.3.1 → 1.3.3
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 +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/README.ko.md +11 -10
- package/README.md +11 -10
- package/commands/audit.md +1 -1
- package/commands/crystallize.md +11 -11
- package/commands/doctor.md +1 -1
- package/commands/feedback.md +2 -2
- package/commands/graph.md +1 -1
- package/commands/ingest.md +1 -1
- package/commands/init.md +1 -1
- package/commands/lint.md +1 -1
- package/commands/query.md +1 -1
- package/commands/rename.md +79 -0
- package/commands/resume.md +2 -2
- package/commands/stats.md +1 -1
- package/commands/uninstall.md +1 -1
- package/commands/upgrade.md +1 -1
- package/commands/verify.md +1 -1
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/CONTRIBUTING.md +2 -2
- package/hooks/hypo-auto-commit.mjs +8 -24
- package/hooks/hypo-auto-minimal-crystallize.mjs +57 -11
- package/hooks/hypo-compact-guard.mjs +6 -3
- package/hooks/hypo-cwd-change.mjs +1 -1
- package/hooks/hypo-first-prompt.mjs +2 -2
- package/hooks/hypo-hot-rebuild.mjs +14 -1
- package/hooks/hypo-personal-check.mjs +91 -179
- package/hooks/hypo-pre-commit.mjs +1 -1
- package/hooks/hypo-session-end.mjs +1 -1
- package/hooks/hypo-session-start.mjs +7 -7
- package/hooks/hypo-shared.mjs +1082 -89
- package/hooks/hypo-web-fetch-ingest.mjs +2 -2
- package/hooks/version-check-fetch.mjs +2 -8
- package/hooks/version-check.mjs +18 -0
- package/package.json +3 -2
- package/scripts/check-tracker-ids.mjs +329 -0
- package/scripts/crystallize.mjs +322 -110
- package/scripts/doctor.mjs +6 -9
- package/scripts/feedback-sync.mjs +1 -1
- package/scripts/init.mjs +1 -1
- package/scripts/install-git-hooks.mjs +75 -40
- package/scripts/lib/check-tracker-ids.mjs +140 -0
- package/scripts/lib/design-history-stale.mjs +59 -14
- package/scripts/lib/extensions.mjs +4 -4
- package/scripts/lib/fix-manifest.mjs +2 -2
- package/scripts/lib/fix-status-verify.mjs +5 -4
- package/scripts/lib/plugin-detect.mjs +15 -6
- package/scripts/lib/project-create.mjs +1 -1
- package/scripts/lint.mjs +63 -8
- package/scripts/rename.mjs +836 -0
- package/scripts/resume.mjs +75 -19
- package/scripts/uninstall.mjs +1 -1
- package/scripts/upgrade.mjs +26 -25
- package/skills/crystallize/SKILL.md +12 -9
- package/skills/graph/SKILL.md +1 -1
- package/skills/ingest/SKILL.md +1 -1
- package/skills/lint/SKILL.md +1 -1
- package/skills/query/SKILL.md +1 -1
- package/skills/verify/SKILL.md +1 -1
- package/templates/SCHEMA.md +2 -2
- package/templates/hypo-config.md +2 -2
- package/templates/hypo-guide.md +22 -1
- package/templates/hypo-help.md +1 -0
- package/templates/projects/_template/index.md +1 -1
package/scripts/lint.mjs
CHANGED
|
@@ -81,7 +81,7 @@ const TYPE_CONDITIONAL_FIELDS = {
|
|
|
81
81
|
'tool-eval': ['status'],
|
|
82
82
|
postmortem: ['outcome'],
|
|
83
83
|
learning: ['source'],
|
|
84
|
-
// feedback: ADR 0031
|
|
84
|
+
// feedback: ADR 0031 — projection SoT requires full classification
|
|
85
85
|
feedback: [
|
|
86
86
|
'status',
|
|
87
87
|
'scope',
|
|
@@ -102,7 +102,7 @@ const TYPE_ENUM_FIELDS = {
|
|
|
102
102
|
prd: { status: ['draft', 'active', 'completed', 'cancelled', 'archived'] },
|
|
103
103
|
adr: { status: ['proposed', 'accepted', 'deprecated', 'superseded'] },
|
|
104
104
|
'tool-eval': { status: ['adopted', 'evaluating', 'rejected'] },
|
|
105
|
-
// feedback: ADR 0031
|
|
105
|
+
// feedback: ADR 0031 — sensitivity:private is forbidden (wiki is a
|
|
106
106
|
// git-pushed public surface)
|
|
107
107
|
feedback: {
|
|
108
108
|
status: ['active', 'superseded', 'archived'],
|
|
@@ -135,15 +135,56 @@ function collectPages(dir, root, pages = [], ignorePatterns = []) {
|
|
|
135
135
|
|
|
136
136
|
// ── slug map ─────────────────────────────────────────────────────────────────
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
// `extraTargets` are link-target-only slugs (root *.md, sources/*) that resolve
|
|
139
|
+
// wikilinks but are not themselves linted — added verbatim, with NO derived
|
|
140
|
+
// basename/dir-relative aliases, so they can't mask an unrelated broken link.
|
|
141
|
+
function buildSlugMap(pages, extraTargets = []) {
|
|
139
142
|
const map = new Set();
|
|
140
143
|
for (const { rel } of pages) {
|
|
141
|
-
|
|
144
|
+
const noExt = rel.replace(/\.md$/, '').replace(/\\/g, '/');
|
|
145
|
+
map.add(noExt);
|
|
142
146
|
map.add(basename(rel, '.md'));
|
|
147
|
+
// Dir-relative alias: drop the leading scan-dir segment so the vault's
|
|
148
|
+
// convention link [[learnings/foo]] resolves to pages/learnings/foo.md.
|
|
149
|
+
// (A page directly under a scan dir has no extra segment to drop.)
|
|
150
|
+
const slash = noExt.indexOf('/');
|
|
151
|
+
if (slash !== -1) map.add(noExt.slice(slash + 1));
|
|
143
152
|
}
|
|
153
|
+
for (const t of extraTargets) map.add(t);
|
|
144
154
|
return map;
|
|
145
155
|
}
|
|
146
156
|
|
|
157
|
+
// Link-target-only slugs: files that are valid wikilink destinations but are
|
|
158
|
+
// NOT linted themselves. Root-level *.md (hot.md / log.md / hypo-guide.md /
|
|
159
|
+
// SCHEMA.md — special operational files whose types sit outside VALID_TYPES)
|
|
160
|
+
// and sources/* (immutable captured sources). Returned as verbatim slugs so
|
|
161
|
+
// buildSlugMap adds no derived aliases for them.
|
|
162
|
+
function collectLinkTargets(hypoDir, ignorePatterns = []) {
|
|
163
|
+
const targets = [];
|
|
164
|
+
if (existsSync(hypoDir)) {
|
|
165
|
+
for (const entry of readdirSync(hypoDir)) {
|
|
166
|
+
const full = join(hypoDir, entry);
|
|
167
|
+
// root-level *.md FILES only (no recursion), honoring .hypoignore exactly
|
|
168
|
+
// like collectPages — otherwise an ignored root file (e.g. a secret) would
|
|
169
|
+
// resolve [[its-slug]] as valid, a false negative.
|
|
170
|
+
if (
|
|
171
|
+
extname(entry) === '.md' &&
|
|
172
|
+
!entry.startsWith('.') &&
|
|
173
|
+
!isIgnored(full, hypoDir, ignorePatterns) &&
|
|
174
|
+
statSync(full).isFile()
|
|
175
|
+
) {
|
|
176
|
+
targets.push(entry.replace(/\.md$/, ''));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// sources/*: linkable as the full 'sources/<name>' slug only — deliberately no
|
|
181
|
+
// bare basename, so a stale [[name]] can't silently resolve to a source file.
|
|
182
|
+
for (const { rel } of collectPages(join(hypoDir, 'sources'), hypoDir, [], ignorePatterns)) {
|
|
183
|
+
targets.push(rel.replace(/\.md$/, '').replace(/\\/g, '/'));
|
|
184
|
+
}
|
|
185
|
+
return targets;
|
|
186
|
+
}
|
|
187
|
+
|
|
147
188
|
// ── wikilink extractor ────────────────────────────────────────────────────────
|
|
148
189
|
|
|
149
190
|
// Strip regions where wikilinks are not real references:
|
|
@@ -168,7 +209,12 @@ function stripNonWikilinkRegions(content) {
|
|
|
168
209
|
function extractWikilinks(content) {
|
|
169
210
|
const stripped = stripNonWikilinkRegions(content);
|
|
170
211
|
const links = [];
|
|
171
|
-
|
|
212
|
+
// Target capture excludes `\` and stops before an optional `\` preceding the
|
|
213
|
+
// alias/anchor delimiter, so a markdown-table-escaped alias
|
|
214
|
+
// `[[a/b\|label]]` (the `\|` is a literal pipe inside a table cell) yields the
|
|
215
|
+
// clean target `a/b`, not `a/b\`. A bare `[[a\]]` (no delimiter) simply fails
|
|
216
|
+
// to match rather than being mis-read as `[[a]]`.
|
|
217
|
+
for (const m of stripped.matchAll(/\[\[([^\]|#\\]+?)(?:\\?[|#][^\]]*?)?\]\]/g)) {
|
|
172
218
|
links.push(m[1].trim());
|
|
173
219
|
}
|
|
174
220
|
return links;
|
|
@@ -367,7 +413,8 @@ const args = parseArgs(process.argv);
|
|
|
367
413
|
const ignorePatterns = loadHypoIgnore(args.hypoDir);
|
|
368
414
|
const scanDirs = ['pages', 'projects', 'journal'].map((d) => join(args.hypoDir, d));
|
|
369
415
|
const pages = scanDirs.flatMap((d) => collectPages(d, args.hypoDir, [], ignorePatterns));
|
|
370
|
-
const
|
|
416
|
+
const linkTargets = collectLinkTargets(args.hypoDir, ignorePatterns);
|
|
417
|
+
const slugMap = buildSlugMap(pages, linkTargets);
|
|
371
418
|
const tagVocab = parseSchemaVocab(args.hypoDir);
|
|
372
419
|
const pageDirs = parseSchemaPageDirs(args.hypoDir);
|
|
373
420
|
|
|
@@ -382,7 +429,7 @@ for (const s of findDesignHistoryStale(args.hypoDir)) {
|
|
|
382
429
|
issue(
|
|
383
430
|
'warn',
|
|
384
431
|
`projects/${s.project}/design-history.md`,
|
|
385
|
-
`design-history stale: session-log 최신=${s.lastSession} > design-history 최신=${s.lastDesignHistory}${gap} — projects/${s.project}/design-history.md에 설계 변경 사항을 append
|
|
432
|
+
`design-history stale: session-log 설계-관련 최신=${s.lastSession} > design-history 최신=${s.lastDesignHistory}${gap} — projects/${s.project}/design-history.md에 설계 변경 사항을 append 하거나, 무-설계 세션이면 session-log 엔트리에 "ADR 없음" 마커를 명시하세요`,
|
|
386
433
|
null,
|
|
387
434
|
'W8',
|
|
388
435
|
);
|
|
@@ -474,4 +521,12 @@ if (args.json) {
|
|
|
474
521
|
}
|
|
475
522
|
}
|
|
476
523
|
|
|
477
|
-
|
|
524
|
+
// Set exitCode and let Node exit naturally rather than calling process.exit():
|
|
525
|
+
// when stdout is a pipe, the JSON write above is async, and a synchronous
|
|
526
|
+
// process.exit() tears the process down before the OS pipe buffer (64 KiB) is
|
|
527
|
+
// drained — truncating large `--json` output mid-string. Consumers that spawn
|
|
528
|
+
// this script and JSON.parse the stdout (crystallize's runLint, the PreCompact
|
|
529
|
+
// gate) then crash on the partial output. There are no pending async handles
|
|
530
|
+
// here (pure synchronous fs), so the event loop empties at end-of-script and
|
|
531
|
+
// Node flushes stdout fully before exiting with this code.
|
|
532
|
+
process.exitCode = errors.length > 0 ? 1 : 0;
|