@thinkingsage/kanon 0.8.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 (199) hide show
  1. package/CHANGELOG.md +410 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -0
  4. package/bridge/mcp-server.cjs +14171 -0
  5. package/package.json +98 -0
  6. package/src/adapters/capabilities.ts +178 -0
  7. package/src/adapters/claude-code.ts +110 -0
  8. package/src/adapters/cline.ts +98 -0
  9. package/src/adapters/codex.ts +173 -0
  10. package/src/adapters/copilot.ts +106 -0
  11. package/src/adapters/cursor.ts +97 -0
  12. package/src/adapters/degradation.ts +95 -0
  13. package/src/adapters/index.ts +324 -0
  14. package/src/adapters/kiro-frontmatter.ts +139 -0
  15. package/src/adapters/kiro-inclusion.ts +86 -0
  16. package/src/adapters/kiro.ts +412 -0
  17. package/src/adapters/qdeveloper.ts +115 -0
  18. package/src/adapters/types.ts +81 -0
  19. package/src/adapters/windsurf.ts +96 -0
  20. package/src/admin.ts +283 -0
  21. package/src/asset-conventions.ts +118 -0
  22. package/src/attribution-backfill.ts +319 -0
  23. package/src/attribution-report.ts +95 -0
  24. package/src/attribution.ts +239 -0
  25. package/src/backends/github.ts +194 -0
  26. package/src/backends/http.ts +122 -0
  27. package/src/backends/index.ts +39 -0
  28. package/src/backends/local.ts +47 -0
  29. package/src/backends/s3.ts +157 -0
  30. package/src/backends/types.ts +59 -0
  31. package/src/base-cache.ts +270 -0
  32. package/src/browse-ui.ts +3754 -0
  33. package/src/browse.ts +1038 -0
  34. package/src/build.ts +1108 -0
  35. package/src/catalog.ts +204 -0
  36. package/src/cli-deprecated.ts +29 -0
  37. package/src/cli.ts +773 -0
  38. package/src/collection-admin.ts +287 -0
  39. package/src/collection-builder.ts +464 -0
  40. package/src/collections.ts +116 -0
  41. package/src/compatibility.ts +105 -0
  42. package/src/config.ts +743 -0
  43. package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
  44. package/src/eval.ts +1169 -0
  45. package/src/file-writer.ts +61 -0
  46. package/src/format-registry.ts +141 -0
  47. package/src/guild/auto-updater.ts +163 -0
  48. package/src/guild/backend-resolver.ts +49 -0
  49. package/src/guild/cli.ts +592 -0
  50. package/src/guild/collection-expander.ts +47 -0
  51. package/src/guild/global-cache.ts +247 -0
  52. package/src/guild/hook-generator.ts +100 -0
  53. package/src/guild/manifest.ts +154 -0
  54. package/src/guild/path-utils.ts +12 -0
  55. package/src/guild/sync.ts +622 -0
  56. package/src/guild/version-resolver.ts +42 -0
  57. package/src/help/metadata.ts +445 -0
  58. package/src/help/renderer.ts +265 -0
  59. package/src/help/typo-suggester.ts +25 -0
  60. package/src/hooks/expression.ts +493 -0
  61. package/src/hooks/pipeline.ts +141 -0
  62. package/src/import.ts +773 -0
  63. package/src/importers/claude-code.ts +134 -0
  64. package/src/importers/cline.ts +103 -0
  65. package/src/importers/codex.ts +140 -0
  66. package/src/importers/copilot.ts +103 -0
  67. package/src/importers/cursor.ts +105 -0
  68. package/src/importers/index.ts +390 -0
  69. package/src/importers/kiro.ts +110 -0
  70. package/src/importers/qdeveloper.ts +103 -0
  71. package/src/importers/types.ts +54 -0
  72. package/src/importers/windsurf.ts +104 -0
  73. package/src/install.ts +1005 -0
  74. package/src/manifest-admin.ts +306 -0
  75. package/src/mcp-bridge.ts +240 -0
  76. package/src/mutation/delta.ts +50 -0
  77. package/src/mutation/history.ts +66 -0
  78. package/src/mutation/operators.ts +524 -0
  79. package/src/mutation/runner.ts +332 -0
  80. package/src/new.ts +106 -0
  81. package/src/outcomes/collision.ts +127 -0
  82. package/src/outcomes/normalize.ts +208 -0
  83. package/src/outcomes/registry.ts +173 -0
  84. package/src/parser.ts +446 -0
  85. package/src/provenance-backfill-cli.ts +319 -0
  86. package/src/provenance-backfill.ts +520 -0
  87. package/src/publish.ts +354 -0
  88. package/src/reconcile-orchestrator.ts +502 -0
  89. package/src/reconcile-report-renderer.ts +176 -0
  90. package/src/resolve-body.ts +15 -0
  91. package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
  92. package/src/rosetta/builtins/contracts.ts +1033 -0
  93. package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
  94. package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
  95. package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
  96. package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
  97. package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
  98. package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
  99. package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
  100. package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
  101. package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
  102. package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
  103. package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
  104. package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
  105. package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
  106. package/src/rosetta/builtins/sources/cline-native.ts +176 -0
  107. package/src/rosetta/builtins/sources/codex-native.ts +343 -0
  108. package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
  109. package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
  110. package/src/rosetta/builtins/sources/index.ts +95 -0
  111. package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
  112. package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
  113. package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
  114. package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
  115. package/src/rosetta/builtins/sources/superpowers.ts +240 -0
  116. package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
  117. package/src/rosetta/builtins/targets/claude-code.ts +181 -0
  118. package/src/rosetta/builtins/targets/cline.ts +87 -0
  119. package/src/rosetta/builtins/targets/codex.ts +226 -0
  120. package/src/rosetta/builtins/targets/copilot.ts +103 -0
  121. package/src/rosetta/builtins/targets/cursor.ts +87 -0
  122. package/src/rosetta/builtins/targets/index.ts +60 -0
  123. package/src/rosetta/builtins/targets/kiro.ts +278 -0
  124. package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
  125. package/src/rosetta/builtins/targets/windsurf.ts +87 -0
  126. package/src/rosetta/canonical.ts +729 -0
  127. package/src/rosetta/compatibility.ts +432 -0
  128. package/src/rosetta/contracts.ts +329 -0
  129. package/src/rosetta/detector.ts +724 -0
  130. package/src/rosetta/diagnostics.ts +630 -0
  131. package/src/rosetta/engine-bootstrap.ts +103 -0
  132. package/src/rosetta/engine.ts +744 -0
  133. package/src/rosetta/index.ts +381 -0
  134. package/src/rosetta/inspection.ts +530 -0
  135. package/src/rosetta/plan.ts +448 -0
  136. package/src/rosetta/provenance-digest.ts +369 -0
  137. package/src/rosetta/reconcile.ts +812 -0
  138. package/src/rosetta/redaction.ts +467 -0
  139. package/src/rosetta/registry.ts +712 -0
  140. package/src/rosetta/renderers.ts +571 -0
  141. package/src/rosetta/request-guard.ts +335 -0
  142. package/src/rosetta/resolution.ts +419 -0
  143. package/src/rosetta/source-accounting.ts +233 -0
  144. package/src/rosetta/templates.ts +129 -0
  145. package/src/rosetta-cli.ts +717 -0
  146. package/src/rosetta-docs-generator.ts +793 -0
  147. package/src/rosetta-profiles-cli.ts +367 -0
  148. package/src/schemas.ts +1712 -0
  149. package/src/spec-coordination.ts +1141 -0
  150. package/src/temper.ts +747 -0
  151. package/src/template-bundle-loader.ts +312 -0
  152. package/src/template-engine.ts +53 -0
  153. package/src/translation-application-policy.ts +496 -0
  154. package/src/translation-orchestrator.ts +1013 -0
  155. package/src/translation-plan-applier.ts +473 -0
  156. package/src/tutorial.ts +305 -0
  157. package/src/validate.ts +1093 -0
  158. package/src/versioning.ts +553 -0
  159. package/src/wizard.ts +660 -0
  160. package/src/workspace.ts +237 -0
  161. package/templates/eval-contexts/claude-code.md.njk +6 -0
  162. package/templates/eval-contexts/cline.md.njk +6 -0
  163. package/templates/eval-contexts/copilot.md.njk +6 -0
  164. package/templates/eval-contexts/cursor.md.njk +6 -0
  165. package/templates/eval-contexts/kiro.md.njk +10 -0
  166. package/templates/eval-contexts/qdeveloper.md.njk +6 -0
  167. package/templates/eval-contexts/windsurf.md.njk +6 -0
  168. package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
  169. package/templates/harness-adapters/_base/base.md.njk +16 -0
  170. package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
  171. package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
  172. package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
  173. package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
  174. package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
  175. package/templates/harness-adapters/cline/hook.sh.njk +4 -0
  176. package/templates/harness-adapters/cline/mcp.json.njk +1 -0
  177. package/templates/harness-adapters/cline/rule.md.njk +1 -0
  178. package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
  179. package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
  180. package/templates/harness-adapters/codex/skill.md.njk +27 -0
  181. package/templates/harness-adapters/copilot/agents.md.njk +1 -0
  182. package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
  183. package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
  184. package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
  185. package/templates/harness-adapters/cursor/rule.md.njk +6 -0
  186. package/templates/harness-adapters/kiro/hook.json.njk +1 -0
  187. package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
  188. package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
  189. package/templates/harness-adapters/kiro/power.md.njk +12 -0
  190. package/templates/harness-adapters/kiro/steering.md.njk +16 -0
  191. package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
  192. package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
  193. package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
  194. package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
  195. package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
  196. package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
  197. package/templates/knowledge/hooks.yaml.njk +4 -0
  198. package/templates/knowledge/knowledge.md.njk +53 -0
  199. package/templates/knowledge/mcp-servers.yaml.njk +2 -0
