@cakemail-org/cakemail-cli 1.7.0 → 2.0.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 (198) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +40 -0
  3. package/.env.test.example +45 -0
  4. package/CHANGELOG.md +1031 -0
  5. package/README.md +41 -37
  6. package/audit-formats.js +128 -0
  7. package/cakemail.rb +20 -0
  8. package/dist/client.js +1 -1
  9. package/dist/client.js.map +1 -1
  10. package/dist/commands/account.js +1 -1
  11. package/dist/commands/account.js.map +1 -1
  12. package/dist/commands/attributes.js +1 -1
  13. package/dist/commands/attributes.js.map +1 -1
  14. package/dist/commands/campaigns.js +1 -1
  15. package/dist/commands/campaigns.js.map +1 -1
  16. package/dist/commands/contacts.js +1 -1
  17. package/dist/commands/contacts.js.map +1 -1
  18. package/dist/commands/emails.js +1 -1
  19. package/dist/commands/emails.js.map +1 -1
  20. package/dist/commands/interests.js +1 -1
  21. package/dist/commands/interests.js.map +1 -1
  22. package/dist/commands/lists.js +1 -1
  23. package/dist/commands/lists.js.map +1 -1
  24. package/dist/commands/logs.js +1 -1
  25. package/dist/commands/logs.js.map +1 -1
  26. package/dist/commands/reports.js +1 -1
  27. package/dist/commands/reports.js.map +1 -1
  28. package/dist/commands/segments.js +1 -1
  29. package/dist/commands/segments.js.map +1 -1
  30. package/dist/commands/senders.js +1 -1
  31. package/dist/commands/senders.js.map +1 -1
  32. package/dist/commands/suppressed.js +1 -1
  33. package/dist/commands/suppressed.js.map +1 -1
  34. package/dist/commands/tags.js +1 -1
  35. package/dist/commands/tags.js.map +1 -1
  36. package/dist/commands/templates.js +1 -1
  37. package/dist/commands/templates.js.map +1 -1
  38. package/dist/commands/transactional-templates.js +1 -1
  39. package/dist/commands/transactional-templates.js.map +1 -1
  40. package/dist/commands/webhooks.js +1 -1
  41. package/dist/commands/webhooks.js.map +1 -1
  42. package/dist/utils/config.js +2 -2
  43. package/dist/utils/config.js.map +1 -1
  44. package/dist/utils/errors.js +1 -1
  45. package/dist/utils/errors.js.map +1 -1
  46. package/dist/utils/progress.d.ts.map +1 -1
  47. package/dist/utils/progress.js +32 -4
  48. package/dist/utils/progress.js.map +1 -1
  49. package/dist/utils/spinner.d.ts +17 -0
  50. package/dist/utils/spinner.d.ts.map +1 -0
  51. package/dist/utils/spinner.js +43 -0
  52. package/dist/utils/spinner.js.map +1 -0
  53. package/docs/DOCUMENTATION-STANDARD.md +1068 -0
  54. package/docs/README.md +161 -0
  55. package/docs/developer/ARCHITECTURE.md +516 -0
  56. package/docs/developer/AUTH.md +204 -0
  57. package/docs/developer/CONTRIBUTING.md +227 -0
  58. package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
  59. package/docs/developer/PROJECT_INDEX.md +365 -0
  60. package/docs/planning/API_COVERAGE.md +1045 -0
  61. package/docs/planning/BACKLOG.md +1159 -0
  62. package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
  63. package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
  64. package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
  65. package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
  66. package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
  67. package/docs/planning/cakemail-profile-system-plan.md +1121 -0
  68. package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
  69. package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
  70. package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
  71. package/docs/user-manual/.obsidian/app.json +1 -0
  72. package/docs/user-manual/.obsidian/appearance.json +1 -0
  73. package/docs/user-manual/.obsidian/core-plugins.json +33 -0
  74. package/docs/user-manual/.obsidian/workspace.json +167 -0
  75. package/docs/user-manual/01-getting-started/01-installation.md +214 -0
  76. package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
  77. package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
  78. package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
  79. package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
  80. package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
  81. package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
  82. package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
  83. package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
  84. package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
  85. package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
  86. package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
  87. package/docs/user-manual/03-email-operations/01-senders.md +490 -0
  88. package/docs/user-manual/03-email-operations/02-templates.md +444 -0
  89. package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
  90. package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
  91. package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
  92. package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
  93. package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
  94. package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
  95. package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
  96. package/docs/user-manual/05-contact-management/01-lists.md +836 -0
  97. package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
  98. package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
  99. package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
  100. package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
  101. package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
  102. package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
  103. package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
  104. package/docs/user-manual/07-integrations/02-automation.md +326 -0
  105. package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
  106. package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
  107. package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
  108. package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
  109. package/docs/user-manual/09-command-reference/01-config.md +776 -0
  110. package/docs/user-manual/09-command-reference/02-account.md +652 -0
  111. package/docs/user-manual/09-command-reference/03-lists.md +958 -0
  112. package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
  113. package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
  114. package/docs/user-manual/09-command-reference/06-segments.md +894 -0
  115. package/docs/user-manual/09-command-reference/07-senders.md +803 -0
  116. package/docs/user-manual/09-command-reference/08-templates.md +818 -0
  117. package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
  118. package/docs/user-manual/09-command-reference/10-emails.md +807 -0
  119. package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
  120. package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
  121. package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
  122. package/docs/user-manual/09-command-reference/14-interests.md +630 -0
  123. package/docs/user-manual/09-command-reference/15-tags.md +584 -0
  124. package/docs/user-manual/09-command-reference/16-logs.md +656 -0
  125. package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
  126. package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
  127. package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
  128. package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
  129. package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
  130. package/docs/user-manual/11-appendix/04-faq.md +484 -0
  131. package/docs/user-manual/11-appendix/05-glossary.md +250 -0
  132. package/docs/user-manual/README.md +0 -0
  133. package/package.json +13 -61
  134. package/src/cli.ts +125 -0
  135. package/src/client.ts +16 -0
  136. package/src/commands/account.ts +267 -0
  137. package/src/commands/accounts.ts +78 -0
  138. package/src/commands/actions.ts +249 -0
  139. package/src/commands/attributes.ts +139 -0
  140. package/src/commands/campaign-blueprints.ts +106 -0
  141. package/src/commands/campaigns.ts +469 -0
  142. package/src/commands/config.ts +77 -0
  143. package/src/commands/contacts.ts +612 -0
  144. package/src/commands/custom-attributes.ts +127 -0
  145. package/src/commands/dkims.ts +117 -0
  146. package/src/commands/domains.ts +82 -0
  147. package/src/commands/email-apis.ts +569 -0
  148. package/src/commands/emails.ts +197 -0
  149. package/src/commands/forms.ts +283 -0
  150. package/src/commands/interests.ts +155 -0
  151. package/src/commands/links.ts +38 -0
  152. package/src/commands/lists.ts +406 -0
  153. package/src/commands/logos.ts +71 -0
  154. package/src/commands/logs.ts +386 -0
  155. package/src/commands/reports.ts +306 -0
  156. package/src/commands/segments.ts +158 -0
  157. package/src/commands/senders.ts +204 -0
  158. package/src/commands/sub-accounts.ts +271 -0
  159. package/src/commands/suppressed-emails.ts +234 -0
  160. package/src/commands/suppressed.ts +198 -0
  161. package/src/commands/system-emails.ts +85 -0
  162. package/src/commands/tags.ts +146 -0
  163. package/src/commands/tasks.ts +116 -0
  164. package/src/commands/templates.ts +189 -0
  165. package/src/commands/tokens.ts +83 -0
  166. package/src/commands/transactional-emails.ts +374 -0
  167. package/src/commands/transactional-templates.ts +385 -0
  168. package/src/commands/users.ts +506 -0
  169. package/src/commands/webhooks.ts +172 -0
  170. package/src/commands/workflow-blueprints.ts +123 -0
  171. package/src/commands/workflows.ts +265 -0
  172. package/src/types/profile.ts +93 -0
  173. package/src/utils/auth.ts +272 -0
  174. package/src/utils/config-file.ts +96 -0
  175. package/src/utils/config.ts +134 -0
  176. package/src/utils/confirm.ts +32 -0
  177. package/src/utils/defaults.ts +99 -0
  178. package/src/utils/errors.ts +116 -0
  179. package/src/utils/interactive.ts +91 -0
  180. package/src/utils/list-defaults.ts +74 -0
  181. package/src/utils/output.ts +190 -0
  182. package/src/utils/progress.ts +320 -0
  183. package/src/utils/spinner.ts +22 -0
  184. package/tests/IMPLEMENTATION_STATUS.md +258 -0
  185. package/tests/PTY_SETUP.md +118 -0
  186. package/tests/PTY_TESTING_GUIDE.md +507 -0
  187. package/tests/README.md +244 -0
  188. package/tests/fixtures/api-responses/campaigns.json +34 -0
  189. package/tests/fixtures/test-config.json +13 -0
  190. package/tests/helpers/cli-runner.ts +128 -0
  191. package/tests/helpers/mock-server.ts +301 -0
  192. package/tests/helpers/pty-runner.ts +181 -0
  193. package/tests/integration/campaigns-real-api.test.ts +196 -0
  194. package/tests/integration/setup-integration.ts +50 -0
  195. package/tests/pty/campaigns.test.ts +241 -0
  196. package/tests/setup.ts +34 -0
  197. package/tsconfig.json +15 -0
  198. package/vitest.config.ts +28 -0
