@theglitchking/hit-em-with-the-docs 2.2.0 → 2.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.
Files changed (118) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +7 -0
  4. package/commands/audit-facts.md +25 -0
  5. package/commands/cite.md +17 -0
  6. package/commands/extract-facts.md +66 -0
  7. package/commands/find-citers.md +16 -0
  8. package/commands/help.md +86 -0
  9. package/commands/migrate-incident.md +18 -0
  10. package/dist/action/core/audit/auditor.d.ts +14 -0
  11. package/dist/action/core/audit/auditor.d.ts.map +1 -1
  12. package/dist/action/core/domains/classifier.d.ts +15 -1
  13. package/dist/action/core/domains/classifier.d.ts.map +1 -1
  14. package/dist/action/core/knowledge-base/audit.d.ts +67 -0
  15. package/dist/action/core/knowledge-base/audit.d.ts.map +1 -0
  16. package/dist/action/core/knowledge-base/cite.d.ts +41 -0
  17. package/dist/action/core/knowledge-base/cite.d.ts.map +1 -0
  18. package/dist/action/core/knowledge-base/citers.d.ts +119 -0
  19. package/dist/action/core/knowledge-base/citers.d.ts.map +1 -0
  20. package/dist/action/core/knowledge-base/extract.d.ts +61 -0
  21. package/dist/action/core/knowledge-base/extract.d.ts.map +1 -0
  22. package/dist/action/core/knowledge-base/fix-legacy-layout.d.ts +61 -0
  23. package/dist/action/core/knowledge-base/fix-legacy-layout.d.ts.map +1 -0
  24. package/dist/action/core/knowledge-base/migrate.d.ts +67 -0
  25. package/dist/action/core/knowledge-base/migrate.d.ts.map +1 -0
  26. package/dist/action/core/maintain/orchestrator.d.ts.map +1 -1
  27. package/dist/action/core/metadata/errors.d.ts +62 -0
  28. package/dist/action/core/metadata/errors.d.ts.map +1 -0
  29. package/dist/action/core/metadata/schema.d.ts +709 -58
  30. package/dist/action/core/metadata/schema.d.ts.map +1 -1
  31. package/dist/action/generators/facts-index.d.ts +27 -0
  32. package/dist/action/generators/facts-index.d.ts.map +1 -0
  33. package/dist/action/generators/incidents-index.d.ts +21 -0
  34. package/dist/action/generators/incidents-index.d.ts.map +1 -0
  35. package/dist/action/generators/symptoms-index.d.ts +20 -0
  36. package/dist/action/generators/symptoms-index.d.ts.map +1 -0
  37. package/dist/action/generators/table.d.ts +8 -0
  38. package/dist/action/generators/table.d.ts.map +1 -0
  39. package/dist/action/index.d.ts +7 -0
  40. package/dist/action/index.d.ts.map +1 -1
  41. package/dist/action/index.js +4 -4
  42. package/dist/action/utils/config.d.ts +99 -0
  43. package/dist/action/utils/config.d.ts.map +1 -0
  44. package/dist/cli/index.js +315 -1
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/core/audit/auditor.d.ts +14 -0
  47. package/dist/core/audit/auditor.d.ts.map +1 -1
  48. package/dist/core/audit/auditor.js +19 -3
  49. package/dist/core/audit/auditor.js.map +1 -1
  50. package/dist/core/domains/classifier.d.ts +15 -1
  51. package/dist/core/domains/classifier.d.ts.map +1 -1
  52. package/dist/core/domains/classifier.js +83 -1
  53. package/dist/core/domains/classifier.js.map +1 -1
  54. package/dist/core/knowledge-base/audit.d.ts +67 -0
  55. package/dist/core/knowledge-base/audit.d.ts.map +1 -0
  56. package/dist/core/knowledge-base/audit.js +158 -0
  57. package/dist/core/knowledge-base/audit.js.map +1 -0
  58. package/dist/core/knowledge-base/cite.d.ts +41 -0
  59. package/dist/core/knowledge-base/cite.d.ts.map +1 -0
  60. package/dist/core/knowledge-base/cite.js +82 -0
  61. package/dist/core/knowledge-base/cite.js.map +1 -0
  62. package/dist/core/knowledge-base/citers.d.ts +119 -0
  63. package/dist/core/knowledge-base/citers.d.ts.map +1 -0
  64. package/dist/core/knowledge-base/citers.js +249 -0
  65. package/dist/core/knowledge-base/citers.js.map +1 -0
  66. package/dist/core/knowledge-base/extract.d.ts +61 -0
  67. package/dist/core/knowledge-base/extract.d.ts.map +1 -0
  68. package/dist/core/knowledge-base/extract.js +132 -0
  69. package/dist/core/knowledge-base/extract.js.map +1 -0
  70. package/dist/core/knowledge-base/fix-legacy-layout.d.ts +61 -0
  71. package/dist/core/knowledge-base/fix-legacy-layout.d.ts.map +1 -0
  72. package/dist/core/knowledge-base/fix-legacy-layout.js +189 -0
  73. package/dist/core/knowledge-base/fix-legacy-layout.js.map +1 -0
  74. package/dist/core/knowledge-base/migrate.d.ts +67 -0
  75. package/dist/core/knowledge-base/migrate.d.ts.map +1 -0
  76. package/dist/core/knowledge-base/migrate.js +147 -0
  77. package/dist/core/knowledge-base/migrate.js.map +1 -0
  78. package/dist/core/maintain/orchestrator.d.ts.map +1 -1
  79. package/dist/core/maintain/orchestrator.js +48 -1
  80. package/dist/core/maintain/orchestrator.js.map +1 -1
  81. package/dist/core/metadata/errors.d.ts +62 -0
  82. package/dist/core/metadata/errors.d.ts.map +1 -0
  83. package/dist/core/metadata/errors.js +95 -0
  84. package/dist/core/metadata/errors.js.map +1 -0
  85. package/dist/core/metadata/schema.d.ts +709 -58
  86. package/dist/core/metadata/schema.d.ts.map +1 -1
  87. package/dist/core/metadata/schema.js +453 -79
  88. package/dist/core/metadata/schema.js.map +1 -1
  89. package/dist/generators/facts-index.d.ts +27 -0
  90. package/dist/generators/facts-index.d.ts.map +1 -0
  91. package/dist/generators/facts-index.js +111 -0
  92. package/dist/generators/facts-index.js.map +1 -0
  93. package/dist/generators/incidents-index.d.ts +21 -0
  94. package/dist/generators/incidents-index.d.ts.map +1 -0
  95. package/dist/generators/incidents-index.js +79 -0
  96. package/dist/generators/incidents-index.js.map +1 -0
  97. package/dist/generators/symptoms-index.d.ts +20 -0
  98. package/dist/generators/symptoms-index.d.ts.map +1 -0
  99. package/dist/generators/symptoms-index.js +123 -0
  100. package/dist/generators/symptoms-index.js.map +1 -0
  101. package/dist/generators/table.d.ts +8 -0
  102. package/dist/generators/table.d.ts.map +1 -0
  103. package/dist/generators/table.js +23 -0
  104. package/dist/generators/table.js.map +1 -0
  105. package/dist/index.d.ts +7 -0
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +8 -0
  108. package/dist/index.js.map +1 -1
  109. package/dist/utils/config.d.ts +99 -0
  110. package/dist/utils/config.d.ts.map +1 -0
  111. package/dist/utils/config.js +95 -0
  112. package/dist/utils/config.js.map +1 -0
  113. package/package.json +1 -1
  114. package/templates/knowledge-base/README.md +31 -0
  115. package/templates/knowledge-base/fact.template.md +44 -0
  116. package/templates/knowledge-base/incident-facts.template.md +31 -0
  117. package/templates/knowledge-base/incident-narrative.template.md +50 -0
  118. package/templates/knowledge-base/playbook-symptoms.template.md +74 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Citation graph walker for knowledge-base primitives (2.3.0).
