kward 0.70.0 → 0.72.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +1 -1
  3. data/CHANGELOG.md +89 -3
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +90 -2
  6. data/README.md +34 -6
  7. data/Rakefile +96 -0
  8. data/doc/agent-tools.md +52 -0
  9. data/doc/api.md +92 -0
  10. data/doc/authentication.md +58 -23
  11. data/doc/code-search.md +42 -2
  12. data/doc/configuration.md +102 -13
  13. data/doc/context-budgeting.md +136 -0
  14. data/doc/context-tools.md +83 -0
  15. data/doc/editor.md +394 -0
  16. data/doc/extensibility.md +16 -7
  17. data/doc/files.md +100 -0
  18. data/doc/getting-started.md +25 -18
  19. data/doc/git.md +122 -0
  20. data/doc/memory.md +24 -4
  21. data/doc/personas.md +34 -5
  22. data/doc/plugins.md +74 -3
  23. data/doc/releasing.md +45 -8
  24. data/doc/rpc.md +77 -15
  25. data/doc/session-management.md +254 -0
  26. data/doc/shell.md +286 -0
  27. data/doc/tabs.md +122 -0
  28. data/doc/troubleshooting.md +77 -1
  29. data/doc/usage.md +60 -15
  30. data/doc/web-search.md +12 -4
  31. data/doc/workspace-tools.md +144 -0
  32. data/examples/plugins/space_invaders.rb +377 -0
  33. data/lib/kward/agent.rb +1 -1
  34. data/lib/kward/cli/commands.rb +41 -2
  35. data/lib/kward/cli/git.rb +150 -0
  36. data/lib/kward/cli/interactive_turn.rb +73 -9
  37. data/lib/kward/cli/openrouter_commands.rb +55 -0
  38. data/lib/kward/cli/plugins.rb +54 -4
  39. data/lib/kward/cli/prompt_interface.rb +111 -6
  40. data/lib/kward/cli/rendering.rb +11 -6
  41. data/lib/kward/cli/runtime_helpers.rb +133 -3
  42. data/lib/kward/cli/sessions.rb +262 -13
  43. data/lib/kward/cli/settings.rb +216 -37
  44. data/lib/kward/cli/slash_commands.rb +439 -8
  45. data/lib/kward/cli/tabs.rb +695 -0
  46. data/lib/kward/cli.rb +171 -26
  47. data/lib/kward/compactor.rb +4 -1
  48. data/lib/kward/config_files.rb +125 -5
  49. data/lib/kward/context_budget_meter.rb +44 -0
  50. data/lib/kward/conversation.rb +59 -22
  51. data/lib/kward/editor_mode.rb +25 -0
  52. data/lib/kward/ekwsh.rb +362 -0
  53. data/lib/kward/model/client.rb +37 -50
  54. data/lib/kward/model/context_usage.rb +13 -6
  55. data/lib/kward/model/model_info.rb +92 -16
  56. data/lib/kward/model/payloads.rb +2 -0
  57. data/lib/kward/openrouter_model_cache.rb +120 -0
  58. data/lib/kward/plugin_registry.rb +108 -1
  59. data/lib/kward/project_files.rb +52 -0
  60. data/lib/kward/prompt_history.rb +82 -0
  61. data/lib/kward/prompt_interface/banner.rb +16 -51
  62. data/lib/kward/prompt_interface/composer_controller.rb +124 -83
  63. data/lib/kward/prompt_interface/composer_renderer.rb +116 -14
  64. data/lib/kward/prompt_interface/composer_state.rb +96 -27
  65. data/lib/kward/prompt_interface/editor/auto_close_pairs.rb +123 -0
  66. data/lib/kward/prompt_interface/editor/auto_indent.rb +509 -0
  67. data/lib/kward/prompt_interface/editor/buffer.rb +109 -0
  68. data/lib/kward/prompt_interface/editor/controller.rb +1018 -0
  69. data/lib/kward/prompt_interface/editor/endwise.rb +321 -0
  70. data/lib/kward/prompt_interface/editor/file_marker.rb +40 -0
  71. data/lib/kward/prompt_interface/editor/indent_navigation.rb +61 -0
  72. data/lib/kward/prompt_interface/editor/kill_ring.rb +78 -0
  73. data/lib/kward/prompt_interface/editor/modes/emacs.rb +259 -0
  74. data/lib/kward/prompt_interface/editor/modes/modern.rb +353 -0
  75. data/lib/kward/prompt_interface/editor/modes/vibe.rb +1962 -0
  76. data/lib/kward/prompt_interface/editor/renderer.rb +243 -0
  77. data/lib/kward/prompt_interface/editor/search.rb +76 -0
  78. data/lib/kward/prompt_interface/editor/selections.rb +120 -0
  79. data/lib/kward/prompt_interface/editor/state.rb +1249 -0
  80. data/lib/kward/prompt_interface/editor/status_text.rb +23 -0
  81. data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +420 -0
  82. data/lib/kward/prompt_interface/editor/undo_history.rb +46 -0
  83. data/lib/kward/prompt_interface/editor/vibe_state.rb +44 -0
  84. data/lib/kward/prompt_interface/file_overlay.rb +211 -0
  85. data/lib/kward/prompt_interface/git_prompt.rb +299 -0
  86. data/lib/kward/prompt_interface/interactive/controller.rb +186 -0
  87. data/lib/kward/prompt_interface/interactive/renderer.rb +71 -0
  88. data/lib/kward/prompt_interface/interactive/state.rb +62 -0
  89. data/lib/kward/prompt_interface/key_handler.rb +416 -43
  90. data/lib/kward/prompt_interface/layout.rb +2 -2
  91. data/lib/kward/prompt_interface/overlay_renderer.rb +21 -2
  92. data/lib/kward/prompt_interface/project_browser.rb +524 -0
  93. data/lib/kward/prompt_interface/prompt_renderer.rb +32 -13
  94. data/lib/kward/prompt_interface/question_prompt.rb +122 -82
  95. data/lib/kward/prompt_interface/runtime_state.rb +49 -1
  96. data/lib/kward/prompt_interface/screen.rb +17 -0
  97. data/lib/kward/prompt_interface/selection_prompt.rb +511 -58
  98. data/lib/kward/prompt_interface/stream_state.rb +7 -0
  99. data/lib/kward/prompt_interface/transcript_buffer.rb +13 -16
  100. data/lib/kward/prompt_interface/transcript_renderer.rb +3 -3
  101. data/lib/kward/prompt_interface.rb +307 -35
  102. data/lib/kward/prompts/commands.rb +7 -1
  103. data/lib/kward/prompts.rb +4 -2
  104. data/lib/kward/rpc/server.rb +45 -11
  105. data/lib/kward/rpc/session_manager.rb +52 -53
  106. data/lib/kward/rpc/session_tree_rows.rb +9 -115
  107. data/lib/kward/rpc/tool_event_normalizer.rb +1 -1
  108. data/lib/kward/session_store.rb +67 -4
  109. data/lib/kward/session_tree_nodes.rb +136 -0
  110. data/lib/kward/session_tree_renderer.rb +9 -131
  111. data/lib/kward/tab_store.rb +47 -0
  112. data/lib/kward/telemetry/logger.rb +5 -3
  113. data/lib/kward/text_boundary.rb +25 -0
  114. data/lib/kward/tool_output_compactor.rb +127 -0
  115. data/lib/kward/tools/base.rb +8 -2
  116. data/lib/kward/tools/context_budget_stats.rb +54 -0
  117. data/lib/kward/tools/context_for_task.rb +202 -0
  118. data/lib/kward/tools/read_file.rb +8 -4
  119. data/lib/kward/tools/registry.rb +92 -15
  120. data/lib/kward/tools/retrieve_tool_output.rb +71 -0
  121. data/lib/kward/tools/search/web.rb +2 -2
  122. data/lib/kward/tools/summarize_file_structure.rb +29 -0
  123. data/lib/kward/tools/tool_call.rb +12 -0
  124. data/lib/kward/version.rb +1 -1
  125. data/lib/kward/workers/git_guard.rb +68 -0
  126. data/lib/kward/workers/live_view.rb +49 -0
  127. data/lib/kward/workers/manager.rb +288 -0
  128. data/lib/kward/workers/store.rb +72 -0
  129. data/lib/kward/workers/tool_policy.rb +23 -0
  130. data/lib/kward/workers/worker.rb +82 -0
  131. data/lib/kward/workers/write_lock.rb +38 -0
  132. data/lib/kward/workers.rb +7 -0
  133. data/lib/kward/workspace.rb +154 -12
  134. data/templates/default/fulldoc/html/css/kward.css +362 -42
  135. data/templates/default/fulldoc/html/full_list.erb +107 -0
  136. data/templates/default/fulldoc/html/js/kward.js +161 -2
  137. data/templates/default/fulldoc/html/setup.rb +8 -0
  138. data/templates/default/kward_navigation.rb +102 -0
  139. data/templates/default/layout/html/layout.erb +43 -10
  140. data/templates/default/layout/html/setup.rb +39 -38
  141. metadata +65 -3
  142. data/lib/kward/resources/avatar_kward_logo.rb +0 -50
  143. data/lib/kward/resources/pixel_logo.rb +0 -232