@@ -0,0 +1,1517 @@
1
+ # Kenogami: Bidirectional Knowledge Graph System
2
+
3
+ ## Overview
4
+
5
+ Kenogami is a knowledge-base system where the **knowledge graph becomes the source of truth** rather than just a derivative of articles. This inverts the traditional flow from "Articles → Knowledge Graph" to "Knowledge Graph ↔ Articles" with bidirectional synchronization.
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ 1. [Traditional vs Bidirectional Flow](#traditional-vs-bidirectional-flow)
12
+ 2. [Architectural Changes](#architectural-changes)
13
+ 3. [Knowledge Graph as Source of Truth](#knowledge-graph-as-source-of-truth)
14
+ 4. [Article Generation from Graph](#article-generation-from-graph)
15
+ 5. [Synchronization Strategies](#synchronization-strategies)
16
+ 6. [Conflict Resolution](#conflict-resolution)
17
+ 7. [Implementation Design](#implementation-design)
18
+ 8. [User Workflows](#user-workflows)
19
+ 9. [Integration with AI Testing](#integration-with-ai-testing)
20
+
21
+ ---
22
+
23
+ ## Traditional vs Bidirectional Flow
24
+
25
+ ### Traditional Flow (Current AI Testing Design)
26
+
27
+ ```
28
+ ┌─────────────────┐
29
+ │ Articles │ (Source of Truth)
30
+ │ (Markdown) │
31
+ └────────┬────────┘
32
+
33
+ │ Parse & Extract
34
+
35
+ ┌─────────────────┐
36
+ │ Knowledge Graph │ (Derived Data)
37
+ │ (Read-Only) │
38
+ └────────┬────────┘
39
+
40
+ │ Query
41
+
42
+ ┌─────────────────┐
43
+ │ AI Testing │
44
+ │ Validation │
45
+ └─────────────────┘
46
+ ```
47
+
48
+ **Problems:**
49
+ - ❌ Duplicate content across articles (same command documented in multiple places)
50
+ - ❌ Inconsistent formatting and terminology
51
+ - ❌ Hard to maintain relationships between concepts
52
+ - ❌ No single source of truth for facts
53
+ - ❌ Articles can drift out of sync with each other
54
+
55
+ ### Bidirectional Flow (Kenogami Approach)
56
+
57
+ ```
58
+ ┌──────────────────────────────────────────────────────────────┐
59
+ │ Knowledge Graph (KG) │
60
+ │ (SOURCE OF TRUTH) │
61
+ │ │
62
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
63
+ │ │ Command │───▶│Parameter │ │ Concept │ │
64
+ │ │ Node │ │ Node │ │ Node │ │
65
+ │ └──────────┘ └──────────┘ └──────────┘ │
66
+ │ │ │ │
67
+ │ │ hasExample │ relatesTo │
68
+ │ ▼ ▼ │
69
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
70
+ │ │ Example │ │ Error │ │Workflow │ │
71
+ │ │ Node │ │ Node │ │ Node │ │
72
+ │ └──────────┘ └──────────┘ └──────────┘ │
73
+ └───────┬──────────────────────────────────┬──────────────────┘
74
+ │ │
75
+ │ Generate │ Extract/Update
76
+ ▼ │
77
+ ┌─────────────────┐ │
78
+ │ Articles │◀───────────────────────┘
79
+ │ (Markdown) │
80
+ │ (Generated) │
81
+ └────────┬────────┘
82
+
83
+ │ Test Against
84
+
85
+ ┌─────────────────┐
86
+ │ AI Testing │
87
+ │ Validation │
88
+ └─────────────────┘
89
+ ```
90
+
91
+ **Benefits:**
92
+ - ✅ Single source of truth (the graph)
93
+ - ✅ Automatic consistency across all articles
94
+ - ✅ Relationships are first-class entities
95
+ - ✅ Generate multiple article views from same data
96
+ - ✅ Update once, reflect everywhere
97
+ - ✅ Track changes at the fact level, not document level
98
+
99
+ ---
100
+
101
+ ## Architectural Changes
102
+
103
+ ### System Components
104
+
105
+ ```
106
+ ┌─────────────────────────────────────────────────────────────┐
107
+ │ Kenogami System │
108
+ └─────────────────────────────────────────────────────────────┘
109
+
110
+ ┌─────────────────────┼─────────────────────┐
111
+ │ │ │
112
+ ▼ ▼ ▼
113
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
114
+ │ Graph │ │ Article │ │Sync Engine │
115
+ │ Editor │ │ Generator │ │ │
116
+ │ │ │ │ │ │
117
+ │ - Add nodes │ │ - Templates │ │ - Diff │
118
+ │ - Edit facts │ │ - Rendering │ │ - Merge │
119
+ │ - Relations │ │ - Formatting │ │ - Conflict │
120
+ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
121
+ │ │ │
122
+ │ │ │
123
+ └─────────────────────┼─────────────────────┘
124
+
125
+
126
+ ┌─────────────────┐
127
+ │ Knowledge Graph │
128
+ │ (Neo4j or │
129
+ │ similar DB) │
130
+ └─────────────────┘
131
+ ```
132
+
133
+ ### Data Flow Changes
134
+
135
+ **Old Flow: Article → Graph**
136
+ ```typescript
137
+ // Traditional extraction
138
+ const articles = readMarkdownFiles('./docs');
139
+ const knowledgeGraph = await extractKnowledge(articles);
140
+ // Graph is derivative, articles are source
141
+ ```
142
+
143
+ **New Flow: Graph → Articles**
144
+ ```typescript
145
+ // Graph-first approach
146
+ const knowledgeGraph = loadKnowledgeGraph(); // Source of truth
147
+ const articles = await generateArticles(knowledgeGraph, templates);
148
+ // Articles are derivative, graph is source
149
+ ```
150
+
151
+ **Hybrid Flow: Graph ↔ Articles (Recommended)**
152
+ ```typescript
153
+ // Bidirectional sync
154
+ const knowledgeGraph = loadKnowledgeGraph();
155
+
156
+ // When articles are edited by humans:
157
+ const articleChanges = detectChanges('./docs');
158
+ await syncArticlesToGraph(articleChanges, knowledgeGraph);
159
+
160
+ // When graph is edited programmatically:
161
+ const graphChanges = detectGraphChanges(knowledgeGraph);
162
+ await syncGraphToArticles(graphChanges, './docs', templates);
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Knowledge Graph as Source of Truth
168
+
169
+ ### Graph Schema Design
170
+
171
+ **Node Types:**
172
+
173
+ ```typescript
174
+ type KnowledgeNode =
175
+ | CommandNode
176
+ | ConceptNode
177
+ | ParameterNode
178
+ | ExampleNode
179
+ | ErrorNode
180
+ | WorkflowNode
181
+ | TutorialNode
182
+ | FAQNode;
183
+
184
+ interface CommandNode {
185
+ id: string;
186
+ type: 'command';
187
+ name: string; // "cakemail campaigns list"
188
+ category: string; // "campaigns"
189
+ description: string;
190
+ version_added: string; // "1.3.0"
191
+ version_deprecated?: string;
192
+
193
+ // Metadata
194
+ aliases: string[];
195
+ platform: 'cli' | 'web' | 'mobile' | 'api';
196
+ authentication_required: boolean;
197
+
198
+ // Relationships (defined as edges)
199
+ // → hasParameter → ParameterNode
200
+ // → hasExample → ExampleNode
201
+ // → canThrow → ErrorNode
202
+ // → partOfWorkflow → WorkflowNode
203
+ // → relatedTo → CommandNode
204
+ // → documentedIn → ArticleNode
205
+ }
206
+
207
+ interface ParameterNode {
208
+ id: string;
209
+ type: 'parameter';
210
+ name: string; // "status"
211
+ flag: string; // "-s, --status"
212
+ description: string;
213
+ value_type: 'string' | 'number' | 'boolean' | 'enum';
214
+ required: boolean;
215
+ default_value?: any;
216
+ enum_values?: string[];
217
+
218
+ // Relationships
219
+ // ← hasParameter ← CommandNode
220
+ // → hasExample → ExampleNode
221
+ // → validates → ValidationRuleNode
222
+ }
223
+
224
+ interface ExampleNode {
225
+ id: string;
226
+ type: 'example';
227
+ title: string;
228
+ command: string; // Full command string
229
+ description: string;
230
+ expected_output: string | object;
231
+ expected_exit_code: number;
232
+ platform: 'cli' | 'web' | 'mobile';
233
+
234
+ // Relationships
235
+ // ← hasExample ← CommandNode
236
+ // → producesOutput → OutputNode
237
+ }
238
+
239
+ interface ConceptNode {
240
+ id: string;
241
+ type: 'concept';
242
+ name: string; // "OAuth Authentication"
243
+ description: string;
244
+ category: 'core' | 'advanced' | 'reference';
245
+
246
+ // Relationships
247
+ // → relatedTo → ConceptNode
248
+ // → implements → CommandNode
249
+ // → prerequisiteFor → ConceptNode
250
+ // → documentedIn → ArticleNode
251
+ }
252
+
253
+ interface WorkflowNode {
254
+ id: string;
255
+ type: 'workflow';
256
+ name: string; // "Create and Send Campaign"
257
+ description: string;
258
+ steps: WorkflowStep[];
259
+
260
+ // Relationships
261
+ // → hasStep → CommandNode (ordered)
262
+ // → requires → ConceptNode
263
+ }
264
+
265
+ interface ArticleNode {
266
+ id: string;
267
+ type: 'article';
268
+ title: string;
269
+ slug: string; // "campaigns-management"
270
+ path: string; // "docs/user-manual/campaigns.md"
271
+ category: string;
272
+ template: 'command_reference' | 'tutorial' | 'concept' | 'faq';
273
+ last_generated: Date;
274
+ last_human_edit?: Date;
275
+
276
+ // Relationships
277
+ // ← documentedIn ← CommandNode
278
+ // ← documentedIn ← ConceptNode
279
+ // → includes → (any node type)
280
+ }
281
+ ```
282
+
283
+ **Relationship Types:**
284
+
285
+ ```cypher
286
+ // Command → Parameter
287
+ (:Command)-[:HAS_PARAMETER {position: 1, required: true}]->(:Parameter)
288
+
289
+ // Command → Example
290
+ (:Command)-[:HAS_EXAMPLE {order: 1}]->(:Example)
291
+
292
+ // Command → Error
293
+ (:Command)-[:CAN_THROW {condition: "invalid list ID"}]->(:Error)
294
+
295
+ // Command → Command (related)
296
+ (:Command)-[:RELATED_TO {similarity: 0.8}]->(:Command)
297
+
298
+ // Command → Workflow
299
+ (:Command)-[:PART_OF_WORKFLOW {step: 2}]->(:Workflow)
300
+
301
+ // Concept → Concept (prerequisite)
302
+ (:Concept)-[:PREREQUISITE_FOR]->(:Concept)
303
+
304
+ // Article → Content (what it documents)
305
+ (:Article)-[:DOCUMENTS {section: "Usage"}]->(:Command)
306
+
307
+ // Temporal relationships
308
+ (:Command)-[:SUPERSEDES]->(:Command) // v2 command replaces v1
309
+ (:Command)-[:DEPRECATED_IN {version: "2.0.0"}]->(:Version)
310
+ ```
311
+
312
+ ### Graph Query Examples
313
+
314
+ **Find all commands in a category:**
315
+ ```cypher
316
+ MATCH (c:Command {category: 'campaigns'})
317
+ RETURN c.name, c.description
318
+ ORDER BY c.name
319
+ ```
320
+
321
+ **Find command with all related information:**
322
+ ```cypher
323
+ MATCH (c:Command {name: 'cakemail campaigns list'})
324
+ OPTIONAL MATCH (c)-[:HAS_PARAMETER]->(p:Parameter)
325
+ OPTIONAL MATCH (c)-[:HAS_EXAMPLE]->(e:Example)
326
+ OPTIONAL MATCH (c)-[:CAN_THROW]->(err:Error)
327
+ OPTIONAL MATCH (c)-[:RELATED_TO]->(related:Command)
328
+ RETURN c,
329
+ collect(DISTINCT p) as parameters,
330
+ collect(DISTINCT e) as examples,
331
+ collect(DISTINCT err) as errors,
332
+ collect(DISTINCT related) as related_commands
333
+ ```
334
+
335
+ **Find all articles that need regeneration:**
336
+ ```cypher
337
+ // Find articles where documented commands have changed
338
+ MATCH (a:Article)-[:DOCUMENTS]->(c:Command)
339
+ WHERE c.updated_at > a.last_generated
340
+ RETURN a.path, a.title, count(c) as outdated_commands
341
+ ORDER BY outdated_commands DESC
342
+ ```
343
+
344
+ **Find workflow for a goal:**
345
+ ```cypher
346
+ MATCH (w:Workflow {name: 'Create and Send Campaign'})
347
+ MATCH (w)-[s:HAS_STEP]->(c:Command)
348
+ RETURN c.name, s.step
349
+ ORDER BY s.step
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Article Generation from Graph
355
+
356
+ ### Template System
357
+
358
+ **Article Templates:**
359
+
360
+ ```typescript
361
+ interface ArticleTemplate {
362
+ type: 'command_reference' | 'tutorial' | 'concept' | 'faq';
363
+ structure: TemplateSection[];
364
+ renderingRules: RenderingRule[];
365
+ }
366
+
367
+ interface CommandReferenceTemplate extends ArticleTemplate {
368
+ type: 'command_reference';
369
+ structure: [
370
+ { section: 'title', source: 'command.name' },
371
+ { section: 'description', source: 'command.description' },
372
+ { section: 'usage', generator: 'renderUsage' },
373
+ { section: 'parameters', generator: 'renderParameters' },
374
+ { section: 'examples', generator: 'renderExamples' },
375
+ { section: 'errors', generator: 'renderErrors' },
376
+ { section: 'related', generator: 'renderRelated' }
377
+ ];
378
+ }
379
+ ```
380
+
381
+ **Template Example (Command Reference):**
382
+
383
+ ```handlebars
384
+ {{!-- templates/command-reference.hbs --}}
385
+ # {{command.name}}
386
+
387
+ {{command.description}}
388
+
389
+ ## Usage
390
+
391
+ ```bash
392
+ {{command.name}} {{#each parameters}}{{renderParameter this}}{{/each}}
393
+ ```
394
+
395
+ ## Parameters
396
+
397
+ {{#each parameters}}
398
+ ### {{flag}}
399
+
400
+ {{description}}
401
+
402
+ - **Type:** `{{value_type}}`
403
+ - **Required:** {{#if required}}Yes{{else}}No{{/if}}
404
+ {{#if default_value}}
405
+ - **Default:** `{{default_value}}`
406
+ {{/if}}
407
+ {{#if enum_values}}
408
+ - **Valid values:** {{#each enum_values}}`{{this}}`{{#unless @last}}, {{/unless}}{{/each}}
409
+ {{/if}}
410
+
411
+ {{/each}}
412
+
413
+ ## Examples
414
+
415
+ {{#each examples}}
416
+ ### {{title}}
417
+
418
+ ```bash
419
+ {{command}}
420
+ ```
421
+
422
+ {{description}}
423
+
424
+ **Expected output:**
425
+ ```{{#if (isJSON expected_output)}}json{{else}}text{{/if}}
426
+ {{expected_output}}
427
+ ```
428
+
429
+ {{/each}}
430
+
431
+ ## Error Handling
432
+
433
+ {{#each errors}}
434
+ ### {{error_message}}
435
+
436
+ {{description}}
437
+
438
+ **Exit code:** {{exit_code}}
439
+
440
+ {{#if suggestion}}
441
+ 💡 **Tip:** {{suggestion}}
442
+ {{/if}}
443
+
444
+ {{/each}}
445
+
446
+ ## Related Commands
447
+
448
+ {{#each related_commands}}
449
+ - [`{{name}}`]({{link}}) - {{description}}
450
+ {{/each}}
451
+
452
+ ---
453
+
454
+ *Last generated: {{generated_at}}*
455
+ *Source: Knowledge Graph v{{graph_version}}*
456
+ ```
457
+
458
+ ### Article Generator
459
+
460
+ ```typescript
461
+ class ArticleGenerator {
462
+ constructor(
463
+ private knowledgeGraph: KnowledgeGraph,
464
+ private templates: TemplateRegistry
465
+ ) {}
466
+
467
+ async generateArticle(articleId: string): Promise<GeneratedArticle> {
468
+ // 1. Load article node from graph
469
+ const articleNode = await this.knowledgeGraph.getNode(articleId);
470
+
471
+ // 2. Query all related content
472
+ const content = await this.queryArticleContent(articleNode);
473
+
474
+ // 3. Select template
475
+ const template = this.templates.get(articleNode.template);
476
+
477
+ // 4. Render article
478
+ const markdown = await this.renderTemplate(template, content);
479
+
480
+ // 5. Add metadata
481
+ const frontmatter = this.generateFrontmatter(articleNode, content);
482
+
483
+ return {
484
+ path: articleNode.path,
485
+ content: `${frontmatter}\n\n${markdown}`,
486
+ metadata: {
487
+ generated_at: new Date(),
488
+ source_nodes: content.nodes.map(n => n.id),
489
+ template: articleNode.template
490
+ }
491
+ };
492
+ }
493
+
494
+ async queryArticleContent(articleNode: ArticleNode): Promise<ArticleContent> {
495
+ // Query based on article type
496
+ switch (articleNode.template) {
497
+ case 'command_reference':
498
+ return this.queryCommandReference(articleNode);
499
+ case 'tutorial':
500
+ return this.queryTutorial(articleNode);
501
+ case 'concept':
502
+ return this.queryConcept(articleNode);
503
+ case 'faq':
504
+ return this.queryFAQ(articleNode);
505
+ }
506
+ }
507
+
508
+ async queryCommandReference(articleNode: ArticleNode): Promise<ArticleContent> {
509
+ // Cypher query to get all commands documented in this article
510
+ const query = `
511
+ MATCH (a:Article {id: $articleId})-[:DOCUMENTS]->(c:Command)
512
+ OPTIONAL MATCH (c)-[:HAS_PARAMETER]->(p:Parameter)
513
+ OPTIONAL MATCH (c)-[:HAS_EXAMPLE]->(e:Example)
514
+ OPTIONAL MATCH (c)-[:CAN_THROW]->(err:Error)
515
+ OPTIONAL MATCH (c)-[:RELATED_TO]->(rel:Command)
516
+ RETURN c,
517
+ collect(DISTINCT p) as parameters,
518
+ collect(DISTINCT e) as examples,
519
+ collect(DISTINCT err) as errors,
520
+ collect(DISTINCT rel) as related
521
+ `;
522
+
523
+ const result = await this.knowledgeGraph.query(query, { articleId: articleNode.id });
524
+
525
+ return {
526
+ commands: result.map(r => ({
527
+ ...r.c,
528
+ parameters: r.parameters,
529
+ examples: r.examples,
530
+ errors: r.errors,
531
+ related: r.related
532
+ }))
533
+ };
534
+ }
535
+
536
+ generateFrontmatter(articleNode: ArticleNode, content: ArticleContent): string {
537
+ return `---
538
+ title: ${articleNode.title}
539
+ category: ${articleNode.category}
540
+ template: ${articleNode.template}
541
+ generated_at: ${new Date().toISOString()}
542
+ graph_version: ${this.knowledgeGraph.version}
543
+ source_nodes: [${content.nodes.map(n => n.id).join(', ')}]
544
+ ---`;
545
+ }
546
+ }
547
+ ```
548
+
549
+ ### Regeneration Strategies
550
+
551
+ **Strategy 1: Full Regeneration**
552
+ ```typescript
553
+ // Regenerate all articles from scratch
554
+ async function regenerateAllArticles() {
555
+ const articles = await knowledgeGraph.getAllNodes('Article');
556
+
557
+ for (const article of articles) {
558
+ const generated = await articleGenerator.generateArticle(article.id);
559
+ await writeFile(generated.path, generated.content);
560
+
561
+ // Update article node with generation timestamp
562
+ await knowledgeGraph.updateNode(article.id, {
563
+ last_generated: new Date()
564
+ });
565
+ }
566
+ }
567
+ ```
568
+
569
+ **Strategy 2: Selective Regeneration (Efficient)**
570
+ ```typescript
571
+ // Only regenerate articles affected by graph changes
572
+ async function regenerateChangedArticles(changedNodeIds: string[]) {
573
+ // Find all articles that document the changed nodes
574
+ const query = `
575
+ MATCH (a:Article)-[:DOCUMENTS*1..2]->(n)
576
+ WHERE n.id IN $changedNodeIds
577
+ RETURN DISTINCT a
578
+ `;
579
+
580
+ const affectedArticles = await knowledgeGraph.query(query, { changedNodeIds });
581
+
582
+ for (const article of affectedArticles) {
583
+ await articleGenerator.generateArticle(article.id);
584
+ }
585
+ }
586
+ ```
587
+
588
+ **Strategy 3: Incremental Updates (Advanced)**
589
+ ```typescript
590
+ // Update only changed sections of articles
591
+ async function incrementalUpdate(nodeId: string, changes: NodeChanges) {
592
+ const affectedArticles = await findArticlesDocumenting(nodeId);
593
+
594
+ for (const article of affectedArticles) {
595
+ // Parse existing article
596
+ const existingContent = await readFile(article.path);
597
+ const ast = parseMarkdown(existingContent);
598
+
599
+ // Find section that documents this node
600
+ const section = findSectionForNode(ast, nodeId);
601
+
602
+ // Regenerate only that section
603
+ const newSection = await generateSection(nodeId, changes);
604
+
605
+ // Replace section in AST
606
+ ast.replace(section, newSection);
607
+
608
+ // Write back
609
+ await writeFile(article.path, renderMarkdown(ast));
610
+ }
611
+ }
612
+ ```
613
+
614
+ ---
615
+
616
+ ## Synchronization Strategies
617
+
618
+ ### Two-Way Sync Architecture
619
+
620
+ ```
621
+ ┌──────────────────────────────────────────────────────────────┐
622
+ │ Sync Coordinator │
623
+ └──────────────────────────────────────────────────────────────┘
624
+
625
+ ┌─────────────────────┼─────────────────────┐
626
+ │ │ │
627
+ ▼ ▼ ▼
628
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
629
+ │ Change │ │ Conflict │ │ Merge │
630
+ │ Detector │ │ Resolver │ │ Engine │
631
+ └──────────────┘ └──────────────┘ └──────────────┘
632
+ ```
633
+
634
+ ### Change Detection
635
+
636
+ **Article Changes (Human Edits):**
637
+
638
+ ```typescript
639
+ class ArticleChangeDetector {
640
+ async detectChanges(articlePath: string): Promise<ArticleChanges> {
641
+ // 1. Parse current article
642
+ const currentContent = await readFile(articlePath);
643
+ const currentAST = parseMarkdown(currentContent);
644
+
645
+ // 2. Get last known state from graph
646
+ const articleNode = await this.getArticleNodeByPath(articlePath);
647
+ const lastGenerated = articleNode.last_generated;
648
+
649
+ // 3. Load version from last generation
650
+ const lastContent = await this.getGeneratedVersion(articleNode.id, lastGenerated);
651
+ const lastAST = parseMarkdown(lastContent);
652
+
653
+ // 4. Diff the two versions
654
+ const diff = diffAST(lastAST, currentAST);
655
+
656
+ // 5. Classify changes
657
+ return this.classifyChanges(diff, articleNode);
658
+ }
659
+
660
+ classifyChanges(diff: ASTDiff, articleNode: ArticleNode): ArticleChanges {
661
+ const changes: ArticleChanges = {
662
+ structural: [], // New sections, removed sections
663
+ factual: [], // Changed facts (command names, parameters, etc.)
664
+ editorial: [], // Wording, examples, explanations
665
+ metadata: [] // Frontmatter changes
666
+ };
667
+
668
+ for (const change of diff.changes) {
669
+ if (this.isStructuralChange(change)) {
670
+ changes.structural.push(change);
671
+ } else if (this.isFactualChange(change)) {
672
+ changes.factual.push(change);
673
+ } else {
674
+ changes.editorial.push(change);
675
+ }
676
+ }
677
+
678
+ return changes;
679
+ }
680
+
681
+ isFactualChange(change: Change): boolean {
682
+ // Factual changes affect the knowledge graph
683
+ // Examples: command names, parameter types, exit codes
684
+ return (
685
+ change.affects('command.name') ||
686
+ change.affects('parameter.type') ||
687
+ change.affects('example.command') ||
688
+ change.affects('error.exit_code')
689
+ );
690
+ }
691
+ }
692
+ ```
693
+
694
+ **Graph Changes (Programmatic Updates):**
695
+
696
+ ```typescript
697
+ class GraphChangeDetector {
698
+ async detectChanges(): Promise<GraphChanges> {
699
+ // Use graph's built-in change tracking
700
+ const query = `
701
+ MATCH (n)
702
+ WHERE n.updated_at > $lastCheck
703
+ RETURN n, labels(n) as type
704
+ `;
705
+
706
+ const changedNodes = await this.knowledgeGraph.query(query, {
707
+ lastCheck: this.lastCheckpoint
708
+ });
709
+
710
+ return {
711
+ nodes: changedNodes,
712
+ affectedArticles: await this.findAffectedArticles(changedNodes)
713
+ };
714
+ }
715
+
716
+ async findAffectedArticles(changedNodes: Node[]): Promise<ArticleNode[]> {
717
+ const nodeIds = changedNodes.map(n => n.id);
718
+
719
+ const query = `
720
+ MATCH (a:Article)-[:DOCUMENTS*1..3]->(n)
721
+ WHERE n.id IN $nodeIds
722
+ RETURN DISTINCT a
723
+ `;
724
+
725
+ return this.knowledgeGraph.query(query, { nodeIds });
726
+ }
727
+ }
728
+ ```
729
+
730
+ ### Sync Directions
731
+
732
+ **Direction 1: Article → Graph (Human edits articles)**
733
+
734
+ ```typescript
735
+ class ArticleToGraphSync {
736
+ async syncArticleChanges(changes: ArticleChanges): Promise<SyncResult> {
737
+ const updates: GraphUpdate[] = [];
738
+
739
+ // 1. Apply structural changes
740
+ for (const change of changes.structural) {
741
+ if (change.type === 'section_added') {
742
+ // Extract new content and create graph nodes
743
+ const newNodes = await this.extractNodesFromSection(change.section);
744
+ updates.push({ type: 'create', nodes: newNodes });
745
+ } else if (change.type === 'section_removed') {
746
+ // Mark nodes as deprecated or remove relationships
747
+ updates.push({ type: 'deprecate', nodes: change.affectedNodes });
748
+ }
749
+ }
750
+
751
+ // 2. Apply factual changes
752
+ for (const change of changes.factual) {
753
+ const node = await this.findNodeForChange(change);
754
+ updates.push({
755
+ type: 'update',
756
+ nodeId: node.id,
757
+ field: change.field,
758
+ oldValue: change.oldValue,
759
+ newValue: change.newValue
760
+ });
761
+ }
762
+
763
+ // 3. Editorial changes don't affect graph
764
+ // They're preserved as article-specific styling
765
+
766
+ // 4. Apply all updates to graph
767
+ return this.applyUpdates(updates);
768
+ }
769
+
770
+ async extractNodesFromSection(section: MarkdownSection): Promise<Node[]> {
771
+ // Use LLM to extract structured data from markdown
772
+ const prompt = `
773
+ Extract structured knowledge from this documentation section:
774
+
775
+ ${section.content}
776
+
777
+ Identify:
778
+ - Commands (name, description, parameters)
779
+ - Examples (command, expected output)
780
+ - Concepts (name, description, relationships)
781
+ - Errors (message, exit code, suggestions)
782
+
783
+ Return as JSON matching our graph schema.
784
+ `;
785
+
786
+ const extracted = await this.llm.complete(prompt, { format: 'json' });
787
+ return this.convertToGraphNodes(extracted);
788
+ }
789
+ }
790
+ ```
791
+
792
+ **Direction 2: Graph → Articles (Programmatic updates to graph)**
793
+
794
+ ```typescript
795
+ class GraphToArticleSync {
796
+ async syncGraphChanges(changes: GraphChanges): Promise<SyncResult> {
797
+ const affectedArticles = changes.affectedArticles;
798
+
799
+ for (const article of affectedArticles) {
800
+ // Check for conflicts (has article been edited since last generation?)
801
+ const hasHumanEdits = await this.hasHumanEdits(article);
802
+
803
+ if (hasHumanEdits) {
804
+ // Conflict! Needs resolution
805
+ await this.raiseConflict({
806
+ article: article.path,
807
+ graphChanges: changes.nodes,
808
+ humanEdits: await this.getHumanEdits(article)
809
+ });
810
+ } else {
811
+ // Safe to regenerate
812
+ await this.regenerateArticle(article);
813
+ }
814
+ }
815
+
816
+ return { success: true, conflicts: this.conflicts };
817
+ }
818
+
819
+ async hasHumanEdits(article: ArticleNode): Promise<boolean> {
820
+ // Compare file modification time with last_generated timestamp
821
+ const fileStats = await stat(article.path);
822
+ return fileStats.mtime > article.last_generated;
823
+ }
824
+ }
825
+ ```
826
+
827
+ ---
828
+
829
+ ## Conflict Resolution
830
+
831
+ ### Conflict Scenarios
832
+
833
+ **Scenario 1: Simultaneous Edits**
834
+ ```
835
+ Graph: Command parameter "status" changed from optional to required
836
+ Article: Human added example showing command without "status" parameter
837
+
838
+ Conflict: Example no longer valid
839
+ ```
840
+
841
+ **Scenario 2: Divergent Facts**
842
+ ```
843
+ Graph: Command exit code is 1 on error
844
+ Article: Human documented exit code as 0 (incorrect)
845
+
846
+ Conflict: Which is correct?
847
+ ```
848
+
849
+ **Scenario 3: Structure vs Content**
850
+ ```
851
+ Graph: Command deprecated, marked for removal
852
+ Article: Human added extensive tutorial about the command
853
+
854
+ Conflict: Keep tutorial or remove?
855
+ ```
856
+
857
+ ### Resolution Strategies
858
+
859
+ **Strategy 1: Graph Wins (Default for Facts)**
860
+
861
+ ```typescript
862
+ class GraphWinsResolver implements ConflictResolver {
863
+ resolve(conflict: Conflict): Resolution {
864
+ // For factual conflicts, graph is source of truth
865
+ if (conflict.type === 'factual') {
866
+ return {
867
+ action: 'use_graph',
868
+ reasoning: 'Knowledge graph is authoritative for facts',
869
+ preserveHumanEdit: false
870
+ };
871
+ }
872
+
873
+ return { action: 'escalate' };
874
+ }
875
+ }
876
+ ```
877
+
878
+ **Strategy 2: Human Wins (For Editorial)**
879
+
880
+ ```typescript
881
+ class HumanWinsResolver implements ConflictResolver {
882
+ resolve(conflict: Conflict): Resolution {
883
+ // For editorial changes, preserve human intent
884
+ if (conflict.type === 'editorial') {
885
+ return {
886
+ action: 'use_article',
887
+ reasoning: 'Human editorial improvements should be preserved',
888
+ syncBackToGraph: false
889
+ };
890
+ }
891
+
892
+ return { action: 'escalate' };
893
+ }
894
+ }
895
+ ```
896
+
897
+ **Strategy 3: Merge (Best Effort)**
898
+
899
+ ```typescript
900
+ class MergeResolver implements ConflictResolver {
901
+ async resolve(conflict: Conflict): Promise<Resolution> {
902
+ // Use AI to attempt intelligent merge
903
+ const prompt = `
904
+ There is a conflict between the knowledge graph and article:
905
+
906
+ Graph says: ${conflict.graphVersion}
907
+ Article says: ${conflict.articleVersion}
908
+
909
+ The conflict type is: ${conflict.type}
910
+
911
+ Can these be merged? If so, how?
912
+ If not, which should take precedence and why?
913
+ `;
914
+
915
+ const resolution = await this.llm.complete(prompt);
916
+ return this.parseResolution(resolution);
917
+ }
918
+ }
919
+ ```
920
+
921
+ **Strategy 4: Three-Way Merge (Most Robust)**
922
+
923
+ ```typescript
924
+ class ThreeWayMergeResolver implements ConflictResolver {
925
+ resolve(conflict: Conflict): Resolution {
926
+ // Compare:
927
+ // 1. Last generated version (base)
928
+ // 2. Current graph version
929
+ // 3. Current article version
930
+
931
+ const base = conflict.lastGeneratedVersion;
932
+ const graph = conflict.graphVersion;
933
+ const article = conflict.articleVersion;
934
+
935
+ if (graph === base && article !== base) {
936
+ // Only article changed → use article
937
+ return { action: 'use_article', syncBackToGraph: true };
938
+ } else if (article === base && graph !== base) {
939
+ // Only graph changed → use graph
940
+ return { action: 'use_graph' };
941
+ } else {
942
+ // Both changed → conflict!
943
+ return {
944
+ action: 'manual_review',
945
+ versions: { base, graph, article },
946
+ suggestedResolution: this.suggestMerge(base, graph, article)
947
+ };
948
+ }
949
+ }
950
+ }
951
+ ```
952
+
953
+ ### Conflict UI
954
+
955
+ ```typescript
956
+ // Interactive conflict resolution interface
957
+ class ConflictResolutionUI {
958
+ async presentConflict(conflict: Conflict): Promise<Resolution> {
959
+ console.log(`
960
+ ┌─────────────────────────────────────────────────────────┐
961
+ │ 🚨 CONFLICT DETECTED │
962
+ ├─────────────────────────────────────────────────────────┤
963
+ │ File: ${conflict.article.path} │
964
+ │ Type: ${conflict.type} │
965
+ │ │
966
+ │ ❓ What should we do? │
967
+ └─────────────────────────────────────────────────────────┘
968
+
969
+ 📊 Graph Version (Programmatic):
970
+ ${highlight(conflict.graphVersion)}
971
+
972
+ ✏️ Article Version (Human Edit):
973
+ ${highlight(conflict.articleVersion)}
974
+
975
+ 🔀 Options:
976
+ 1) Use graph version (discard human edit)
977
+ 2) Use article version (update graph to match)
978
+ 3) Merge both (AI-assisted)
979
+ 4) Edit manually
980
+ 5) Skip for now
981
+
982
+ Choice:
983
+ `);
984
+
985
+ const choice = await this.prompt();
986
+ return this.handleChoice(choice, conflict);
987
+ }
988
+ }
989
+ ```
990
+
991
+ ---
992
+
993
+ ## Implementation Design
994
+
995
+ ### Database Schema (Neo4j Example)
996
+
997
+ ```cypher
998
+ // Create constraints
999
+ CREATE CONSTRAINT command_id IF NOT EXISTS
1000
+ FOR (c:Command) REQUIRE c.id IS UNIQUE;
1001
+
1002
+ CREATE CONSTRAINT article_path IF NOT EXISTS
1003
+ FOR (a:Article) REQUIRE a.path IS UNIQUE;
1004
+
1005
+ // Create indexes for common queries
1006
+ CREATE INDEX command_name IF NOT EXISTS
1007
+ FOR (c:Command) ON (c.name);
1008
+
1009
+ CREATE INDEX article_category IF NOT EXISTS
1010
+ FOR (a:Article) ON (a.category);
1011
+
1012
+ // Full-text search
1013
+ CREATE FULLTEXT INDEX article_content IF NOT EXISTS
1014
+ FOR (a:Article) ON EACH [a.title, a.description];
1015
+ ```
1016
+
1017
+ ### Core System Components
1018
+
1019
+ **Component 1: Knowledge Graph Manager**
1020
+
1021
+ ```typescript
1022
+ class KnowledgeGraphManager {
1023
+ constructor(private neo4j: Neo4jDriver) {}
1024
+
1025
+ // CRUD operations
1026
+ async createNode(type: string, properties: any): Promise<Node> {
1027
+ const query = `
1028
+ CREATE (n:${type} $properties)
1029
+ SET n.id = randomUUID(),
1030
+ n.created_at = datetime(),
1031
+ n.updated_at = datetime()
1032
+ RETURN n
1033
+ `;
1034
+ return this.neo4j.run(query, { properties });
1035
+ }
1036
+
1037
+ async updateNode(id: string, updates: any): Promise<Node> {
1038
+ const query = `
1039
+ MATCH (n {id: $id})
1040
+ SET n += $updates,
1041
+ n.updated_at = datetime()
1042
+ RETURN n
1043
+ `;
1044
+ return this.neo4j.run(query, { id, updates });
1045
+ }
1046
+
1047
+ async createRelationship(
1048
+ fromId: string,
1049
+ toId: string,
1050
+ type: string,
1051
+ properties?: any
1052
+ ): Promise<Relationship> {
1053
+ const query = `
1054
+ MATCH (from {id: $fromId}), (to {id: $toId})
1055
+ CREATE (from)-[r:${type} $properties]->(to)
1056
+ RETURN r
1057
+ `;
1058
+ return this.neo4j.run(query, { fromId, toId, properties });
1059
+ }
1060
+
1061
+ // Versioning
1062
+ async createSnapshot(label: string): Promise<Snapshot> {
1063
+ const query = `
1064
+ MATCH (n)
1065
+ WITH collect(n) as nodes
1066
+ CREATE (s:Snapshot {
1067
+ id: randomUUID(),
1068
+ label: $label,
1069
+ created_at: datetime(),
1070
+ node_count: size(nodes)
1071
+ })
1072
+ RETURN s
1073
+ `;
1074
+ return this.neo4j.run(query, { label });
1075
+ }
1076
+
1077
+ async restoreSnapshot(snapshotId: string): Promise<void> {
1078
+ // Restore graph to previous snapshot state
1079
+ // Implementation depends on versioning strategy
1080
+ }
1081
+ }
1082
+ ```
1083
+
1084
+ **Component 2: Sync Coordinator**
1085
+
1086
+ ```typescript
1087
+ class SyncCoordinator {
1088
+ constructor(
1089
+ private graphManager: KnowledgeGraphManager,
1090
+ private articleGenerator: ArticleGenerator,
1091
+ private changeDetector: ChangeDetector,
1092
+ private conflictResolver: ConflictResolver
1093
+ ) {}
1094
+
1095
+ async syncAll(direction: 'graph_to_articles' | 'articles_to_graph' | 'bidirectional') {
1096
+ if (direction === 'graph_to_articles' || direction === 'bidirectional') {
1097
+ await this.syncGraphToArticles();
1098
+ }
1099
+
1100
+ if (direction === 'articles_to_graph' || direction === 'bidirectional') {
1101
+ await this.syncArticlesToGraph();
1102
+ }
1103
+ }
1104
+
1105
+ async syncGraphToArticles(): Promise<SyncResult> {
1106
+ // 1. Detect graph changes
1107
+ const graphChanges = await this.changeDetector.detectGraphChanges();
1108
+
1109
+ if (graphChanges.length === 0) {
1110
+ return { status: 'up_to_date' };
1111
+ }
1112
+
1113
+ // 2. Find affected articles
1114
+ const affectedArticles = await this.findAffectedArticles(graphChanges);
1115
+
1116
+ // 3. For each article, check for conflicts
1117
+ const results = [];
1118
+ for (const article of affectedArticles) {
1119
+ const hasConflict = await this.checkForConflicts(article, graphChanges);
1120
+
1121
+ if (hasConflict) {
1122
+ const resolution = await this.conflictResolver.resolve(hasConflict);
1123
+ results.push(await this.applyResolution(resolution));
1124
+ } else {
1125
+ // No conflict, regenerate
1126
+ results.push(await this.articleGenerator.generateArticle(article.id));
1127
+ }
1128
+ }
1129
+
1130
+ return { status: 'synced', results };
1131
+ }
1132
+
1133
+ async syncArticlesToGraph(): Promise<SyncResult> {
1134
+ // 1. Detect article changes
1135
+ const articleChanges = await this.changeDetector.detectArticleChanges();
1136
+
1137
+ if (articleChanges.length === 0) {
1138
+ return { status: 'up_to_date' };
1139
+ }
1140
+
1141
+ // 2. Extract graph updates from changes
1142
+ const graphUpdates = [];
1143
+ for (const change of articleChanges) {
1144
+ if (change.type === 'factual') {
1145
+ const update = await this.extractGraphUpdate(change);
1146
+ graphUpdates.push(update);
1147
+ }
1148
+ }
1149
+
1150
+ // 3. Apply updates to graph
1151
+ for (const update of graphUpdates) {
1152
+ await this.graphManager.applyUpdate(update);
1153
+ }
1154
+
1155
+ return { status: 'synced', updates: graphUpdates.length };
1156
+ }
1157
+ }
1158
+ ```
1159
+
1160
+ **Component 3: Article Generator (Enhanced)**
1161
+
1162
+ ```typescript
1163
+ class SmartArticleGenerator extends ArticleGenerator {
1164
+ // Preserve human editorial changes during regeneration
1165
+ async generateArticlePreservingEdits(
1166
+ articleId: string,
1167
+ humanEdits: EditorialChange[]
1168
+ ): Promise<GeneratedArticle> {
1169
+ // 1. Generate fresh article from graph
1170
+ const generatedArticle = await super.generateArticle(articleId);
1171
+
1172
+ // 2. Parse both versions
1173
+ const generatedAST = parseMarkdown(generatedArticle.content);
1174
+ const currentContent = await readFile(articleId);
1175
+ const currentAST = parseMarkdown(currentContent);
1176
+
1177
+ // 3. Identify editorial sections to preserve
1178
+ const preserveSections = this.identifyEditorialSections(currentAST, humanEdits);
1179
+
1180
+ // 4. Merge: use generated for facts, current for editorial
1181
+ const mergedAST = this.mergeASTs(generatedAST, currentAST, preserveSections);
1182
+
1183
+ // 5. Render back to markdown
1184
+ return {
1185
+ ...generatedArticle,
1186
+ content: renderMarkdown(mergedAST)
1187
+ };
1188
+ }
1189
+
1190
+ identifyEditorialSections(ast: MarkdownAST, edits: EditorialChange[]): Section[] {
1191
+ // Find sections that contain purely editorial content
1192
+ // (no factual data from graph)
1193
+ return ast.sections.filter(section =>
1194
+ this.isPurelyEditorial(section) &&
1195
+ this.wasEditedByHuman(section, edits)
1196
+ );
1197
+ }
1198
+
1199
+ mergeASTs(
1200
+ generated: MarkdownAST,
1201
+ current: MarkdownAST,
1202
+ preserveSections: Section[]
1203
+ ): MarkdownAST {
1204
+ const merged = { ...generated };
1205
+
1206
+ for (const section of preserveSections) {
1207
+ // Replace generated section with human version
1208
+ const sectionIndex = merged.sections.findIndex(s =>
1209
+ s.heading === section.heading
1210
+ );
1211
+
1212
+ if (sectionIndex >= 0) {
1213
+ merged.sections[sectionIndex] = section;
1214
+ }
1215
+ }
1216
+
1217
+ return merged;
1218
+ }
1219
+ }
1220
+ ```
1221
+
1222
+ ---
1223
+
1224
+ ## User Workflows
1225
+
1226
+ ### Workflow 1: Technical Writer Updates Documentation
1227
+
1228
+ **Before Kenogami (Traditional):**
1229
+ ```
1230
+ 1. Writer edits campaigns.md
1231
+ 2. Writer edits getting-started.md
1232
+ 3. Writer edits command-reference.md
1233
+ 4. (Oops, forgot to update examples in tutorial.md)
1234
+ 5. (Oops, parameter description inconsistent across files)
1235
+ ```
1236
+
1237
+ **With Kenogami (Graph-First):**
1238
+ ```
1239
+ 1. Writer opens Kenogami UI
1240
+ 2. Writer finds "campaigns create" command node
1241
+ 3. Writer edits parameter description in ONE place
1242
+ 4. Clicks "Regenerate affected articles" (4 articles updated automatically)
1243
+ 5. Reviews changes, approves
1244
+ 6. All articles now consistent ✅
1245
+ ```
1246
+
1247
+ ### Workflow 2: Developer Adds New Command
1248
+
1249
+ **Before:**
1250
+ ```
1251
+ 1. Developer implements `campaigns schedule` command
1252
+ 2. Developer documents it in campaigns.ts JSDoc
1253
+ 3. (Forgets to update user manual)
1254
+ 4. (User reads outdated docs, reports "missing feature")
1255
+ ```
1256
+
1257
+ **With Kenogami:**
1258
+ ```
1259
+ 1. Developer implements `campaigns schedule` command
1260
+ 2. Developer adds to knowledge graph (via script or UI):
1261
+ - Command node
1262
+ - Parameter nodes
1263
+ - Example nodes
1264
+ - Related command links
1265
+ 3. Run: `kenogami sync --direction graph-to-articles`
1266
+ 4. All relevant articles auto-updated ✅
1267
+ - Command reference updated
1268
+ - Tutorial examples added
1269
+ - Related commands section updated
1270
+ ```
1271
+
1272
+ ### Workflow 3: Resolving Conflicts
1273
+
1274
+ **Scenario:**
1275
+ ```
1276
+ Developer: Marks command as deprecated in graph
1277
+ Writer: Just wrote extensive tutorial about that command
1278
+ ```
1279
+
1280
+ **Resolution:**
1281
+ ```
1282
+ 1. Kenogami detects conflict during sync
1283
+ 2. Shows side-by-side comparison:
1284
+
1285
+ Graph: status = "deprecated_in_v2.0"
1286
+ Article: Contains 500-word tutorial (recently edited)
1287
+
1288
+ 3. Presents options:
1289
+ a) Keep tutorial, add deprecation notice
1290
+ b) Remove tutorial
1291
+ c) Move tutorial to "Legacy Commands" section
1292
+
1293
+ 4. Writer chooses (a)
1294
+ 5. Kenogami:
1295
+ - Preserves tutorial content
1296
+ - Adds deprecation notice at top
1297
+ - Updates all links to point to replacement command
1298
+ ```
1299
+
1300
+ ---
1301
+
1302
+ ## Integration with AI Testing
1303
+
1304
+ ### Bidirectional Benefit for Testing
1305
+
1306
+ **Testing Flow with Kenogami:**
1307
+
1308
+ ```
1309
+ ┌──────────────────────────────────────────────────────────┐
1310
+ │ 1. Knowledge Graph (Source of Truth) │
1311
+ │ - Commands, parameters, expected outputs │
1312
+ └────────────────┬─────────────────────────────────────────┘
1313
+
1314
+ ┌───────┴────────┐
1315
+ │ │
1316
+ ▼ ▼
1317
+ ┌──────────────┐ ┌──────────────┐
1318
+ │ Articles │ │ Test Cases │
1319
+ │ (Generated) │ │ (Generated) │
1320
+ └──────┬───────┘ └──────┬───────┘
1321
+ │ │
1322
+ │ │
1323
+ └────────┬────────┘
1324
+
1325
+ ┌─────────────────┐
1326
+ │ AI Testing │
1327
+ │ Validation │
1328
+ └────────┬─────────┘
1329
+
1330
+
1331
+ ┌─────────────────┐
1332
+ │ Test Results │
1333
+ │ (Pass/Fail) │
1334
+ └────────┬─────────┘
1335
+
1336
+ │ If fail: Update graph OR article
1337
+
1338
+ ┌─────────────────┐
1339
+ │ Feedback Loop │
1340
+ │ to Graph │
1341
+ └──────────────────┘
1342
+ ```
1343
+
1344
+ **Enhanced Test Generation:**
1345
+
1346
+ ```typescript
1347
+ class KenogamiTestGenerator extends TestCaseGenerator {
1348
+ constructor(private knowledgeGraph: KnowledgeGraph) {
1349
+ super();
1350
+ }
1351
+
1352
+ async generateTestsFromGraph(): Promise<TestCase[]> {
1353
+ // Query graph for all commands
1354
+ const commands = await this.knowledgeGraph.query(`
1355
+ MATCH (c:Command)
1356
+ OPTIONAL MATCH (c)-[:HAS_PARAMETER]->(p:Parameter)
1357
+ OPTIONAL MATCH (c)-[:HAS_EXAMPLE]->(e:Example)
1358
+ RETURN c, collect(p) as parameters, collect(e) as examples
1359
+ `);
1360
+
1361
+ // Generate test cases directly from graph
1362
+ return commands.map(cmd => ({
1363
+ id: `TEST-${cmd.c.id}`,
1364
+ command: this.buildCommandString(cmd.c, cmd.parameters),
1365
+ expectedBehavior: {
1366
+ exitCode: 0,
1367
+ output: cmd.examples[0]?.expected_output,
1368
+ schema: this.generateSchemaFromParameters(cmd.parameters)
1369
+ }
1370
+ }));
1371
+ }
1372
+
1373
+ async feedbackFailureToGraph(testResult: TestResult): Promise<void> {
1374
+ if (!testResult.passed) {
1375
+ // Analyze failure
1376
+ const analysis = await this.analyzeFailure(testResult);
1377
+
1378
+ if (analysis.type === 'documentation_issue') {
1379
+ // Update graph with correct information
1380
+ await this.knowledgeGraph.updateNode(analysis.nodeId, {
1381
+ [analysis.field]: analysis.correctValue,
1382
+ updated_by: 'ai_testing',
1383
+ updated_reason: analysis.reason
1384
+ });
1385
+
1386
+ // Trigger article regeneration
1387
+ await this.syncCoordinator.syncGraphToArticles();
1388
+ }
1389
+ }
1390
+ }
1391
+ }
1392
+ ```
1393
+
1394
+ **Test Results Feed Back to Graph:**
1395
+
1396
+ ```typescript
1397
+ // When tests discover undocumented behavior
1398
+ class TestResultAnalyzer {
1399
+ async analyzeAndUpdateGraph(results: TestResult[]): Promise<GraphUpdate[]> {
1400
+ const updates = [];
1401
+
1402
+ for (const result of results) {
1403
+ if (result.passed && result.confidence < 0.8) {
1404
+ // Test passed but with low confidence - might be undocumented behavior
1405
+ const analysis = await this.llm.analyze(`
1406
+ Test passed but documentation was unclear:
1407
+
1408
+ Command: ${result.testCase.command}
1409
+ Expected: ${result.testCase.expectedBehavior}
1410
+ Actual: ${result.actualOutput}
1411
+
1412
+ Should we update the knowledge graph with this information?
1413
+ `);
1414
+
1415
+ if (analysis.should_update) {
1416
+ updates.push({
1417
+ nodeId: result.testCase.commandId,
1418
+ field: analysis.field,
1419
+ newValue: analysis.value,
1420
+ source: 'ai_testing_discovery'
1421
+ });
1422
+ }
1423
+ }
1424
+
1425
+ if (!result.passed && result.actualBehaviorIsCorrect) {
1426
+ // Test failed because documentation is wrong
1427
+ updates.push({
1428
+ nodeId: result.testCase.commandId,
1429
+ field: result.mismatchField,
1430
+ oldValue: result.expected,
1431
+ newValue: result.actual,
1432
+ source: 'ai_testing_correction'
1433
+ });
1434
+ }
1435
+ }
1436
+
1437
+ // Apply updates to graph
1438
+ for (const update of updates) {
1439
+ await this.knowledgeGraph.applyUpdate(update);
1440
+ }
1441
+
1442
+ return updates;
1443
+ }
1444
+ }
1445
+ ```
1446
+
1447
+ ---
1448
+
1449
+ ## Summary: Impact on Flow
1450
+
1451
+ ### Before Kenogami (Traditional)
1452
+
1453
+ ```
1454
+ Articles → Extract → Knowledge Graph → Generate Tests → Validate
1455
+
1456
+ Problems:
1457
+ - Articles can be inconsistent
1458
+ - Changes require manual updates in multiple places
1459
+ - Graph is read-only, can't improve from testing
1460
+ - Documentation drift over time
1461
+ ```
1462
+
1463
+ ### With Kenogami (Bidirectional)
1464
+
1465
+ ```
1466
+ ┌─ Generate → Articles
1467
+
1468
+ Knowledge Graph ────┼─ Generate → Test Cases
1469
+ ▲ │
1470
+ │ └─ Generate → API Specs
1471
+
1472
+ └─ Updates from: Test Results, Human Edits, Code Analysis
1473
+
1474
+ Benefits:
1475
+ ✅ Single source of truth (the graph)
1476
+ ✅ Automatic consistency
1477
+ ✅ Test results improve documentation
1478
+ ✅ Multi-format output (articles, tests, specs) always in sync
1479
+ ✅ Changes propagate everywhere automatically
1480
+ ```
1481
+
1482
+ ### Key Architectural Shifts
1483
+
1484
+ | Aspect | Traditional | Kenogami |
1485
+ |--------|-------------|----------|
1486
+ | **Source of Truth** | Articles | Knowledge Graph |
1487
+ | **Article Creation** | Manual writing | Generated from graph |
1488
+ | **Updates** | Edit articles | Edit graph → regenerate |
1489
+ | **Consistency** | Manual effort | Automatic |
1490
+ | **Test Generation** | From articles | From graph (more reliable) |
1491
+ | **Test Feedback** | Manual fix docs | Auto-update graph |
1492
+ | **Multi-Format** | Duplicate content | Single source, multiple outputs |
1493
+ | **Version Control** | File-based | Graph snapshots + files |
1494
+
1495
+ ### Migration Path
1496
+
1497
+ ```
1498
+ Phase 1: Build graph from existing articles (one-time)
1499
+ Phase 2: Validate graph completeness
1500
+ Phase 3: Generate articles from graph (verify output)
1501
+ Phase 4: Enable bidirectional sync (with conflicts)
1502
+ Phase 5: Full graph-first workflow
1503
+ ```
1504
+
1505
+ ---
1506
+
1507
+ ## Conclusion
1508
+
1509
+ **Kenogami transforms documentation from static files into a living knowledge system** where:
1510
+
1511
+ 1. **Knowledge Graph is authoritative** - Facts live in one place
1512
+ 2. **Articles are generated views** - Consistent, up-to-date, multi-format
1513
+ 3. **Bidirectional sync** - Accept edits from both humans and automation
1514
+ 4. **Testing drives documentation** - Failed tests improve the graph
1515
+ 5. **Scale effortlessly** - Add CLI, Web, Mobile docs without duplication
1516
+
1517
+ This architecture is **ideal for AI testing** because the knowledge graph provides a **single, queryable, version-controlled source of truth** that both generates documentation AND test cases, creating a self-improving system.