3
+ *
4
+ * Builds the per-fact citation index from three source kinds:
5
+ * - Facts (`<vault>/facts/<id>.md`) — the canonical fact corpus.
6
+ * - Incident-facts bridges (`<vault>/incidents/<slug>/facts.md`) — produced /
7
+ * strengthened / weakened edges.
8
+ * - Playbooks (any file with `symptoms: [...]` frontmatter) — `cites` edges
9
+ * from each symptom entry to the facts the playbook step depends on.
10
+ *
11
+ * Used by:
12
+ * - `facts/INDEX.md` generator (PR2) — to compute citer counts per fact.
13
+ * - `hewtd find-citers <fact-id>` CLI command (PR3) — typed structure output.
14
+ * - Dangling-citation linting (future PR) — flags `cites:` pointing at
15
+ * non-existent facts.
16
+ */
17
+ export interface FactRef {
18
+ /** Fact id (from frontmatter `id:`). */
19
+ id: string;
20
+ /** Absolute path to the fact's markdown file. */
21
+ path: string;
22
+ /** Path relative to the project root (for stable, diff-friendly indexes). */
23
+ relPath: string;
24
+ /** Frontmatter title. */
25
+ title: string;
26
+ /** Confidence enum value, if present. */
27
+ confidence: string | undefined;
28
+ /** YYYY-MM-DD or null if missing/malformed. */
29
+ lastVerified: string | undefined;
30
+ /** Tags from frontmatter. */
31
+ tags: string[];
32
+ }
33
+ export interface IncidentRef {
34
+ /** Incident id (from narrative.md frontmatter `id:`). */
35
+ id: string;
36
+ /** Absolute path to the incident folder. */
37
+ folderPath: string;
38
+ /** Project-relative folder path. */
39
+ relFolderPath: string;
40
+ /** Frontmatter title from narrative.md. */
41
+ title: string;
42
+ /** YYYY-MM-DD. */
43
+ date: string | undefined;
44
+ severity: string | undefined;
45
+ resolution_status: string | undefined;
46
+ components: string[];
47
+ /** Fact ids produced by this incident (from facts.md). */
48
+ produced: string[];
49
+ strengthened: string[];
50
+ weakened: string[];
51
+ }
52
+ export interface PlaybookCitation {
53
+ /** Project-relative path to the playbook. */
54
+ relPath: string;
55
+ /** Absolute path. */
56
+ path: string;
57
+ /** Frontmatter title. */
58
+ title: string;
59
+ /** Anchor within the playbook (e.g. `#vault-down-auth-staging`). */
60
+ target: string;
61
+ /** Symptom kind that triggered the citation. */
62
+ kind: 'alert_name' | 'user_phrase' | 'error_pattern';
63
+ /** The matching key (string or array, depending on kind). */
64
+ key: string | string[];
65
+ severity: string | undefined;
66
+ }
67
+ export interface CiterIndex {
68
+ /** All discovered facts, keyed by id. */
69
+ facts: Map<string, FactRef>;
70
+ /** All discovered incident folders, keyed by id. */
71
+ incidents: Map<string, IncidentRef>;
72
+ /**
73
+ * Citation edges per fact id. A fact id appearing here may not have a
74
+ * matching entry in `facts` — that's a dangling citation, detected by
75
+ * the caller.
76
+ */
77
+ citers: Map<string, PlaybookCitation[]>;
78
+ }
79
+ /**
80
+ * Typed structure returned by `findCiters(factId)`. Matches the shape the
81
+ * `hewtd find-citers` CLI command will print and the structure documented
82
+ * in `docs/knowledge-base-primitives.md`.
83
+ */
84
+ export interface FindCitersResult {
85
+ fact_id: string;
86
+ fact_exists: boolean;
87
+ citers: string[];
88
+ incidents_produced_in: string[];
89
+ incidents_strengthened_by: string[];
90
+ incidents_weakened_by: string[];
91
+ }
92
+ export interface BuildCiterIndexOptions {
93
+ /** Absolute path to the project root. */
94
+ projectRoot: string;
95
+ /** Absolute path to the vault root (default config: <root>/.documentation/knowledge-base/). */
96
+ vaultRoot: string;
97
+ /**
98
+ * Glob patterns (absolute, project-rooted) used to scan playbooks for
99
+ * `symptoms:` frontmatter blocks. The walker filters by frontmatter
100
+ * presence — files without a `symptoms:` block are skipped quickly.
101
+ */
102
+ playbookGlobs: string[];
103
+ }
104
+ /**
105
+ * Walk the corpus and build a citer index. Designed to be cheap on no-vault
106
+ * trees: if the vault root doesn't exist, returns an empty index without
107
+ * scanning playbooks for symptoms (no point — no facts to cite).
108
+ */
109
+ export declare function buildCiterIndex(options: BuildCiterIndexOptions): Promise<CiterIndex>;
110
+ /**
111
+ * Public lookup: typed structure for one fact id. Matches the shape the
112
+ * `hewtd find-citers` CLI command prints.
113
+ */
114
+ export declare function findCitersInIndex(index: CiterIndex, factId: string): FindCitersResult;
115
+ /**
116
+ * Count distinct citers for a fact id. Used by the facts-index generator.
117
+ */
118
+ export declare function citerCount(index: CiterIndex, factId: string): number;
119
+ //# sourceMappingURL=citers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citers.d.ts","sourceRoot":"","sources":["../../../src/core/knowledge-base/citers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,MAAM,WAAW,OAAO;IACtB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,+CAA+C;IAC/C,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,6BAA6B;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,eAAe,CAAC;IACrD,6DAA6D;IAC7D,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC;;;;OAIG;IACH,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;CACzC;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAMD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAuDrB;AAiJD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,GACb,gBAAgB,CAyBlB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQpE"}
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Citation graph walker for knowledge-base primitives (2.3.0).
3
+ *
4
+ * Builds the per-fact citation index from three source kinds:
5
+ * - Facts (`<vault>/facts/<id>.md`) — the canonical fact corpus.
6
+ * - Incident-facts bridges (`<vault>/incidents/<slug>/facts.md`) — produced /
7
+ * strengthened / weakened edges.
8
+ * - Playbooks (any file with `symptoms: [...]` frontmatter) — `cites` edges
9
+ * from each symptom entry to the facts the playbook step depends on.
10
+ *
11
+ * Used by:
12
+ * - `facts/INDEX.md` generator (PR2) — to compute citer counts per fact.
13
+ * - `hewtd find-citers <fact-id>` CLI command (PR3) — typed structure output.
14
+ * - Dangling-citation linting (future PR) — flags `cites:` pointing at
15
+ * non-existent facts.
16
+ */
17
+ import { readFile } from 'fs/promises';
18
+ import { join, relative, resolve } from 'path';
19
+ import { glob } from 'glob';
20
+ import matter from 'gray-matter';
21
+ const isString = (v) => typeof v === 'string';
22
+ const isStringArray = (v) => Array.isArray(v) && v.every(isString);
23
+ /**
24
+ * Walk the corpus and build a citer index. Designed to be cheap on no-vault
25
+ * trees: if the vault root doesn't exist, returns an empty index without
26
+ * scanning playbooks for symptoms (no point — no facts to cite).
27
+ */
28
+ export async function buildCiterIndex(options) {
29
+ const { projectRoot, vaultRoot, playbookGlobs } = options;
30
+ const facts = new Map();
31
+ const incidents = new Map();
32
+ const citers = new Map();
33
+ // ----- Facts -----
34
+ const factFiles = await glob(join(vaultRoot, 'facts/*.md'), {
35
+ nodir: true,
36
+ absolute: true,
37
+ });
38
+ for (const factPath of factFiles) {
39
+ const ref = await readFactRef(factPath, projectRoot);
40
+ if (ref)
41
+ facts.set(ref.id, ref);
42
+ }
43
+ // ----- Incidents -----
44
+ const narrativeFiles = await glob(join(vaultRoot, 'incidents/*/narrative.md'), { nodir: true, absolute: true });
45
+ for (const narrativePath of narrativeFiles) {
46
+ const ref = await readIncidentRef(narrativePath, projectRoot);
47
+ if (ref)
48
+ incidents.set(ref.id, ref);
49
+ }
50
+ // Short-circuit: if no facts and no incidents, no point scanning playbooks.
51
+ if (facts.size === 0 && incidents.size === 0) {
52
+ return { facts, incidents, citers };
53
+ }
54
+ // ----- Playbook citations -----
55
+ // Cheap pre-filter: read each candidate file and skip if frontmatter has
56
+ // no `symptoms` key. Avoids paying for full YAML parse on every doc.
57
+ const playbookCandidates = new Set();
58
+ for (const pattern of playbookGlobs) {
59
+ const matches = await glob(pattern, { nodir: true, absolute: true });
60
+ for (const m of matches)
61
+ playbookCandidates.add(m);
62
+ }
63
+ for (const playbookPath of playbookCandidates) {
64
+ const cits = await extractPlaybookCitations(playbookPath, projectRoot);
65
+ for (const c of cits) {
66
+ // Each citation has a list of fact ids implicit in the symptoms entry.
67
+ const factIds = c.factIds;
68
+ for (const factId of factIds) {
69
+ const existing = citers.get(factId) ?? [];
70
+ existing.push(c.citation);
71
+ citers.set(factId, existing);
72
+ }
73
+ }
74
+ }
75
+ return { facts, incidents, citers };
76
+ }
77
+ async function readFactRef(path, projectRoot) {
78
+ try {
79
+ const content = await readFile(path, 'utf-8');
80
+ const { data } = matter(content);
81
+ if (data.tier !== 'fact' || !isString(data.id))
82
+ return null;
83
+ const tags = isStringArray(data.tags) ? data.tags : [];
84
+ const lastVerified = normalizeDate(data.last_verified);
85
+ return {
86
+ id: data.id,
87
+ path,
88
+ relPath: relative(projectRoot, path),
89
+ title: isString(data.title) ? data.title : data.id,
90
+ confidence: isString(data.confidence) ? data.confidence : undefined,
91
+ lastVerified,
92
+ tags,
93
+ };
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ async function readIncidentRef(narrativePath, projectRoot) {
100
+ try {
101
+ const narrativeContent = await readFile(narrativePath, 'utf-8');
102
+ const { data: narrative } = matter(narrativeContent);
103
+ if (narrative.tier !== 'incident-narrative' || !isString(narrative.id)) {
104
+ return null;
105
+ }
106
+ const folderPath = resolve(narrativePath, '..');
107
+ const factsMdPath = join(folderPath, 'facts.md');
108
+ let produced = [];
109
+ let strengthened = [];
110
+ let weakened = [];
111
+ try {
112
+ const factsContent = await readFile(factsMdPath, 'utf-8');
113
+ const { data: facts } = matter(factsContent);
114
+ if (facts.tier === 'incident-facts') {
115
+ if (isStringArray(facts.produced))
116
+ produced = facts.produced;
117
+ if (isStringArray(facts.strengthened))
118
+ strengthened = facts.strengthened;
119
+ if (isStringArray(facts.weakened))
120
+ weakened = facts.weakened;
121
+ }
122
+ }
123
+ catch {
124
+ // facts.md missing — incident has no produced/strengthened/weakened edges
125
+ }
126
+ return {
127
+ id: narrative.id,
128
+ folderPath,
129
+ relFolderPath: relative(projectRoot, folderPath),
130
+ title: isString(narrative.title) ? narrative.title : narrative.id,
131
+ date: normalizeDate(narrative.date),
132
+ severity: isString(narrative.severity) ? narrative.severity : undefined,
133
+ resolution_status: isString(narrative.resolution_status)
134
+ ? narrative.resolution_status
135
+ : undefined,
136
+ components: isStringArray(narrative.components) ? narrative.components : [],
137
+ produced,
138
+ strengthened,
139
+ weakened,
140
+ };
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ }
146
+ async function extractPlaybookCitations(playbookPath, projectRoot) {
147
+ try {
148
+ const content = await readFile(playbookPath, 'utf-8');
149
+ const { data } = matter(content);
150
+ if (!Array.isArray(data.symptoms) || data.symptoms.length === 0) {
151
+ return [];
152
+ }
153
+ const title = isString(data.title) ? data.title : playbookPath;
154
+ const results = [];
155
+ for (const sym of data.symptoms) {
156
+ const s = sym;
157
+ if (!isString(s.target) || !isStringArray(s.cites))
158
+ continue;
159
+ let kind;
160
+ let key;
161
+ if (isString(s.alert_name)) {
162
+ kind = 'alert_name';
163
+ key = s.alert_name;
164
+ }
165
+ else if (isStringArray(s.user_phrase) || isString(s.user_phrase)) {
166
+ kind = 'user_phrase';
167
+ key = s.user_phrase;
168
+ }
169
+ else if (isString(s.error_pattern)) {
170
+ kind = 'error_pattern';
171
+ key = s.error_pattern;
172
+ }
173
+ else {
174
+ continue; // malformed — caught by audit-strict, ignored here
175
+ }
176
+ results.push({
177
+ citation: {
178
+ relPath: relative(projectRoot, playbookPath),
179
+ path: playbookPath,
180
+ title,
181
+ target: s.target,
182
+ kind,
183
+ key,
184
+ severity: isString(s.severity) ? s.severity : undefined,
185
+ },
186
+ factIds: s.cites,
187
+ });
188
+ }
189
+ return results;
190
+ }
191
+ catch {
192
+ return [];
193
+ }
194
+ }
195
+ function normalizeDate(value) {
196
+ if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
197
+ return value;
198
+ }
199
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
200
+ return value.toISOString().slice(0, 10);
201
+ }
202
+ return undefined;
203
+ }
204
+ /**
205
+ * Public lookup: typed structure for one fact id. Matches the shape the
206
+ * `hewtd find-citers` CLI command prints.
207
+ */
208
+ export function findCitersInIndex(index, factId) {
209
+ // Dedup citers by playbook relPath — multiple symptoms entries in the same
210
+ // playbook citing the same fact count as ONE citer.
211
+ const citerSet = new Set();
212
+ for (const c of index.citers.get(factId) ?? []) {
213
+ citerSet.add(c.relPath);
214
+ }
215
+ const incidents_produced_in = [];
216
+ const incidents_strengthened_by = [];
217
+ const incidents_weakened_by = [];
218
+ for (const inc of index.incidents.values()) {
219
+ if (inc.produced.includes(factId))
220
+ incidents_produced_in.push(inc.id);
221
+ if (inc.strengthened.includes(factId))
222
+ incidents_strengthened_by.push(inc.id);
223
+ if (inc.weakened.includes(factId))
224
+ incidents_weakened_by.push(inc.id);
225
+ }
226
+ return {
227
+ fact_id: factId,
228
+ fact_exists: index.facts.has(factId),
229
+ citers: [...citerSet].sort(),
230
+ incidents_produced_in: incidents_produced_in.sort(),
231
+ incidents_strengthened_by: incidents_strengthened_by.sort(),
232
+ incidents_weakened_by: incidents_weakened_by.sort(),
233
+ };
234
+ }
235
+ /**
236
+ * Count distinct citers for a fact id. Used by the facts-index generator.
237
+ */
238
+ export function citerCount(index, factId) {
239
+ const list = index.citers.get(factId);
240
+ if (!list)
241
+ return 0;
242
+ // Distinct by playbook relPath (multiple symptoms in the same playbook
243
+ // citing the same fact count once).
244
+ const seen = new Set();
245
+ for (const c of list)
246
+ seen.add(c.relPath);
247
+ return seen.size;
248
+ }
249
+ //# sourceMappingURL=citers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citers.js","sourceRoot":"","sources":["../../../src/core/knowledge-base/citers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Fact-extraction writer (2.3.0 PR3).
3
+ *
4
+ * The CLI side of `hewtd extract-facts <incident-folder>`. Takes accepted
5
+ * fact specs and writes them to `<vault>/facts/<id>.md` with provenance
6
+ * auto-populated to the source incident folder. Idempotent — re-running
7
+ * with an already-committed fact spec is a no-op.
8
+ *
9
+ * The LLM proposal step (read narrative → suggest fact specs) lives in the
10
+ * `/hit-em-with-the-docs:extract-facts` slash command, NOT here. This
11
+ * module is the deterministic writer that the slash command invokes after
12
+ * the user accepts proposals.
13
+ */
14
+ export interface FactSpec {
15
+ id: string;
16
+ title: string;
17
+ confidence: 'high' | 'medium' | 'low' | 'hypothesis';
18
+ verifyCommand?: string;
19
+ /** Claim paragraph (will be inserted as `## Claim`). */
20
+ claim: string;
21
+ /** Optional how-to-verify paragraph. */
22
+ howToVerify?: string;
23
+ /** Optional consequences paragraph. */
24
+ consequences?: string;
25
+ /** Optional tags (will be merged with auto-derived tags). */
26
+ tags?: string[];
27
+ /** Optional domains. Defaults to ['knowledge-base'] if not specified. */
28
+ domains?: string[];
29
+ }
30
+ export interface ExtractFactsOptions {
31
+ /** Absolute path to the incident folder (must contain narrative.md). */
32
+ incidentFolder: string;
33
+ /** Absolute path to the vault root. Facts go into `<vault>/facts/`. */
34
+ vaultRoot: string;
35
+ /** Project root for provenance relpath computation. */
36
+ projectRoot: string;
37
+ /** Accepted fact specs (already curated by the LLM proposer + user). */
38
+ accept: FactSpec[];
39
+ /** If true, returns the plan without writing files. */
40
+ dryRun?: boolean;
41
+ }
42
+ export interface ExtractedFact {
43
+ id: string;
44
+ factPath: string;
45
+ action: 'created' | 'already_exists';
46
+ content: string;
47
+ }
48
+ export interface ExtractFactsResult {
49
+ incidentId: string;
50
+ incidentFolder: string;
51
+ extractedFacts: ExtractedFact[];
52
+ /** Whether the incident's facts.md was updated. */
53
+ factsMdUpdated: boolean;
54
+ factsMdPath: string;
55
+ }
56
+ export declare function extractFacts(options: ExtractFactsOptions): Promise<ExtractFactsResult>;
57
+ /**
58
+ * Re-export the resolved vault root so callers don't have to recompute it.
59
+ */
60
+ export declare function defaultVaultRoot(projectRoot: string): string;
61
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/core/knowledge-base/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,YAAY,CAAC;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,gBAAgB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,mDAAmD;IACnD,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAqG7B;AAsBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE5D"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Fact-extraction writer (2.3.0 PR3).
3
+ *
4
+ * The CLI side of `hewtd extract-facts <incident-folder>`. Takes accepted
5
+ * fact specs and writes them to `<vault>/facts/<id>.md` with provenance
6
+ * auto-populated to the source incident folder. Idempotent — re-running
7
+ * with an already-committed fact spec is a no-op.
8
+ *
9
+ * The LLM proposal step (read narrative → suggest fact specs) lives in the
10
+ * `/hit-em-with-the-docs:extract-facts` slash command, NOT here. This
11
+ * module is the deterministic writer that the slash command invokes after
12
+ * the user accepts proposals.
13
+ */
14
+ import { readFile, writeFile, mkdir } from 'fs/promises';
15
+ import { existsSync } from 'fs';
16
+ import { basename, dirname, join, relative, resolve } from 'path';
17
+ import matter from 'gray-matter';
18
+ export async function extractFacts(options) {
19
+ const { incidentFolder, vaultRoot, projectRoot, accept, dryRun = false } = options;
20
+ const narrativePath = join(incidentFolder, 'narrative.md');
21
+ const narrativeRaw = await readFile(narrativePath, 'utf-8');
22
+ const narrative = matter(narrativeRaw);
23
+ const incidentId = typeof narrative.data.id === 'string'
24
+ ? narrative.data.id
25
+ : basename(incidentFolder);
26
+ const provenanceRelPath = relative(projectRoot, incidentFolder).replace(/\\/g, '/');
27
+ const factsDir = join(vaultRoot, 'facts');
28
+ const today = new Date().toISOString().slice(0, 10);
29
+ const extractedFacts = [];
30
+ for (const spec of accept) {
31
+ const factPath = join(factsDir, `${spec.id}.md`);
32
+ if (existsSync(factPath)) {
33
+ extractedFacts.push({
34
+ id: spec.id,
35
+ factPath,
36
+ action: 'already_exists',
37
+ content: await readFile(factPath, 'utf-8'),
38
+ });
39
+ continue;
40
+ }
41
+ const frontmatter = {
42
+ title: spec.title,
43
+ tier: 'fact',
44
+ domains: spec.domains ?? ['knowledge-base'],
45
+ status: 'active',
46
+ last_updated: today,
47
+ id: spec.id,
48
+ confidence: spec.confidence,
49
+ last_verified: today,
50
+ provenance: [provenanceRelPath + '/'],
51
+ tags: spec.tags ?? [],
52
+ };
53
+ if (spec.verifyCommand) {
54
+ frontmatter.verify_command = spec.verifyCommand;
55
+ }
56
+ const body = renderFactBody(spec);
57
+ const content = matter.stringify(body, frontmatter);
58
+ if (!dryRun) {
59
+ await mkdir(dirname(factPath), { recursive: true });
60
+ await writeFile(factPath, content, 'utf-8');
61
+ }
62
+ extractedFacts.push({
63
+ id: spec.id,
64
+ factPath,
65
+ action: 'created',
66
+ content,
67
+ });
68
+ }
69
+ // Update incident's facts.md `produced:` list (idempotent — only adds new
70
+ // ids, never removes existing ones).
71
+ const factsMdPath = join(incidentFolder, 'facts.md');
72
+ let factsMdUpdated = false;
73
+ if (existsSync(factsMdPath)) {
74
+ const factsMdRaw = await readFile(factsMdPath, 'utf-8');
75
+ const factsMd = matter(factsMdRaw);
76
+ // Important: gray-matter caches parse results keyed by content. Mutating
77
+ // factsMd.data leaks across calls with identical input strings (notably
78
+ // in tests). Always build a fresh data object.
79
+ const existing = Array.isArray(factsMd.data.produced)
80
+ ? [...factsMd.data.produced]
81
+ : [];
82
+ const newIds = extractedFacts
83
+ .filter((e) => e.action === 'created')
84
+ .map((e) => e.id);
85
+ const toAdd = newIds.filter((id) => !existing.includes(id));
86
+ if (toAdd.length > 0) {
87
+ const newData = {
88
+ ...factsMd.data,
89
+ produced: [...existing, ...toAdd],
90
+ last_updated: today,
91
+ };
92
+ const newContent = matter.stringify(factsMd.content, newData);
93
+ if (!dryRun) {
94
+ await writeFile(factsMdPath, newContent, 'utf-8');
95
+ }
96
+ factsMdUpdated = true;
97
+ }
98
+ }
99
+ return {
100
+ incidentId,
101
+ incidentFolder,
102
+ extractedFacts,
103
+ factsMdUpdated,
104
+ factsMdPath,
105
+ };
106
+ }
107
+ function renderFactBody(spec) {
108
+ const sections = [];
109
+ sections.push(`# ${spec.title}`);
110
+ sections.push('');
111
+ sections.push('## Claim');
112
+ sections.push(spec.claim.trim());
113
+ sections.push('');
114
+ if (spec.howToVerify) {
115
+ sections.push('## How to verify');
116
+ sections.push(spec.howToVerify.trim());
117
+ sections.push('');
118
+ }
119
+ if (spec.consequences) {
120
+ sections.push('## Consequences');
121
+ sections.push(spec.consequences.trim());
122
+ sections.push('');
123
+ }
124
+ return '\n' + sections.join('\n');
125
+ }
126
+ /**
127
+ * Re-export the resolved vault root so callers don't have to recompute it.
128
+ */
129
+ export function defaultVaultRoot(projectRoot) {
130
+ return resolve(projectRoot, '.documentation/knowledge-base');
131
+ }
132
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/core/knowledge-base/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Migrate 2.3.0-buggy incident folders into the canonical layout (2.4.0).
3
+ *
4
+ * In 2.3.0, `hewtd migrate-incident <flat-file>` wrote the new folder
5
+ * AS A SIBLING of the flat file. When the flat file lived at the vault
6
+ * root (the documented example), the resulting folder ended up at
7
+ * `<vault-root>/<slug>/` instead of `<vault-root>/incidents/<slug>/`.
8
+ * The `incidents/INDEX.md` generator scans only the canonical path, so
9
+ * those folders were silently absent from the index.
10
+ *
11
+ * This module:
12
+ * 1. Detects legacy folders — anything matching `<vault-root>/<slug>/`
13
+ * that contains a valid `narrative.md` AND isn't already under the
14
+ * canonical `<vault-root>/incidents/` parent.
15
+ * 2. Moves them into `<vault-root>/incidents/<slug>/` (preserving any
16
+ * `facts.md` / `evidence/` contents).
17
+ * 3. Rewrites `provenance:` references in any `<vault-root>/facts/*.md`
18
+ * files that pointed at the old folder paths.
19
+ *
20
+ * Idempotent: re-running on a clean vault is a no-op with a status report.
21
+ */
22
+ export interface FixLegacyLayoutOptions {
23
+ /** Absolute vault root. */
24
+ vaultRoot: string;
25
+ /** Project root — used for relative-path provenance rewrites. */
26
+ projectRoot: string;
27
+ /** If true, returns the plan without writing. */
28
+ dryRun?: boolean;
29
+ }
30
+ export interface MovedFolder {
31
+ /** Slug (folder basename), e.g. `2026-04-23-keycloak-idle-session`. */
32
+ slug: string;
33
+ /** Absolute source path (legacy). */
34
+ from: string;
35
+ /** Absolute destination path (canonical). */
36
+ to: string;
37
+ }
38
+ export interface RewrittenProvenance {
39
+ /** Absolute fact file path. */
40
+ factPath: string;
41
+ /** Old provenance string that was replaced. */
42
+ oldRef: string;
43
+ /** New provenance string. */
44
+ newRef: string;
45
+ }
46
+ export interface FixLegacyLayoutResult {
47
+ vaultRoot: string;
48
+ moved: MovedFolder[];
49
+ rewrittenProvenance: RewrittenProvenance[];
50
+ /** Paths that LOOKED like incident folders but were ambiguous. Reported but not moved. */
51
+ skipped: {
52
+ path: string;
53
+ reason: string;
54
+ }[];
55
+ action: 'fixed' | 'no_op' | 'dry_run';
56
+ }
57
+ /**
58
+ * Discover and (optionally) relocate 2.3.0-layout incident folders.
59
+ */
60
+ export declare function fixLegacyLayout(options: FixLegacyLayoutOptions): Promise<FixLegacyLayoutResult>;
61
+ //# sourceMappingURL=fix-legacy-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-legacy-layout.d.ts","sourceRoot":"","sources":["../../../src/core/knowledge-base/fix-legacy-layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,MAAM,WAAW,sBAAsB;IACrC,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,mBAAmB,EAAE,mBAAmB,EAAE,CAAC;IAC3C,0FAA0F;IAC1F,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;CACvC;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAgKhC"}