@@ -5,17 +5,90 @@ const guideLinks = {
5
5
  'doc/configuration.md': 'file.configuration.html',
6
6
  'doc/authentication.md': 'file.authentication.html',
7
7
  'doc/troubleshooting.md': 'file.troubleshooting.html',
8
+ 'doc/session-management.md': 'file.session-management.html',
8
9
  'doc/memory.md': 'file.memory.html',
9
10
  'doc/personas.md': 'file.personas.html',
10
11
  'doc/extensibility.md': 'file.extensibility.html',
11
12
  'doc/plugins.md': 'file.plugins.html',
13
+ 'doc/agent-tools.md': 'file.agent-tools.html',
14
+ 'doc/workspace-tools.md': 'file.workspace-tools.html',
12
15
  'doc/web-search.md': 'file.web-search.html',
13
16
  'doc/code-search.md': 'file.code-search.html',
17
+ 'doc/context-tools.md': 'file.context-tools.html',
14
18
  'doc/rpc.md': 'file.rpc.html',
15
19
  'doc/releasing.md': 'file.releasing.html'
16
20
  }
17
21
 
18
22
  let pageController = null
23
+ let navigating = false
24
+
25
+ const scrollToCurrentHash = () => {
26
+ if (!window.location.hash) {
27
+ window.scrollTo({ top: 0 })
28
+ return
29
+ }
30
+
31
+ const id = decodeURIComponent(window.location.hash.slice(1))
32
+ const target = document.getElementById(id) || document.getElementsByName(id)[0]
33
+ if (target) target.scrollIntoView()
34
+ }
35
+
36
+ const replacePage = async (url, pushState = true) => {
37
+ if (navigating) return
38
+ navigating = true
39
+ document.documentElement.classList.add('kward-navigating')
40
+
41
+ try {
42
+ const response = await fetch(url, { headers: { 'X-Requested-With': 'Kward-Docs' } })
43
+ if (!response.ok) throw new Error(`Navigation failed with ${response.status}`)
44
+
45
+ const html = await response.text()
46
+ const nextDocument = new DOMParser().parseFromString(html, 'text/html')
47
+ if (!nextDocument.body) throw new Error('Navigation response did not include a body')
48
+
49
+ document.title = nextDocument.title
50
+ document.body.className = nextDocument.body.className
51
+ document.body.innerHTML = nextDocument.body.innerHTML
52
+
53
+ if (pushState) window.history.pushState({ kwardDocs: true }, '', url)
54
+ initializePage()
55
+ scrollToCurrentHash()
56
+ } catch (_error) {
57
+ window.location.href = url
58
+ } finally {
59
+ navigating = false
60
+ document.documentElement.classList.remove('kward-navigating')
61
+ }
62
+ }
63
+
64
+ const isNavigableLink = (event, link) => {
65
+ if (event.defaultPrevented || event.button !== 0) return false
66
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false
67
+ if (link.target && link.target !== '_self') return false
68
+ if (link.download || link.dataset.noTurbo) return false
69
+
70
+ const url = new URL(link.href, window.location.href)
71
+ if (url.origin !== window.location.origin) return false
72
+ if (url.pathname === window.location.pathname && url.search === window.location.search) return false
73
+
74
+ return url.pathname === '/' || url.pathname.endsWith('/') || url.pathname.endsWith('.html')
75
+ }
76
+
77
+ const visitLink = (link) => replacePage(link.href)
78
+
79
+ const setupTurbolinks = (signal) => {
80
+ document.addEventListener('click', (event) => {
81
+ const link = event.target.closest('a[href]')
82
+ if (!link || !isNavigableLink(event, link)) return
83
+
84
+ event.preventDefault()
85
+ visitLink(link)
86
+ }, { signal })
87
+
88
+ window.addEventListener('popstate', () => {
89
+ replacePage(window.location.href, false)
90
+ }, { signal })
91
+ }
19
92
 