@@ -0,0 +1,445 @@
1
+ export interface UsageExample {
2
+ comment: string;
3
+ invocation: string;
4
+ }
5
+
6
+ export interface OptionGroup {
7
+ label: string;
8
+ options: string[];
9
+ }
10
+
11
+ export interface CommandHelpMeta {
12
+ examples: UsageExample[];
13
+ optionGroups?: OptionGroup[];
14
+ showHarnessList?: boolean;
15
+ }
16
+
17
+ export const commandMetaRegistry: Record<string, CommandHelpMeta> = {
18
+ build: {
19
+ examples: [
20
+ { comment: "Build for all harnesses", invocation: "kanon build" },
21
+ {
22
+ comment: "Build for Kiro only",
23
+ invocation: "kanon build --harness kiro",
24
+ },
25
+ {
26
+ comment: "Strict mode — fail on unsupported capabilities",
27
+ invocation: "kanon build --strict",
28
+ },
29
+ ],
30
+ optionGroups: [
31
+ {
32
+ label: "Build Options",
33
+ options: ["--harness", "--strict"],
34
+ },
35
+ ],
36
+ showHarnessList: true,
37
+ },
38
+ install: {
39
+ examples: [
40
+ {
41
+ comment: "Install a single artifact for Kiro from local dist",
42
+ invocation: "kanon install my-artifact --harness kiro --source .",
43
+ },
44
+ {
45
+ comment: "Install for all harnesses",
46
+ invocation: "kanon install my-artifact --all --source .",
47
+ },
48
+ {
49
+ comment: "Install into the global cache (for guild sync)",
50
+ invocation: "kanon install --global my-artifact --backend github",
51
+ },
52
+ {
53
+ comment: "Install from a GitHub release",
54
+ invocation: "kanon install my-artifact --from-release v1.0.0",
55
+ },
56
+ {
57
+ comment: "Install from a named backend in kanon.config.yaml",
58
+ invocation: "kanon install my-artifact --backend internal",
59
+ },
60
+ {
61
+ comment: "Install into a specific workspace project",
62
+ invocation: "kanon install my-artifact --project frontend",
63
+ },
64
+ {
65
+ comment: "Preview what would be installed",
66
+ invocation: "kanon install my-artifact --dry-run --source .",
67
+ },
68
+ ],
69
+ optionGroups: [
70
+ {
71
+ label: "Source Options",
72
+ options: ["--source", "--from-release", "--backend"],
73
+ },
74
+ {
75
+ label: "Target Options",
76
+ options: ["--harness", "--all", "--global", "--project"],
77
+ },
78
+ {
79
+ label: "Behavior Options",
80
+ options: ["--force", "--dry-run"],
81
+ },
82
+ ],
83
+ showHarnessList: true,
84
+ },
85
+ new: {
86
+ examples: [
87
+ {
88
+ comment: "Scaffold interactively (opens wizard)",
89
+ invocation: "kanon new my-artifact",
90
+ },
91
+ {
92
+ comment: "Scaffold a power with defaults (skip wizard)",
93
+ invocation: "kanon new my-power --type power --yes",
94
+ },
95
+ {
96
+ comment: "Scaffold a workflow artifact",
97
+ invocation: "kanon new deploy-checklist --type workflow",
98
+ },
99
+ ],
100
+ },
101
+ tutorial: {
102
+ examples: [
103
+ {
104
+ comment: "Start the guided walkthrough for first-time authors",
105
+ invocation: "kanon tutorial",
106
+ },
107
+ ],
108
+ },
109
+ validate: {
110
+ examples: [
111
+ {
112
+ comment: "Validate all artifacts",
113
+ invocation: "kanon validate",
114
+ },
115
+ {
116
+ comment: "Validate a specific artifact",
117
+ invocation: "kanon validate knowledge/my-artifact",
118
+ },
119
+ {
120
+ comment:
121
+ "Run security checks (prompt injection, dangerous hooks, obfuscation)",
122
+ invocation: "kanon validate --security",
123
+ },
124
+ ],
125
+ },
126
+ catalog: {
127
+ examples: [
128
+ {
129
+ comment: "Show catalog subcommands",
130
+ invocation: "kanon catalog --help",
131
+ },
132
+ ],
133
+ },
134
+ "catalog generate": {
135
+ examples: [
136
+ {
137
+ comment: "Generate catalog.json from all knowledge sources",
138
+ invocation: "kanon catalog generate",
139
+ },
140
+ {
141
+ comment: "Regenerate after adding or removing artifacts",
142
+ invocation: "kanon catalog generate",
143
+ },
144
+ ],
145
+ },
146
+ "catalog browse": {
147
+ examples: [
148
+ {
149
+ comment: "Open the catalog browser in your default browser",
150
+ invocation: "kanon catalog browse",
151
+ },
152
+ {
153
+ comment: "Browse on a custom port",
154
+ invocation: "kanon catalog browse --port 8080",
155
+ },
156
+ ],
157
+ },
158
+ "catalog export": {
159
+ examples: [
160
+ {
161
+ comment: "Export a self-contained static site for GitHub Pages",
162
+ invocation: "kanon catalog export --output dist/web",
163
+ },
164
+ {
165
+ comment: "Export to a custom directory",
166
+ invocation: "kanon catalog export --output ../site",
167
+ },
168
+ ],
169
+ },
170
+ collection: {
171
+ examples: [
172
+ {
173
+ comment: "Show collection status for all collections",
174
+ invocation: "kanon collection",
175
+ },
176
+ ],
177
+ },
178
+ "collection new": {
179
+ examples: [
180
+ {
181
+ comment: "Scaffold a new collection interactively",
182
+ invocation: "kanon collection new",
183
+ },
184
+ {
185
+ comment: "Scaffold with a name pre-filled",
186
+ invocation: "kanon collection new aws-tools",
187
+ },
188
+ ],
189
+ },
190
+ "collection build": {
191
+ examples: [
192
+ {
193
+ comment: "Build all collection bundles for all harnesses",
194
+ invocation: "kanon collection build",
195
+ },
196
+ {
197
+ comment: "Build collections for Kiro only",
198
+ invocation: "kanon collection build --harness kiro",
199
+ },
200
+ ],
201
+ showHarnessList: true,
202
+ },
203
+ import: {
204
+ examples: [
205
+ {
206
+ comment: "Import a Kiro power into canonical format",
207
+ invocation: "kanon import path/to/power",
208
+ },
209
+ {
210
+ comment: "Import all artifacts from a directory",
211
+ invocation: "kanon import path/to/artifacts --all",
212
+ },
213
+ {
214
+ comment: "Import and assign to a collection",
215
+ invocation: "kanon import path/to/power --collections my-collection",
216
+ },
217
+ {
218
+ comment: "Import accepting derived upstream attribution (no prompt)",
219
+ invocation: "kanon import path/to/power --attribution-defaults",
220
+ },
221
+ {
222
+ comment: "Import without capturing attribution",
223
+ invocation: "kanon import path/to/power --no-attribution",
224
+ },
225
+ {
226
+ comment: "Preview what would be imported",
227
+ invocation: "kanon import path/to/power --dry-run",
228
+ },
229
+ ],
230
+ optionGroups: [
231
+ {
232
+ label: "Source Options",
233
+ options: ["--format", "--all"],
234
+ },
235
+ {
236
+ label: "Attribution Options",
237
+ options: ["--attribution-defaults", "--no-attribution"],
238
+ },
239
+ {
240
+ label: "Behavior Options",
241
+ options: ["--dry-run", "--collections", "--knowledge-dir"],
242
+ },
243
+ ],
244
+ },
245
+ attribute: {
246
+ examples: [
247
+ {
248
+ comment: "Print a NOTICES report of upstream attribution by license",
249
+ invocation: "kanon attribute",
250
+ },
251
+ {
252
+ comment: "Write the NOTICES report to a file",
253
+ invocation: "kanon attribute --output NOTICES",
254
+ },
255
+ ],
256
+ optionGroups: [
257
+ {
258
+ label: "Output Options",
259
+ options: ["--output"],
260
+ },
261
+ ],
262
+ },
263
+ "attribute backfill": {
264
+ examples: [
265
+ {
266
+ comment:
267
+ "Preview the attribution backfill (clean vs manual-review split)",
268
+ invocation: "kanon attribute backfill --dry-run",
269
+ },
270
+ {
271
+ comment: "Backfill attribution blocks for imported artifacts",
272
+ invocation: "kanon attribute backfill",
273
+ },
274
+ ],
275
+ optionGroups: [
276
+ {
277
+ label: "Behavior Options",
278
+ options: ["--dry-run"],
279
+ },
280
+ ],
281
+ },
282
+ publish: {
283
+ examples: [
284
+ {
285
+ comment: "Publish to the default GitHub backend",
286
+ invocation: "kanon publish",
287
+ },
288
+ {
289
+ comment: "Publish with an explicit version tag",
290
+ invocation: "kanon publish --tag v1.2.0",
291
+ },
292
+ {
293
+ comment: "Publish to a named backend from kanon.config.yaml",
294
+ invocation: "kanon publish --backend internal",
295
+ },
296
+ {
297
+ comment: "Dry run — validate and package without uploading",
298
+ invocation: "kanon publish --dry-run",
299
+ },
300
+ {
301
+ comment: "Include release notes",
302
+ invocation: "kanon publish --notes CHANGELOG.md",
303
+ },
304
+ ],
305
+ optionGroups: [
306
+ { label: "Target Options", options: ["--backend", "--tag"] },
307
+ { label: "Behavior Options", options: ["--dry-run", "--notes"] },
308
+ ],
309
+ },
310
+ eval: {
311
+ examples: [
312
+ {
313
+ comment: "Run evals for all artifacts and harnesses",
314
+ invocation: "kanon eval",
315
+ },
316
+ {
317
+ comment: "Run evals for a specific artifact",
318
+ invocation: "kanon eval my-artifact",
319
+ },
320
+ {
321
+ comment: "Run evals for Cursor with a custom threshold",
322
+ invocation: "kanon eval --harness cursor --threshold 0.8",
323
+ },
324
+ {
325
+ comment: "Scaffold an eval suite for an artifact",
326
+ invocation: "kanon eval --init my-artifact",
327
+ },
328
+ {
329
+ comment: "Machine-readable output for CI",
330
+ invocation: "kanon eval --ci --output results.json",
331
+ },
332
+ ],
333
+ optionGroups: [
334
+ {
335
+ label: "Execution Options",
336
+ options: ["--harness", "--provider", "--threshold", "--init"],
337
+ },
338
+ {
339
+ label: "Output Options",
340
+ options: ["--output", "--ci", "--no-context"],
341
+ },
342
+ ],
343
+ showHarnessList: true,
344
+ },
345
+ guild: {
346
+ examples: [
347
+ {
348
+ comment: "Typical workflow: add artifact, sync, check status",
349
+ invocation:
350
+ "kanon guild init adr && kanon guild sync && kanon guild status",
351
+ },
352
+ ],
353
+ },
354
+ "guild init": {
355
+ examples: [
356
+ {
357
+ comment: "Add an artifact to the manifest",
358
+ invocation: "kanon guild init my-artifact",
359
+ },
360
+ {
361
+ comment: "Add a collection from a named backend",
362
+ invocation:
363
+ "kanon guild init neon-caravan --collection --backend github",
364
+ },
365
+ {
366
+ comment: "Pin a specific version as optional",
367
+ invocation:
368
+ "kanon guild init my-artifact --version 0.1.0 --mode optional",
369
+ },
370
+ ],
371
+ optionGroups: [
372
+ {
373
+ label: "Entry Options",
374
+ options: ["--collection", "--mode", "--version"],
375
+ },
376
+ {
377
+ label: "Source Options",
378
+ options: ["--backend"],
379
+ },
380
+ ],
381
+ },
382
+ "guild sync": {
383
+ examples: [
384
+ {
385
+ comment: "Sync manifest artifacts into harness targets",
386
+ invocation: "kanon guild sync",
387
+ },
388
+ {
389
+ comment: "Check for remote updates before syncing",
390
+ invocation: "kanon guild sync --auto-update",
391
+ },
392
+ {
393
+ comment: "Sync only Kiro harness files",
394
+ invocation: "kanon guild sync --harness kiro",
395
+ },
396
+ {
397
+ comment: "Preview what would be synced",
398
+ invocation: "kanon guild sync --dry-run",
399
+ },
400
+ ],
401
+ optionGroups: [
402
+ {
403
+ label: "Update Options",
404
+ options: ["--auto-update", "--throttle"],
405
+ },
406
+ {
407
+ label: "Behavior Options",
408
+ options: ["--dry-run", "--harness"],
409
+ },
410
+ ],
411
+ showHarnessList: true,
412
+ },
413
+ "guild status": {
414
+ examples: [
415
+ {
416
+ comment: "Show manifest entries, resolved versions, and sync state",
417
+ invocation: "kanon guild status",
418
+ },
419
+ ],
420
+ },
421
+ "guild hook install": {
422
+ examples: [
423
+ {
424
+ comment: "Print shell hook snippet for auto-sync on cd",
425
+ invocation: "kanon guild hook install",
426
+ },
427
+ {
428
+ comment: "Install hook for a specific shell",
429
+ invocation: "kanon guild hook install --shell zsh",
430
+ },
431
+ ],
432
+ },
433
+ help: {
434
+ examples: [
435
+ {
436
+ comment: "Show help for the build command",
437
+ invocation: "kanon help build",
438
+ },
439
+ {
440
+ comment: "Show help for a subcommand",
441
+ invocation: "kanon help guild init",
442
+ },
443
+ ],
444
+ },
445
+ };
@@ -0,0 +1,265 @@
1
+ import { Chalk } from "chalk";
2
+ import type { CommandHelpMeta } from "./metadata";
3
+
4
+ /** Chalk instance that always emits ANSI codes regardless of terminal detection. */
5
+ const colorChalk = new Chalk({ level: 3 });
6
+ /** Chalk instance that never emits ANSI codes. */
7
+ const plainChalk = new Chalk({ level: 0 });
8
+
9
+ export interface RenderOptions {
10
+ useColor: boolean;
11
+ }
12
+
13
+ export const SUPPORTED_HARNESSES = [
14
+ "kiro",
15
+ "claude-code",
16
+ "codex",
17
+ "copilot",
18
+ "cursor",
19
+ "windsurf",
20
+ "cline",
21
+ "qdeveloper",
22
+ ] as const;
23
+
24
+ /**
25
+ * Render the root help screen.
26
+ *
27
+ * Sections (in order):
28
+ * 1. Description
29
+ * 2. Usage line
30
+ * 3. Commands table (aligned columns)
31
+ * 4. Global Options
32
+ * 5. Getting Started tip
33
+ */
34
+ export interface RootCommand {
35
+ name: string;
36
+ description: string;
37
+ subcommands?: { name: string; description: string }[];
38
+ }
39
+
40
+ export function renderRootHelp(
41
+ commands: RootCommand[],
42
+ opts: RenderOptions,
43
+ ): string {
44
+ const pad = " ";
45
+ const lines: string[] = [];
46
+
47
+ // --- helpers for optional chalk styling ---
48
+ const c = opts.useColor ? colorChalk : plainChalk;
49
+ const bold = (s: string) => c.bold(s);
50
+ const cyan = (s: string) => c.cyan(s);
51
+ const yellow = (s: string) => c.yellow(s);
52
+ const dim = (s: string) => c.dim(s);
53
+
54
+ // 1. Description
55
+ lines.push(
56
+ `${pad}${bold("Kanon")} ${dim("—")} write knowledge once, compile to every harness`,
57
+ );
58
+ lines.push("");
59
+
60
+ // 2. Usage
61
+ lines.push(`${pad}${yellow("Usage:")} kanon <command> [options]`);
62
+ lines.push("");
63
+
64
+ // 3. Commands table — structured with optional subcommand groups
65
+ // Column width accounts for top-level names and indented subcommand names
66
+ const allNames = [
67
+ ...commands.map((cmd) => cmd.name),
68
+ ...commands.flatMap(
69
+ (cmd) => cmd.subcommands?.map((s) => ` ${s.name}`) ?? [],
70
+ ),
71
+ ];
72
+ const nameColWidth = Math.max(...allNames.map((n) => n.length)) + 2;
73
+
74
+ lines.push(`${pad}${yellow("Commands:")}`);
75
+ for (const cmd of commands) {
76
+ const paddedName = cmd.name.padEnd(nameColWidth);
77
+ if (cmd.subcommands && cmd.subcommands.length > 0) {
78
+ // Parent command: show name + description, then indented subcommands
79
+ lines.push(`${pad}${pad}${cyan(paddedName)}${cmd.description}`);
80
+ for (const sub of cmd.subcommands) {
81
+ const paddedSub = ` ${sub.name}`.padEnd(nameColWidth);
82
+ lines.push(`${pad}${pad}${dim(paddedSub)}${sub.description}`);
83
+ }
84
+ } else {
85
+ lines.push(`${pad}${pad}${cyan(paddedName)}${cmd.description}`);
86
+ }
87
+ }
88
+ lines.push("");
89
+
90
+ // 4. Global Options
91
+ const globalOptions = [
92
+ { flags: "-V, --version", description: "Output version information" },
93
+ { flags: "-h, --help", description: "Show help" },
94
+ { flags: "--no-color", description: "Disable color output" },
95
+ ];
96
+
97
+ const flagColWidth =
98
+ Math.max(...globalOptions.map((o) => o.flags.length)) + 2;
99
+
100
+ lines.push(`${pad}${yellow("Global Options:")}`);
101
+ for (const opt of globalOptions) {
102
+ const paddedFlags = opt.flags.padEnd(flagColWidth);
103
+ lines.push(`${pad}${pad}${cyan(paddedFlags)}${opt.description}`);
104
+ }
105
+ lines.push("");
106
+
107
+ // 5. Getting Started tip
108
+ lines.push(`${pad}${yellow("Getting Started:")}`);
109
+ lines.push(
110
+ `${pad}${pad}Run ${cyan("kanon new <name>")} to create your first knowledge artifact.`,
111
+ );
112
+ lines.push("");
113
+
114
+ return lines.join("\n");
115
+ }
116
+
117
+ /**
118
+ * Render help for a single command.
119
+ *
120
+ * Sections (in order):
121
+ * 1. Description
122
+ * 2. Usage line
123
+ * 3. Options (grouped or flat)
124
+ * 4. Examples (if metadata provides them)
125
+ */
126
+ export function renderCommandHelp(
127
+ _commandName: string,
128
+ description: string,
129
+ usage: string,
130
+ options: { flags: string; description: string }[],
131
+ meta: CommandHelpMeta | undefined,
132
+ opts: RenderOptions,
133
+ subcommands?: { name: string; description: string }[],
134
+ ): string {
135
+ const pad = " ";
136
+ const lines: string[] = [];
137
+
138
+ // --- helpers for optional chalk styling ---
139
+ const c = opts.useColor ? colorChalk : plainChalk;
140
+ const cyan = (s: string) => c.cyan(s);
141
+ const yellow = (s: string) => c.yellow(s);
142
+ const dim = (s: string) => c.dim(s);
143
+
144
+ // Possibly augment --harness option description with harness names
145
+ const augmentedOptions = options.map((opt) => {
146
+ if (meta?.showHarnessList && opt.flags.includes("--harness")) {
147
+ const harnessList = SUPPORTED_HARNESSES.join(", ");
148
+ return {
149
+ flags: opt.flags,
150
+ description: `${opt.description} (${harnessList})`,
151
+ };
152
+ }
153
+ return opt;
154
+ });
155
+
156
+ // 1. Description
157
+ lines.push(`${pad}${description}`);
158
+ lines.push("");
159
+
160
+ // 2. Usage
161
+ lines.push(`${pad}${yellow("Usage:")} ${usage}`);
162
+ lines.push("");
163
+
164
+ // 2b. Subcommands (for parent commands like catalog, collection)
165
+ if (subcommands && subcommands.length > 0) {
166
+ const nameColWidth = Math.max(...subcommands.map((s) => s.name.length)) + 2;
167
+ lines.push(`${pad}${yellow("Commands:")}`);
168
+ for (const sub of subcommands) {
169
+ lines.push(
170
+ `${pad}${pad}${cyan(sub.name.padEnd(nameColWidth))}${sub.description}`,
171
+ );
172
+ }
173
+ lines.push("");
174
+ }
175
+
176
+ // 3. Options — grouped or flat
177
+ const flagColWidth =
178
+ Math.max(...augmentedOptions.map((o) => o.flags.length), 0) + 2;
179
+
180
+ if (meta?.optionGroups && meta.optionGroups.length > 0) {
181
+ // Build a set of flags that belong to a group
182
+ const groupedFlags = new Set<string>();
183
+ for (const group of meta.optionGroups) {
184
+ for (const flag of group.options) {
185
+ groupedFlags.add(flag);
186
+ }
187
+ }
188
+
189
+ // Render each group
190
+ for (const group of meta.optionGroups) {
191
+ lines.push(`${pad}${yellow(`${group.label}:`)}`);
192
+ for (const flag of group.options) {
193
+ const opt = augmentedOptions.find((o) => o.flags.includes(flag));
194
+ if (opt) {
195
+ const paddedFlags = opt.flags.padEnd(flagColWidth);
196
+ lines.push(`${pad}${pad}${cyan(paddedFlags)}${opt.description}`);
197
+ }
198
+ }
199
+ lines.push("");
200
+ }
201
+
202
+ // Render ungrouped options (if any)
203
+ const ungrouped = augmentedOptions.filter(
204
+ (o) => !Array.from(groupedFlags).some((f) => o.flags.includes(f)),
205
+ );
206
+ if (ungrouped.length > 0) {
207
+ lines.push(`${pad}${yellow("Options:")}`);
208
+ for (const opt of ungrouped) {
209
+ const paddedFlags = opt.flags.padEnd(flagColWidth);
210
+ lines.push(`${pad}${pad}${cyan(paddedFlags)}${opt.description}`);
211
+ }
212
+ lines.push("");
213
+ }
214
+ } else {
215
+ // Flat list
216
+ lines.push(`${pad}${yellow("Options:")}`);
217
+ for (const opt of augmentedOptions) {
218
+ const paddedFlags = opt.flags.padEnd(flagColWidth);
219
+ lines.push(`${pad}${pad}${cyan(paddedFlags)}${opt.description}`);
220
+ }
221
+ lines.push("");
222
+ }
223
+
224
+ // 4. Examples
225
+ if (meta?.examples && meta.examples.length > 0) {
226
+ lines.push(`${pad}${yellow("Examples:")}`);
227
+ for (const example of meta.examples) {
228
+ lines.push(`${pad}${pad}${dim(`# ${example.comment}`)}`);
229
+ lines.push(`${pad}${pad}${cyan(`$ ${example.invocation}`)}`);
230
+ lines.push("");
231
+ }
232
+ }
233
+
234
+ return lines.join("\n");
235
+ }
236
+
237
+ /**
238
+ * Render version information block.
239
+ *
240
+ * Output:
241
+ * kanon v<version>
242
+ * bun v<bunVersion>
243
+ * platform <os>-<arch>
244
+ */
245
+ export function renderVersion(version: string, opts: RenderOptions): string {
246
+ const pad = " ";
247
+ const lines: string[] = [];
248
+
249
+ // --- helpers for optional chalk styling ---
250
+ const c = opts.useColor ? colorChalk : plainChalk;
251
+ const cyan = (s: string) => c.cyan(s);
252
+ const dim = (s: string) => c.dim(s);
253
+
254
+ // Bun version — gracefully degrade if not running in Bun
255
+ const bunVersion = typeof Bun !== "undefined" ? Bun.version : "unknown";
256
+
257
+ const platform = `${process.platform}-${process.arch}`;
258
+
259
+ lines.push(`${pad}${cyan("kanon")} ${dim(`v${version}`)}`);
260
+ lines.push(`${pad}${cyan("bun")} ${dim(`v${bunVersion}`)}`);
261
+ lines.push(`${pad}${cyan("platform")} ${dim(platform)}`);
262
+ lines.push("");
263
+
264
+ return lines.join("\n");
265
+ }