hypomnema 1.3.2 → 1.3.4
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 -1
- package/README.md +2 -1
- package/commands/audit.md +1 -1
- package/commands/crystallize.md +3 -3
- package/commands/doctor.md +1 -1
- package/commands/feedback.md +1 -1
- 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 +1 -1
- 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/hooks/hypo-auto-commit.mjs +8 -24
- package/hooks/hypo-compact-guard.mjs +5 -2
- package/hooks/hypo-shared.mjs +318 -41
- package/package.json +1 -1
- package/scripts/crystallize.mjs +91 -14
- package/scripts/doctor.mjs +2 -2
- package/scripts/graph.mjs +2 -2
- package/scripts/init.mjs +31 -2
- package/scripts/lib/hypo-ignore.mjs +24 -0
- package/scripts/lint.mjs +7 -6
- package/scripts/query.mjs +2 -2
- package/scripts/rename.mjs +480 -17
- package/scripts/stats.mjs +2 -2
- package/scripts/verify.mjs +2 -2
- package/skills/crystallize/SKILL.md +2 -2
- 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/hypo-config.md +1 -1
- package/templates/hypo-guide.md +2 -0
- package/templates/hypo-help.md +1 -0
package/scripts/graph.mjs
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
18
18
|
import { join, relative, extname, basename } from 'path';
|
|
19
19
|
import { resolveHypoRoot, expandHome } from './lib/hypo-root.mjs';
|
|
20
|
-
import { loadHypoIgnore,
|
|
20
|
+
import { loadHypoIgnore, isScanIgnored } from './lib/hypo-ignore.mjs';
|
|
21
21
|
|
|
22
22
|
// ── arg parsing ───────────────────────────────────────────────────────────────
|
|
23
23
|
|
|
@@ -38,7 +38,7 @@ function collectPages(dir, root, pages = [], ignorePatterns = []) {
|
|
|
38
38
|
if (!existsSync(dir)) return pages;
|
|
39
39
|
for (const entry of readdirSync(dir)) {
|
|
40
40
|
const full = join(dir, entry);
|
|
41
|
-
if (
|
|
41
|
+
if (isScanIgnored(full, root, ignorePatterns)) continue;
|
|
42
42
|
const st = statSync(full);
|
|
43
43
|
if (st.isDirectory()) {
|
|
44
44
|
collectPages(full, root, pages, ignorePatterns);
|
package/scripts/init.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
renameSync,
|
|
33
33
|
unlinkSync,
|
|
34
34
|
} from 'fs';
|
|
35
|
-
import { join, basename } from 'path';
|
|
35
|
+
import { join, basename, dirname } from 'path';
|
|
36
36
|
import { homedir } from 'os';
|
|
37
37
|
import { execSync, spawnSync } from 'child_process';
|
|
38
38
|
import { fileURLToPath } from 'url';
|
|
@@ -171,7 +171,19 @@ docstring at the top of scripts/<command>.mjs.`);
|
|
|
171
171
|
|
|
172
172
|
// ── result tracking ──────────────────────────────────────────────────────────
|
|
173
173
|
|
|
174
|
-
const results = { created: [], skipped: [], merged: [], errors: [] };
|
|
174
|
+
const results = { created: [], skipped: [], merged: [], deduped: [], errors: [] };
|
|
175
|
+
|
|
176
|
+
// Stock template basename → legacy hand-authored equivalents to honor instead of
|
|
177
|
+
// injecting a duplicate. The hypo-/wiki- namespace split (the rename that
|
|
178
|
+
// produced hypo-guide from wiki-guide, etc.) means an exact-filename check misses
|
|
179
|
+
// a user's existing page and drops a 0-reference duplicate orphan beside it.
|
|
180
|
+
// Mirrors the explicit HOOK_RENAMES idiom in upgrade.mjs. hypo-guide.md is
|
|
181
|
+
// intentionally absent: the runtime reads it by name, so a mid-migration vault
|
|
182
|
+
// must still receive it even when wiki-guide.md is present.
|
|
183
|
+
const PAGE_EQUIVALENTS = {
|
|
184
|
+
'hypo-automation.md': ['wiki-automation.md'],
|
|
185
|
+
'hypo-help.md': ['wiki-help.md'],
|
|
186
|
+
};
|
|
175
187
|
|
|
176
188
|
function log(action, path) {
|
|
177
189
|
results[action].push(path);
|
|
@@ -213,6 +225,19 @@ function copyTemplate(srcName, destPath, dryRun, transform) {
|
|
|
213
225
|
log('skipped', destPath);
|
|
214
226
|
return;
|
|
215
227
|
}
|
|
228
|
+
// If a legacy hand-authored equivalent already exists, do NOT inject the stock
|
|
229
|
+
// page — that is exactly what produced a duplicate orphan. Skip LOUDLY (own
|
|
230
|
+
// bucket, ⚠ in the summary) so the user can merge manually. Fires in --dry-run
|
|
231
|
+
// too, so a dry run previews the suppression.
|
|
232
|
+
const equivalents = PAGE_EQUIVALENTS[basename(srcName)] || [];
|
|
233
|
+
const existingEquivalent = equivalents.find((name) => existsSync(join(dirname(destPath), name)));
|
|
234
|
+
if (existingEquivalent) {
|
|
235
|
+
log(
|
|
236
|
+
'deduped',
|
|
237
|
+
`${destPath} — kept existing ${existingEquivalent}; stock ${basename(srcName)} not installed (merge manually if you want the template content)`,
|
|
238
|
+
);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
216
241
|
if (!dryRun) {
|
|
217
242
|
let content = readFileSync(src, 'utf-8');
|
|
218
243
|
content = content.replace(/YYYY-MM-DD/g, new Date().toISOString().slice(0, 10));
|
|
@@ -1017,6 +1042,10 @@ if (results.skipped.length)
|
|
|
1017
1042
|
);
|
|
1018
1043
|
if (results.merged.length)
|
|
1019
1044
|
lines.push(`↪ Merged into settings.json:\n${results.merged.map((p) => ` ${p}`).join('\n')}`);
|
|
1045
|
+
if (results.deduped.length)
|
|
1046
|
+
lines.push(
|
|
1047
|
+
`⚠ Skipped to avoid a duplicate — an equivalent page already exists (${results.deduped.length}):\n${results.deduped.map((p) => ` ${p}`).join('\n')}`,
|
|
1048
|
+
);
|
|
1020
1049
|
if (results.errors.length)
|
|
1021
1050
|
lines.push(`✗ Errors:\n${results.errors.map((p) => ` ${p}`).join('\n')}`);
|
|
1022
1051
|
|
|
@@ -23,6 +23,30 @@ function globToRegex(glob) {
|
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// Generated, regenerable tool artifacts that live at the WIKI ROOT and must not
|
|
27
|
+
// pollute the knowledge catalog. These are intentionally NOT added to
|
|
28
|
+
// .hypoignore: that list also drives the pre-commit secret gate
|
|
29
|
+
// (hooks/hypo-pre-commit.mjs), so listing them there would block the report's
|
|
30
|
+
// commit and freeze every auto-commit while it sits at root. Instead they are
|
|
31
|
+
// excluded from catalog/scan only, via isScanIgnored(). The patterns are
|
|
32
|
+
// anchored to the root-relative path (no '/'), so an equivalent name nested
|
|
33
|
+
// under pages/, projects/, or sources/ is left untouched.
|
|
34
|
+
const GENERATED_ARTIFACT_RES = [/^MIGRATION-v[^/]*\.md$/, /^GRAPH_REPORT\.md$/];
|
|
35
|
+
|
|
36
|
+
export function isGeneratedArtifact(filePath, hypoDir) {
|
|
37
|
+
const rel = relative(hypoDir, filePath).replace(/\\/g, '/');
|
|
38
|
+
return GENERATED_ARTIFACT_RES.some((re) => re.test(rel));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Catalog/scan exclusion = privacy/.hypoignore patterns PLUS generated root
|
|
42
|
+
// artifacts. Use this in tools that build the knowledge catalog (lint, graph,
|
|
43
|
+
// stats, query, verify, crystallize, rename, doctor broken-links). Do NOT use it
|
|
44
|
+
// in the pre-commit gate or ingest --check, which are privacy boundaries that
|
|
45
|
+
// must stay on isIgnored()/.hypoignore alone.
|
|
46
|
+
export function isScanIgnored(filePath, hypoDir, patterns) {
|
|
47
|
+
return isIgnored(filePath, hypoDir, patterns) || isGeneratedArtifact(filePath, hypoDir);
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
export function isIgnored(filePath, hypoDir, patterns) {
|
|
27
51
|
const rel = relative(hypoDir, filePath).replace(/\\/g, '/');
|
|
28
52
|
const base = basename(filePath);
|
package/scripts/lint.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from '
|
|
|
20
20
|
import { join, relative, extname, basename } from 'path';
|
|
21
21
|
import { resolveHypoRoot, expandHome } from './lib/hypo-root.mjs';
|
|
22
22
|
import { SESSION_STATE_NEXT_HEADINGS } from '../hooks/hypo-shared.mjs';
|
|
23
|
-
import { loadHypoIgnore,
|
|
23
|
+
import { loadHypoIgnore, isScanIgnored } from './lib/hypo-ignore.mjs';
|
|
24
24
|
import { parseSchemaVocab, checkForbidden, parseSchemaPageDirs } from './lib/schema-vocab.mjs';
|
|
25
25
|
import { findDesignHistoryStale } from './lib/design-history-stale.mjs';
|
|
26
26
|
import { FEEDBACK_SCOPE_RE } from './lib/feedback-scope.mjs';
|
|
@@ -117,7 +117,7 @@ function collectPages(dir, root, pages = [], ignorePatterns = []) {
|
|
|
117
117
|
if (!existsSync(dir)) return pages;
|
|
118
118
|
for (const entry of readdirSync(dir)) {
|
|
119
119
|
const full = join(dir, entry);
|
|
120
|
-
if (
|
|
120
|
+
if (isScanIgnored(full, root, ignorePatterns)) continue;
|
|
121
121
|
const st = statSync(full);
|
|
122
122
|
if (st.isDirectory()) {
|
|
123
123
|
// `_`-prefixed dirs (e.g. pages/feedback/_drafts) hold scaffolds / scratch
|
|
@@ -164,13 +164,14 @@ function collectLinkTargets(hypoDir, ignorePatterns = []) {
|
|
|
164
164
|
if (existsSync(hypoDir)) {
|
|
165
165
|
for (const entry of readdirSync(hypoDir)) {
|
|
166
166
|
const full = join(hypoDir, entry);
|
|
167
|
-
// root-level *.md FILES only (no recursion), honoring .hypoignore
|
|
168
|
-
//
|
|
169
|
-
//
|
|
167
|
+
// root-level *.md FILES only (no recursion), honoring .hypoignore plus the
|
|
168
|
+
// scan-only generated-artifact exclusions (isScanIgnored) like collectPages
|
|
169
|
+
// — otherwise an ignored root file (e.g. a secret) or a regenerable report
|
|
170
|
+
// would resolve [[its-slug]] as valid, a false negative.
|
|
170
171
|
if (
|
|
171
172
|
extname(entry) === '.md' &&
|
|
172
173
|
!entry.startsWith('.') &&
|
|
173
|
-
!
|
|
174
|
+
!isScanIgnored(full, hypoDir, ignorePatterns) &&
|
|
174
175
|
statSync(full).isFile()
|
|
175
176
|
) {
|
|
176
177
|
targets.push(entry.replace(/\.md$/, ''));
|
package/scripts/query.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
20
20
|
import { join, relative, extname } from 'path';
|
|
21
21
|
import { resolveHypoRoot, expandHome } from './lib/hypo-root.mjs';
|
|
22
|
-
import { loadHypoIgnore,
|
|
22
|
+
import { loadHypoIgnore, isScanIgnored } from './lib/hypo-ignore.mjs';
|
|
23
23
|
|
|
24
24
|
// ── arg parsing ──────────────────────────────────────────────────────────────
|
|
25
25
|
|
|
@@ -42,7 +42,7 @@ function collectMdFiles(dir, root, acc = [], ignorePatterns = []) {
|
|
|
42
42
|
for (const entry of readdirSync(dir)) {
|
|
43
43
|
if (entry.startsWith('.')) continue;
|
|
44
44
|
const full = join(dir, entry);
|
|
45
|
-
if (
|
|
45
|
+
if (isScanIgnored(full, root, ignorePatterns)) continue;
|
|
46
46
|
const st = statSync(full);
|
|
47
47
|
if (st.isDirectory()) collectMdFiles(full, root, acc, ignorePatterns);
|
|
48
48
|
else if (extname(entry) === '.md') acc.push({ path: full, rel: relative(root, full) });
|