20
93
  const setupGuideSearch = (signal) => {
21
94
  const form = document.querySelector('.kward-guide-search')
@@ -151,7 +224,7 @@ const setupGuideSearch = (signal) => {
151
224
  } else if (event.key === 'Enter') {
152
225
  if (selectedIndex >= 0 && items[selectedIndex]) {
153
226
  event.preventDefault()
154
- window.location.href = items[selectedIndex].href
227
+ visitLink(items[selectedIndex])
155
228
  }
156
229
  }
157
230
  }, { signal })
@@ -159,7 +232,7 @@ const setupGuideSearch = (signal) => {
159
232
  form.addEventListener('submit', (event) => {
160
233
  event.preventDefault()
161
234
  const firstResult = results.querySelector('a')
162
- if (firstResult) window.location.href = firstResult.href
235
+ if (firstResult) visitLink(firstResult)
163
236
  }, { signal })
164
237
 
165
238
  document.addEventListener('click', (event) => {
@@ -252,6 +325,90 @@ const rewriteGuideLinks = () => {
252
325
  })
253
326
  }
254
327
 
328
+ const setupTableOfContents = () => {
329
+ const existingToc = document.getElementById('toc')
330
+ if (document.querySelector('.kward-no-toc')) {
331
+ if (existingToc) existingToc.remove()
332
+ return
333
+ }
334
+
335
+ const fileContents = document.getElementById('filecontents')
336
+ const content = document.getElementById('content')
337
+ if (!fileContents || !content || content.querySelector('#toc')) return
338
+
339
+ const headingTags = ['h2', 'h3', 'h4', 'h5', 'h6']
340
+ if (fileContents.querySelectorAll('h1').length > 1) headingTags.unshift('h1')
341
+
342
+ const headings = Array.from(fileContents.querySelectorAll(headingTags.join(', ')))
343
+ .filter((heading) => !heading.closest('.method_details .docstring') && heading.id !== 'filecontents')
344
+ if (headings.length === 0) return
345
+
346
+ const topLevel = document.createElement('ol')
347
+ topLevel.className = 'top'
348
+
349
+ let currentList = topLevel
350
+ let currentItem = null
351
+ let counter = 0
352
+ let lastLevel = parseInt(headingTags[0].slice(1), 10)
353
+
354
+ headings.forEach((heading) => {
355
+ const level = parseInt(heading.tagName.slice(1), 10)
356
+
357
+ if (!heading.id) {
358
+ let proposedId = heading.getAttribute('toc-id')
359
+ if (!proposedId) {
360
+ proposedId = heading.textContent.replace(/[^a-z0-9-]/gi, '_')
361
+ if (document.getElementById(proposedId)) {
362
+ proposedId += counter
363
+ counter += 1
364
+ }
365
+ }
366
+ heading.id = proposedId
367
+ }
368
+
369
+ if (level > lastLevel) {
370
+ while (level > lastLevel) {
371
+ if (!currentItem) {
372
+ currentItem = document.createElement('li')
373
+ currentList.appendChild(currentItem)
374
+ }
375
+ const nestedList = document.createElement('ol')
376
+ currentItem.appendChild(nestedList)
377
+ currentList = nestedList
378
+ currentItem = null
379
+ lastLevel += 1
380
+ }
381
+ } else if (level < lastLevel) {
382
+ while (level < lastLevel && currentList.parentElement) {
383
+ currentList = currentList.parentElement.parentElement
384
+ lastLevel -= 1
385
+ }
386
+ }
387
+
388
+ const title = heading.getAttribute('toc-title') || heading.textContent
389
+ const item = document.createElement('li')
390
+ const link = document.createElement('a')
391
+ link.href = `#${heading.id}`
392
+ link.textContent = title
393
+ item.appendChild(link)
394
+ currentList.appendChild(item)
395
+ currentItem = item
396
+ })
397
+
398
+ const toc = document.createElement('div')
399
+ toc.id = 'toc'
400
+ toc.innerHTML = '<p class="title hide_toc"><a href="#"><strong>Table of Contents</strong></a></p>'
401
+ toc.appendChild(topLevel)
402
+ content.insertBefore(toc, content.firstChild)
403
+
404
+ const hideLink = toc.querySelector('.hide_toc')
405
+ hideLink.addEventListener('click', (event) => {
406
+ event.preventDefault()
407
+ const hidden = toc.classList.toggle('hidden')
408
+ topLevel.style.display = hidden ? 'none' : ''
409
+ })
410
+ }
411
+
255
412
  const setupCodeCopy = () => {
256
413
  document.querySelectorAll('pre').forEach((block) => {
257
414
  if (block.closest('.code-copy-wrapper')) return
@@ -285,9 +442,11 @@ const initializePage = () => {
285
442
  if (pageController) pageController.abort()
286
443
  pageController = new AbortController()
287
444
 
445
+ setupTurbolinks(pageController.signal)
288
446
  setupGuideSearch(pageController.signal)
289
447
  setupNavigation(pageController.signal)
290
448
  rewriteGuideLinks()
449
+ setupTableOfContents()
291
450
  setupCodeCopy()
292
451
  }
293
452
 
@@ -1,8 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../kward_navigation"
4
+
5
+ include KwardDocsNavigationData
6
+
3
7
  def generate_assets
4
8
  super
5
9
  asset('css/kward.css', file('css/kward.css', true))
6
10
  asset('js/kward.js', file('js/kward.js', true))
7
11
  asset('images/kward_logo.png', file('images/kward_logo.png', true))
8
12
  end
13
+
14
+ def stylesheets_full_list
15
+ super + %w(css/kward.css)
16
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KwardDocsNavigationData
4
+ GUIDE_GROUPS = [
5
+ [
6
+ "Start here",
7
+ [
8
+ ["Getting started", "file.getting-started.html"],
9
+ ["Usage", "file.usage.html"],
10
+ ["Configuration", "file.configuration.html"],
11
+ ["Authentication", "file.authentication.html"],
12
+ ["Troubleshooting", "file.troubleshooting.html"]
13
+ ]
14
+ ],
15
+ [
16
+ "Feature guides",
17
+ [
18
+ ["Sessions", "file.session-management.html"],
19
+ ["Tabs", "file.tabs.html"],
20
+ ["Memory", "file.memory.html"],
21
+ ["Personas", "file.personas.html"]
22
+ ]
23
+ ],
24
+ [
25
+ "User Tools",
26
+ [
27
+ ["Project files", "file.files.html"],
28
+ ["Integrated Editor", "file.editor.html"],
29
+ ["Git", "file.git.html"],
30
+ ["Shell", "file.shell.html"]
31
+ ]
32
+ ]
33
+ ].freeze
34
+
35
+ EXTENSION_GROUPS = [
36
+ [
37
+ "Customize",
38
+ [
39
+ ["Extensibility", "file.extensibility.html"],
40
+ ["Plugins", "file.plugins.html"]
41
+ ]
42
+ ],
43
+ [
44
+ "Integrate",
45
+ [
46
+ ["RPC protocol", "file.rpc.html"],
47
+ ["Releasing", "file.releasing.html"]
48
+ ]
49
+ ],
50
+ [
51
+ "Agent tools",
52
+ [
53
+ ["Overview", "file.agent-tools.html"],
54
+ ["Workspace tools", "file.workspace-tools.html"],
55
+ ["Web search", "file.web-search.html"],
56
+ ["Code search", "file.code-search.html"],
57
+ ["Context tools", "file.context-tools.html"],
58
+ ["Context budgeting", "file.context-budgeting.html"]
59
+ ]
60
+ ]
61
+ ].freeze
62
+
63
+ API_OVERVIEW = "file.api.html"
64
+ API_GROUPS = [
65
+ [
66
+ "Reference",
67
+ [
68
+ ["Overview", API_OVERVIEW],
69
+ ["Classes & Modules", "class_list.html"],
70
+ ["Methods", "method_list.html"],
71
+ ["Files", "file_list.html"]
72
+ ]
73
+ ],
74
+ [
75
+ "Key namespaces",
76
+ [
77
+ ["Kward", "Kward.html"],
78
+ ["Tools", "Kward/Tools.html"],
79
+ ["RPC", "Kward/RPC.html"],
80
+ ["CLI", "Kward/CLI.html"],
81
+ ["Prompts", "Kward/Prompts.html"],
82
+ ["Skills", "Kward/Skills.html"]
83
+ ]
84
+ ]
85
+ ].freeze
86
+
87
+ def guide_groups
88
+ GUIDE_GROUPS
89
+ end
90
+
91
+ def extension_groups
92
+ EXTENSION_GROUPS
93
+ end
94
+
95
+ def api_groups
96
+ API_GROUPS
97
+ end
98
+
99
+ def api_overview
100
+ API_OVERVIEW
101
+ end
102
+ end
@@ -22,8 +22,8 @@
22
22
  <nav id="kward-primary-nav" class="kward-topnav" aria-label="Primary navigation">
23
23
  <a href="<%= url_for('index.html') %>" class="<%= 'active' if home_page? %>">Home</a>
24
24
  <div class="kward-nav-menu <%= 'active' if guide_page? %>">
25
- <a class="kward-nav-menu-link" href="<%= url_for('file.README.html') %>">User Guides</a>
26
- <button class="kward-nav-menu-button" type="button" aria-label="Expand user guides" aria-expanded="false">⌄</button>
25
+ <a class="kward-nav-menu-link" href="<%= url_for(guide_overview) %>">Guides</a>
26
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand guides" aria-expanded="false">⌄</button>
27
27
  <div class="kward-nav-dropdown">
28
28
  <% guide_groups.each do |title, items| %>
29
29
  <section>
@@ -35,13 +35,44 @@
35
35
  <% end %>
36
36
  </div>
37
37
  </div>
38
- <a href="<%= url_for('Kward.html') %>" class="<%= 'active' if api_page? %>">API Docs</a>
38
+ <div class="kward-nav-menu <%= 'active' if extension_page? %>">
39
+ <a class="kward-nav-menu-link" href="<%= url_for(extension_overview) %>">Advanced</a>
40
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand advanced guides" aria-expanded="false">⌄</button>
41
+ <div class="kward-nav-dropdown kward-advanced-nav-dropdown">
42
+ <% extension_groups.each do |title, items| %>
43
+ <section>
44
+ <h2><%= h title %></h2>
45
+ <% items.each do |label, link| %>
46
+ <a href="<%= url_for(link) %>"><%= h label %></a>
47
+ <% end %>
48
+ </section>
49
+ <% end %>
50
+ </div>
51
+ </div>
52
+ <div class="kward-nav-menu <%= 'active' if api_page? %>">
53
+ <a class="kward-nav-menu-link" href="<%= url_for(api_overview) %>">API Docs</a>
54
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand API docs" aria-expanded="false">⌄</button>
55
+ <div class="kward-nav-dropdown kward-api-nav-dropdown">
56
+ <% api_groups.each do |title, items| %>
57
+ <section>
58
+ <h2><%= h title %></h2>
59
+ <% items.each do |label, link| %>
60
+ <a href="<%= url_for(link) %>"><%= h label %></a>
61
+ <% end %>
62
+ </section>
63
+ <% end %>
64
+ </div>
65
+ </div>
39
66
  </nav>
40
- <form class="kward-guide-search" role="search">
41
- <label class="kward-sr-only" for="kward-guide-search-input">Search user guides</label>
42
- <input id="kward-guide-search-input" type="search" placeholder="Search guides" autocomplete="off" aria-label="Search user guides" aria-controls="kward-guide-search-results">
43
- <div id="kward-guide-search-results" class="kward-guide-search-results" role="listbox" aria-label="Search results"></div>
44
- </form>
67
+ <% if api_page? %>
68
+ <a class="kward-api-index-link" href="<%= url_for('class_list.html') %>">Search API index</a>
69
+ <% else %>
70
+ <form class="kward-guide-search" role="search">
71
+ <label class="kward-sr-only" for="kward-guide-search-input">Search user guides</label>
72
+ <input id="kward-guide-search-input" type="search" placeholder="Search guides" autocomplete="off" aria-label="Search user guides" aria-controls="kward-guide-search-results">
73
+ <div id="kward-guide-search-results" class="kward-guide-search-results" role="listbox" aria-label="Search results"></div>
74
+ </form>
75
+ <% end %>
45
76
  <script type="application/json" id="kward-guide-search-index"><%= guide_search_index_json %></script>
46
77
  </header>
47
78
 
@@ -51,14 +82,16 @@
51
82
  <div class="kward-hero-copy">
52
83
  <p class="kward-eyebrow">⌘ Ruby CLI Coding Agent</p>
53
84
  <h1>
54
- <span class="kward-swosh" aria-label="Your terminal.">
85
+ <span class="kward-swosh" aria-label="Your terminal. Your RPC frontend. Your workflow.">
55
86
  <span class="kward-swosh-text kward-swosh-text-a" aria-hidden="true">Your terminal.</span>
56
87
  <span class="kward-swosh-text kward-swosh-text-b" aria-hidden="true">Your RPC frontend.</span>
88
+ <span class="kward-swosh-text kward-swosh-text-e" aria-hidden="true">Your workflow.</span>
57
89
  </span>
58
90
  <br>
59
- <span class="kward-swosh" aria-label="Your agent.">
91
+ <span class="kward-swosh" aria-label="Your agent. Your LLM engine. Your harness.">
60
92
  <span class="kward-swosh-text kward-swosh-text-c" aria-hidden="true">Your agent.</span>
61
93
  <span class="kward-swosh-text kward-swosh-text-d" aria-hidden="true">Your LLM engine.</span>
94
+ <span class="kward-swosh-text kward-swosh-text-f" aria-hidden="true">Your harness.</span>
62
95
  </span>
63
96
  </h1>
64
97
  <p class="kward-lede">Kward is an extendable Ruby CLI coding agent that helps you understand your project, edit files, run commands, search the web, and automate workflows—right from your terminal.</p>
@@ -1,44 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require_relative "../../kward_navigation"
4
5
 
5
6
  module KwardDocsNavigation
6
- GUIDE_GROUPS = [
7
- [
8
- "Start here",
9
- [
10
- ["Getting started", "file.getting-started.html"],
11
- ["Usage", "file.usage.html"],
12
- ["Configuration", "file.configuration.html"],
13
- ["Authentication", "file.authentication.html"],
14
- ["Troubleshooting", "file.troubleshooting.html"]
15
- ]
16
- ],
17
- [
18
- "Feature guides",
19
- [
20
- ["Memory", "file.memory.html"],
21
- ["Personas", "file.personas.html"],
22
- ["Extensibility", "file.extensibility.html"],
23
- ["Plugins", "file.plugins.html"],
24
- ["Web search", "file.web-search.html"],
25
- ["Code search", "file.code-search.html"]
26
- ]
27
- ],
28
- [
29
- "Advanced/reference",
30
- [
31
- ["RPC protocol", "file.rpc.html"],
32
- ["Releasing", "file.releasing.html"]
33
- ]
34
- ]
35
- ].freeze
7
+ include KwardDocsNavigationData
8
+ API_FILE_LINKS = {
9
+ "doc/api.md" => API_OVERVIEW
10
+ }.freeze
11
+ API_LINKS = ([API_OVERVIEW] + API_GROUPS.flat_map { |_title, items| items.map(&:last) }).freeze
12
+
13
+ EXTENSION_OVERVIEW = "file.extensibility.html"
14
+ EXTENSION_LINKS = ([EXTENSION_OVERVIEW] + EXTENSION_GROUPS.flat_map { |_title, items| items.map(&:last) }).uniq.freeze
36
15
 
37
16
  GUIDE_OVERVIEW = "file.README.html"
38
17
  GUIDE_LINKS = ([GUIDE_OVERVIEW] + GUIDE_GROUPS.flat_map { |_title, items| items.map(&:last) }).freeze
39
18
  GUIDE_SEARCH_FILES = [
40
19
  ["Overview", "README.md", GUIDE_OVERVIEW],
41
- *GUIDE_GROUPS.flat_map do |_title, items|
20
+ *(GUIDE_GROUPS + EXTENSION_GROUPS).flat_map do |_title, items|
42
21
  items.map do |label, link|
43
22
  source = "doc/#{link.delete_prefix("file.").delete_suffix(".html")}.md"
44
23
  [label, source, link]
@@ -51,24 +30,34 @@ module KwardDocsNavigation
51
30
  "doc/configuration.md" => "file.configuration.html",
52
31
  "doc/authentication.md" => "file.authentication.html",
53
32
  "doc/troubleshooting.md" => "file.troubleshooting.html",
33
+ "doc/session-management.md" => "file.session-management.html",
34
+ "doc/tabs.md" => "file.tabs.html",
35
+ "doc/files.md" => "file.files.html",
36
+ "doc/editor.md" => "file.editor.html",
37
+ "doc/git.md" => "file.git.html",
38
+ "doc/shell.md" => "file.shell.html",
54
39
  "doc/memory.md" => "file.memory.html",
55
40
  "doc/personas.md" => "file.personas.html",
56
41
  "doc/extensibility.md" => "file.extensibility.html",
57
42
  "doc/plugins.md" => "file.plugins.html",
43
+ "doc/agent-tools.md" => "file.agent-tools.html",
44
+ "doc/workspace-tools.md" => "file.workspace-tools.html",
45
+ "doc/context-budgeting.md" => "file.context-budgeting.html",
58
46
  "doc/web-search.md" => "file.web-search.html",
59
47
  "doc/code-search.md" => "file.code-search.html",
48
+ "doc/context-tools.md" => "file.context-tools.html",
60
49
  "doc/rpc.md" => "file.rpc.html",
61
50
  "doc/releasing.md" => "file.releasing.html"
62
51
  }.freeze
63
52
 
64
- def guide_groups
65
- GUIDE_GROUPS
66
- end
67
-
68
53
  def guide_overview
69
54
  GUIDE_OVERVIEW
70
55
  end
71
56
 
57
+ def extension_overview
58
+ EXTENSION_OVERVIEW
59
+ end
60
+
72
61
  def guide_search_index_json
73
62
  JSON.generate(guide_search_index).gsub("</", "<\\/")
74
63
  end
@@ -99,7 +88,15 @@ module KwardDocsNavigation
99
88
  end
100
89
 
101
90
  def guide_file?
102
- defined?(@file) && GUIDE_FILE_LINKS.key?(@file&.filename.to_s)
91
+ defined?(@file) && GUIDE_LINKS.include?(GUIDE_FILE_LINKS[@file&.filename.to_s])
92
+ end
93
+
94
+ def extension_file?
95
+ defined?(@file) && EXTENSION_LINKS.include?(GUIDE_FILE_LINKS[@file&.filename.to_s])
96
+ end
97
+
98
+ def api_file?
99
+ defined?(@file) && API_FILE_LINKS.key?(@file&.filename.to_s)
103
100
  end
104
101
 
105
102
  def home_page?
@@ -110,8 +107,12 @@ module KwardDocsNavigation
110
107
  (readme_file? && !options.index) || guide_file? || GUIDE_LINKS.include?(current_docs_path)
111
108
  end
112
109
 
110
+ def extension_page?
111
+ extension_file? || EXTENSION_LINKS.include?(current_docs_path)
112
+ end
113
+
113
114
  def api_page?
114
- !home_page? && !guide_page?
115
+ api_file? || API_LINKS.include?(current_docs_path) || (!home_page? && !guide_page? && !extension_page?)
115
116
  end
116
117
 
117
118
  def diskfile
@@ -121,7 +122,7 @@ module KwardDocsNavigation
121
122
  end
122
123
 
123
124
  def rewrite_guide_links(html)
124
- GUIDE_FILE_LINKS.each do |source, target|
125
+ GUIDE_FILE_LINKS.merge(API_FILE_LINKS).each do |source, target|
125
126
  html = html.gsub(%(href="#{source}"), %(href="#{target}"))
126
127
  end
127
128
  html