kward 0.68.0 → 0.69.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.
- checksums.yaml +4 -4
- data/.github/workflows/pages.yml +48 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +8 -2
- data/README.md +32 -25
- data/Rakefile +14 -1
- data/doc/authentication.md +74 -56
- data/doc/code-search.md +55 -28
- data/doc/configuration.md +18 -0
- data/doc/extensibility.md +89 -128
- data/doc/getting-started.md +52 -54
- data/doc/memory.md +51 -118
- data/doc/personas.md +417 -0
- data/doc/plugins.md +55 -97
- data/doc/releasing.md +3 -1
- data/doc/rpc.md +1 -1
- data/doc/usage.md +125 -144
- data/doc/web-search.md +80 -14
- data/exe/kward +2 -0
- data/lib/kward/agent.rb +1 -1
- data/lib/kward/cli/commands.rb +10 -3
- data/lib/kward/cli/compaction.rb +3 -3
- data/lib/kward/cli/interactive_turn.rb +3 -1
- data/lib/kward/cli/memory_commands.rb +16 -16
- data/lib/kward/cli/plugins.rb +3 -3
- data/lib/kward/cli/prompt_interface.rb +15 -13
- data/lib/kward/cli/rendering.rb +35 -46
- data/lib/kward/cli/runtime_helpers.rb +13 -2
- data/lib/kward/cli/sessions.rb +21 -21
- data/lib/kward/cli/settings.rb +49 -43
- data/lib/kward/cli/slash_commands.rb +6 -4
- data/lib/kward/cli/stats.rb +2 -2
- data/lib/kward/cli/sysprompt.rb +57 -0
- data/lib/kward/cli/tool_summaries.rb +5 -1
- data/lib/kward/cli.rb +14 -2
- data/lib/kward/cli_transcript_formatter.rb +36 -5
- data/lib/kward/compactor.rb +2 -2
- data/lib/kward/config_files.rb +45 -10
- data/lib/kward/conversation.rb +41 -9
- data/lib/kward/memory/manager.rb +131 -14
- data/lib/kward/message_access.rb +6 -0
- data/lib/kward/model/context_usage.rb +11 -10
- data/lib/kward/model/model_info.rb +18 -1
- data/lib/kward/model/payloads.rb +89 -10
- data/lib/kward/model/stream_parser.rb +258 -25
- data/lib/kward/prompt_interface/question_prompt.rb +1 -1
- data/lib/kward/prompt_interface/transcript_renderer.rb +20 -11
- data/lib/kward/prompts.rb +61 -7
- data/lib/kward/rpc/server.rb +7 -2
- data/lib/kward/rpc/session_manager.rb +18 -2
- data/lib/kward/rpc/session_metrics.rb +2 -2
- data/lib/kward/rpc/transcript_normalizer.rb +47 -0
- data/lib/kward/session_store.rb +40 -1
- data/lib/kward/starter_pack_installer.rb +2 -2
- data/lib/kward/tools/fetch_content.rb +41 -0
- data/lib/kward/tools/fetch_raw.rb +40 -0
- data/lib/kward/tools/registry.rb +9 -2
- data/lib/kward/tools/search/web.rb +3 -3
- data/lib/kward/tools/search/web_fetch.rb +202 -0
- data/lib/kward/tools/tool_call.rb +2 -0
- data/lib/kward/version.rb +1 -1
- data/templates/default/fulldoc/html/css/kward.css +1501 -0
- data/templates/default/fulldoc/html/images/kward_logo.png +0 -0
- data/templates/default/fulldoc/html/js/kward.js +296 -0
- data/templates/default/fulldoc/html/setup.rb +8 -0
- data/templates/default/layout/html/breadcrumb.erb +11 -0
- data/templates/default/layout/html/layout.erb +141 -0
- data/templates/default/layout/html/setup.rb +139 -0
- metadata +14 -1
|
Binary file
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
const resetScrollPosition = () => {
|
|
2
|
+
if (window.location.hash) return
|
|
3
|
+
|
|
4
|
+
document.documentElement.scrollTop = 0
|
|
5
|
+
if (document.body) document.body.scrollTop = 0
|
|
6
|
+
window.scrollTo(0, 0)
|
|
7
|
+
|
|
8
|
+
const main = document.getElementById('main')
|
|
9
|
+
if (main) {
|
|
10
|
+
main.scrollTop = 0
|
|
11
|
+
main.scrollLeft = 0
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!window.location.hash && 'scrollRestoration' in history) {
|
|
16
|
+
history.scrollRestoration = 'manual'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
resetScrollPosition()
|
|
20
|
+
window.addEventListener('load', resetScrollPosition)
|
|
21
|
+
window.addEventListener('pageshow', resetScrollPosition)
|
|
22
|
+
window.addEventListener('DOMContentLoaded', resetScrollPosition)
|
|
23
|
+
window.setTimeout(resetScrollPosition, 0)
|
|
24
|
+
window.setTimeout(resetScrollPosition, 50)
|
|
25
|
+
window.setTimeout(resetScrollPosition, 250)
|
|
26
|
+
if (window.requestAnimationFrame) window.requestAnimationFrame(resetScrollPosition)
|
|
27
|
+
|
|
28
|
+
const guideLinks = {
|
|
29
|
+
'doc/getting-started.md': 'file.getting-started.html',
|
|
30
|
+
'doc/usage.md': 'file.usage.html',
|
|
31
|
+
'doc/configuration.md': 'file.configuration.html',
|
|
32
|
+
'doc/authentication.md': 'file.authentication.html',
|
|
33
|
+
'doc/troubleshooting.md': 'file.troubleshooting.html',
|
|
34
|
+
'doc/memory.md': 'file.memory.html',
|
|
35
|
+
'doc/personas.md': 'file.personas.html',
|
|
36
|
+
'doc/extensibility.md': 'file.extensibility.html',
|
|
37
|
+
'doc/plugins.md': 'file.plugins.html',
|
|
38
|
+
'doc/web-search.md': 'file.web-search.html',
|
|
39
|
+
'doc/code-search.md': 'file.code-search.html',
|
|
40
|
+
'doc/rpc.md': 'file.rpc.html',
|
|
41
|
+
'doc/releasing.md': 'file.releasing.html'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let pageController = null
|
|
45
|
+
|
|
46
|
+
const setupGuideSearch = (signal) => {
|
|
47
|
+
const form = document.querySelector('.kward-guide-search')
|
|
48
|
+
const input = document.getElementById('kward-guide-search-input')
|
|
49
|
+
const results = document.getElementById('kward-guide-search-results')
|
|
50
|
+
const indexNode = document.getElementById('kward-guide-search-index')
|
|
51
|
+
|
|
52
|
+
if (!form || !input || !results || !indexNode) return
|
|
53
|
+
|
|
54
|
+
let index = []
|
|
55
|
+
try {
|
|
56
|
+
index = JSON.parse(indexNode.textContent || '[]')
|
|
57
|
+
} catch (_error) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const closeResults = () => {
|
|
62
|
+
results.classList.remove('open')
|
|
63
|
+
results.innerHTML = ''
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const excerpt = (text, query) => {
|
|
67
|
+
const normalizedText = text.toLowerCase()
|
|
68
|
+
const normalizedQuery = query.toLowerCase()
|
|
69
|
+
const matchIndex = normalizedText.indexOf(normalizedQuery)
|
|
70
|
+
const start = Math.max(0, matchIndex - 70)
|
|
71
|
+
const end = Math.min(text.length, (matchIndex < 0 ? 0 : matchIndex) + normalizedQuery.length + 120)
|
|
72
|
+
const prefix = start > 0 ? '…' : ''
|
|
73
|
+
const suffix = end < text.length ? '…' : ''
|
|
74
|
+
return `${prefix}${text.slice(start, end).trim()}${suffix}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const search = (query) => {
|
|
78
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean)
|
|
79
|
+
if (terms.length === 0) return []
|
|
80
|
+
|
|
81
|
+
return index
|
|
82
|
+
.map((item) => {
|
|
83
|
+
const title = item.title || ''
|
|
84
|
+
const path = item.path || ''
|
|
85
|
+
const text = item.text || ''
|
|
86
|
+
const haystack = `${title} ${path} ${text}`.toLowerCase()
|
|
87
|
+
if (!terms.every((term) => haystack.includes(term))) return null
|
|
88
|
+
|
|
89
|
+
const normalizedTitle = title.toLowerCase()
|
|
90
|
+
const normalizedPath = path.toLowerCase()
|
|
91
|
+
const titleMatches = terms.filter((term) => normalizedTitle.includes(term)).length
|
|
92
|
+
const pathMatches = terms.filter((term) => normalizedPath.includes(term)).length
|
|
93
|
+
return { item, score: titleMatches * 3 + pathMatches * 2 }
|
|
94
|
+
})
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.sort((left, right) => right.score - left.score || left.item.title.localeCompare(right.item.title))
|
|
97
|
+
.slice(0, 6)
|
|
98
|
+
.map((result) => result.item)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const renderResults = () => {
|
|
102
|
+
const query = input.value.trim()
|
|
103
|
+
if (query.length < 2) {
|
|
104
|
+
closeResults()
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const matches = search(query)
|
|
109
|
+
results.innerHTML = ''
|
|
110
|
+
|
|
111
|
+
if (matches.length === 0) {
|
|
112
|
+
const empty = document.createElement('div')
|
|
113
|
+
empty.className = 'kward-guide-search-empty'
|
|
114
|
+
empty.textContent = 'No guide results'
|
|
115
|
+
results.appendChild(empty)
|
|
116
|
+
results.classList.add('open')
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
matches.forEach((item) => {
|
|
121
|
+
const link = document.createElement('a')
|
|
122
|
+
link.href = item.url
|
|
123
|
+
link.setAttribute('role', 'option')
|
|
124
|
+
|
|
125
|
+
const title = document.createElement('strong')
|
|
126
|
+
title.textContent = item.title
|
|
127
|
+
link.appendChild(title)
|
|
128
|
+
|
|
129
|
+
const summary = document.createElement('span')
|
|
130
|
+
summary.textContent = excerpt(item.text || '', query)
|
|
131
|
+
link.appendChild(summary)
|
|
132
|
+
|
|
133
|
+
results.appendChild(link)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
results.classList.add('open')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
input.addEventListener('input', renderResults, { signal })
|
|
140
|
+
|
|
141
|
+
input.addEventListener('keydown', (event) => {
|
|
142
|
+
if (event.key === 'Escape') {
|
|
143
|
+
input.value = ''
|
|
144
|
+
closeResults()
|
|
145
|
+
input.blur()
|
|
146
|
+
}
|
|
147
|
+
}, { signal })
|
|
148
|
+
|
|
149
|
+
form.addEventListener('submit', (event) => {
|
|
150
|
+
event.preventDefault()
|
|
151
|
+
const firstResult = results.querySelector('a')
|
|
152
|
+
if (firstResult) visitPage(firstResult.href)
|
|
153
|
+
}, { signal })
|
|
154
|
+
|
|
155
|
+
document.addEventListener('click', (event) => {
|
|
156
|
+
if (!form.contains(event.target)) closeResults()
|
|
157
|
+
}, { signal })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const setupNavigation = (signal) => {
|
|
161
|
+
const toggle = document.querySelector('.kward-nav-toggle')
|
|
162
|
+
|
|
163
|
+
if (toggle) {
|
|
164
|
+
toggle.addEventListener('click', () => {
|
|
165
|
+
const isOpen = document.body.classList.toggle('kward-nav-open')
|
|
166
|
+
toggle.setAttribute('aria-expanded', String(isOpen))
|
|
167
|
+
}, { signal })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
document.querySelectorAll('.kward-nav-menu-button').forEach((button) => {
|
|
171
|
+
button.addEventListener('click', (event) => {
|
|
172
|
+
event.stopPropagation()
|
|
173
|
+
button.parentElement.classList.toggle('open')
|
|
174
|
+
}, { signal })
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
document.addEventListener('click', (event) => {
|
|
178
|
+
document.querySelectorAll('.kward-nav-menu.open').forEach((menu) => {
|
|
179
|
+
if (!menu.contains(event.target)) menu.classList.remove('open')
|
|
180
|
+
})
|
|
181
|
+
}, { signal })
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const rewriteGuideLinks = () => {
|
|
185
|
+
document.querySelectorAll('#filecontents a[href]').forEach((link) => {
|
|
186
|
+
const target = guideLinks[link.getAttribute('href')]
|
|
187
|
+
if (target) link.setAttribute('href', target)
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const setupCodeCopy = () => {
|
|
192
|
+
document.querySelectorAll('pre').forEach((block) => {
|
|
193
|
+
if (block.closest('.code-copy-wrapper')) return
|
|
194
|
+
|
|
195
|
+
const code = block.querySelector('code')
|
|
196
|
+
if (!code || !navigator.clipboard) return
|
|
197
|
+
|
|
198
|
+
const wrapper = document.createElement('div')
|
|
199
|
+
wrapper.className = 'code-copy-wrapper'
|
|
200
|
+
block.parentNode.insertBefore(wrapper, block)
|
|
201
|
+
wrapper.appendChild(block)
|
|
202
|
+
|
|
203
|
+
const button = document.createElement('button')
|
|
204
|
+
button.className = 'copy-code-button'
|
|
205
|
+
button.type = 'button'
|
|
206
|
+
button.textContent = 'Copy'
|
|
207
|
+
|
|
208
|
+
button.addEventListener('click', async () => {
|
|
209
|
+
await navigator.clipboard.writeText(code.textContent || '')
|
|
210
|
+
button.textContent = 'Copied'
|
|
211
|
+
window.setTimeout(() => {
|
|
212
|
+
button.textContent = 'Copy'
|
|
213
|
+
}, 1200)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
wrapper.appendChild(button)
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const initializePage = () => {
|
|
221
|
+
if (pageController) pageController.abort()
|
|
222
|
+
pageController = new AbortController()
|
|
223
|
+
|
|
224
|
+
resetScrollPosition()
|
|
225
|
+
setupGuideSearch(pageController.signal)
|
|
226
|
+
setupNavigation(pageController.signal)
|
|
227
|
+
rewriteGuideLinks()
|
|
228
|
+
setupCodeCopy()
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const samePageUrl = (url) => {
|
|
232
|
+
return url.origin === window.location.origin &&
|
|
233
|
+
url.pathname === window.location.pathname &&
|
|
234
|
+
url.search === window.location.search
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const navigableUrl = (url) => {
|
|
238
|
+
if (url.hash) return false
|
|
239
|
+
if (url.origin !== window.location.origin) return false
|
|
240
|
+
if (!url.pathname.endsWith('.html') && !url.pathname.endsWith('/')) return false
|
|
241
|
+
return !samePageUrl(url)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const replacePage = (html, url) => {
|
|
245
|
+
const nextDocument = new DOMParser().parseFromString(html, 'text/html')
|
|
246
|
+
const nextBody = nextDocument.body
|
|
247
|
+
if (!nextBody) throw new Error('Missing response body')
|
|
248
|
+
|
|
249
|
+
document.title = nextDocument.title
|
|
250
|
+
document.body.className = nextBody.className
|
|
251
|
+
document.body.innerHTML = nextBody.innerHTML
|
|
252
|
+
window.history.pushState({}, '', url.href)
|
|
253
|
+
initializePage()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const visitPage = async (href) => {
|
|
257
|
+
const url = new URL(href, window.location.href)
|
|
258
|
+
|
|
259
|
+
if (!navigableUrl(url)) {
|
|
260
|
+
window.location.href = url.href
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
document.documentElement.classList.add('kward-page-loading')
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const response = await fetch(url.href)
|
|
268
|
+
if (!response.ok) throw new Error(`Failed to load ${url.href}`)
|
|
269
|
+
|
|
270
|
+
const html = await response.text()
|
|
271
|
+
replacePage(html, url)
|
|
272
|
+
} catch (_error) {
|
|
273
|
+
window.location.href = url.href
|
|
274
|
+
} finally {
|
|
275
|
+
document.documentElement.classList.remove('kward-page-loading')
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
document.addEventListener('click', (event) => {
|
|
280
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
|
|
281
|
+
|
|
282
|
+
const link = event.target.closest('a[href]')
|
|
283
|
+
if (!link || link.target || link.hasAttribute('download')) return
|
|
284
|
+
|
|
285
|
+
const url = new URL(link.href, window.location.href)
|
|
286
|
+
if (!navigableUrl(url)) return
|
|
287
|
+
|
|
288
|
+
event.preventDefault()
|
|
289
|
+
visitPage(url.href)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
window.addEventListener('popstate', () => {
|
|
293
|
+
window.location.reload()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
document.addEventListener('DOMContentLoaded', initializePage)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<div id="menu">
|
|
2
|
+
<% if @contents || @file %>
|
|
3
|
+
<% if object != '_index.html' %><a href="<%= guide_page? ? url_for(guide_overview) : url_for_index %>">Index</a> » <% end %>
|
|
4
|
+
<span class="title"><%= @breadcrumb_title %></span>
|
|
5
|
+
<% elsif object.is_a?(CodeObjects::Base) %>
|
|
6
|
+
<a href="<%= url_for_index %>"><% if object.root? || object.type == :method %>Index<% else %>Index (<%= object.name.to_s[0,1] %>)<% end %></a> »
|
|
7
|
+
<%= @breadcrumb.map {|obj| "<span class='title'>" + linkify(obj, obj.name) + "</span>" }.join(" » ") %>
|
|
8
|
+
<%= @breadcrumb.size > 0 ? " » " : "" %>
|
|
9
|
+
<span class="title"><%= object.root? ? "Top Level Namespace" : object.name(true) %></span>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<%= erb(:headers) %>
|
|
5
|
+
</head>
|
|
6
|
+
<body class="kward-docs <%= home_page? ? 'kward-home-body' : 'kward-content-body' %>">
|
|
7
|
+
<div id="main_progress" aria-hidden="true"></div>
|
|
8
|
+
|
|
9
|
+
<header class="kward-topbar">
|
|
10
|
+
<a class="kward-brand" href="<%= url_for('index.html') %>">
|
|
11
|
+
<img class="kward-brand-logo" src="<%= url_for('images/kward_logo.png') %>" alt="" aria-hidden="true">
|
|
12
|
+
<span>
|
|
13
|
+
<strong>Kward</strong>
|
|
14
|
+
</span>
|
|
15
|
+
</a>
|
|
16
|
+
<button class="kward-nav-toggle" type="button" aria-expanded="false" aria-controls="kward-primary-nav" onclick="document.body.classList.toggle('kward-nav-open'); this.setAttribute('aria-expanded', document.body.classList.contains('kward-nav-open'))">
|
|
17
|
+
Menu
|
|
18
|
+
</button>
|
|
19
|
+
<nav id="kward-primary-nav" class="kward-topnav" aria-label="Primary navigation">
|
|
20
|
+
<a href="<%= url_for('index.html') %>" class="<%= 'active' if home_page? %>">Home</a>
|
|
21
|
+
<div class="kward-nav-menu <%= 'active' if guide_page? %>">
|
|
22
|
+
<a class="kward-nav-menu-link" href="<%= url_for('file.README.html') %>">User Guides</a>
|
|
23
|
+
<button class="kward-nav-menu-button" type="button" aria-label="Open user guides menu">⌄</button>
|
|
24
|
+
<div class="kward-nav-dropdown">
|
|
25
|
+
<% guide_groups.each do |title, items| %>
|
|
26
|
+
<section>
|
|
27
|
+
<h2><%= h title %></h2>
|
|
28
|
+
<% items.each do |label, link| %>
|
|
29
|
+
<a href="<%= url_for(link) %>"><%= h label %></a>
|
|
30
|
+
<% end %>
|
|
31
|
+
</section>
|
|
32
|
+
<% end %>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<a href="<%= url_for('Kward.html') %>" class="<%= 'active' if api_page? %>">API Docs</a>
|
|
36
|
+
</nav>
|
|
37
|
+
<form class="kward-guide-search" role="search">
|
|
38
|
+
<label class="kward-sr-only" for="kward-guide-search-input">Search user guides</label>
|
|
39
|
+
<input id="kward-guide-search-input" type="search" placeholder="Search guides" autocomplete="off" aria-label="Search user guides" aria-controls="kward-guide-search-results">
|
|
40
|
+
<div id="kward-guide-search-results" class="kward-guide-search-results" role="listbox" aria-label="Search results"></div>
|
|
41
|
+
</form>
|
|
42
|
+
<script type="application/json" id="kward-guide-search-index"><%= guide_search_index_json %></script>
|
|
43
|
+
</header>
|
|
44
|
+
|
|
45
|
+
<% if home_page? %>
|
|
46
|
+
<main id="main" class="kward-home">
|
|
47
|
+
<section class="kward-hero">
|
|
48
|
+
<div class="kward-hero-copy">
|
|
49
|
+
<p class="kward-eyebrow">⌘ Ruby CLI Coding Agent</p>
|
|
50
|
+
<h1>Your terminal.<br><span>Your agent.</span></h1>
|
|
51
|
+
<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>
|
|
52
|
+
<div class="kward-actions">
|
|
53
|
+
<a class="kward-primary-button" href="<%= url_for('file.README.html') %>">Get Started ›</a>
|
|
54
|
+
<a class="kward-secondary-button" href="https://github.com/kaiwood/kward" target="_blank" rel="noopener noreferrer">View on GitHub</a>
|
|
55
|
+
</div>
|
|
56
|
+
<p class="kward-supports">Supports <span>OpenAI</span>, <span>Anthropic</span>, <span>OpenRouter</span> & more.</p>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="kward-hero-art" aria-hidden="true">
|
|
59
|
+
<img src="<%= url_for('images/kward_logo.png') %>" alt="">
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
|
|
63
|
+
<section id="features" class="kward-feature-strip" aria-label="Feature summary">
|
|
64
|
+
<article><strong>☏</strong><h2>Chat in your terminal</h2><p>Multi-turn coding conversations.</p></article>
|
|
65
|
+
<article><strong>▤</strong><h2>Read, write, edit files</h2><p>With read-before-write guardrails.</p></article>
|
|
66
|
+
<article><strong>›_</strong><h2>Run shell commands</h2><p>Execute local commands safely.</p></article>
|
|
67
|
+
<article><strong>◎</strong><h2>Search the web</h2><p>Get live answers and inspect repos.</p></article>
|
|
68
|
+
<article><strong>✣</strong><h2>Extend with plugins</h2><p>Trusted Ruby plugins for workflows.</p></article>
|
|
69
|
+
<article><strong>▰</strong><h2>Sessions & memory</h2><p>Save, resume, clone, compact, export.</p></article>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section class="kward-install-card">
|
|
73
|
+
<div class="kward-install-copy">
|
|
74
|
+
<h2>⇩ Install</h2>
|
|
75
|
+
<p>Install Kward from RubyGems and get started in seconds.</p>
|
|
76
|
+
<pre><code>gem install kward</code></pre>
|
|
77
|
+
<p>Then initialize the starter pack, if you want the defaults.</p>
|
|
78
|
+
<pre><code>kward init</code></pre>
|
|
79
|
+
<a href="<%= url_for('file.getting-started.html') %>">View full installation docs →</a>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="kward-terminal-card">
|
|
82
|
+
<div class="kward-tabs"><span>Quick Start</span><span>Run from Source</span></div>
|
|
83
|
+
<pre><code># Start an interactive chat
|
|
84
|
+
kward
|
|
85
|
+
|
|
86
|
+
# Show available commands and examples
|
|
87
|
+
kward help
|
|
88
|
+
|
|
89
|
+
# Sign in or save provider credentials
|
|
90
|
+
kward login
|
|
91
|
+
|
|
92
|
+
# Run one prompt and exit
|
|
93
|
+
kward "Explain this project"
|
|
94
|
+
|
|
95
|
+
# Use a specific working directory
|
|
96
|
+
kward --working-directory ~/code/project "Explain this project"</code></pre>
|
|
97
|
+
</div>
|
|
98
|
+
</section>
|
|
99
|
+
|
|
100
|
+
<section class="kward-capabilities">
|
|
101
|
+
<h2>⌘ What Kward can do</h2>
|
|
102
|
+
<ul>
|
|
103
|
+
<li>Keep a multi-turn coding conversation in your terminal.</li>
|
|
104
|
+
<li>Read, write, and edit workspace files with read-before-write guardrails.</li>
|
|
105
|
+
<li>Run local shell commands from the workspace.</li>
|
|
106
|
+
<li>Search the live web and inspect cached public GitHub repositories.</li>
|
|
107
|
+
<li>Save, resume, clone, compact, and export sessions.</li>
|
|
108
|
+
<li>Use optional memory, personas, prompt templates, and skills.</li>
|
|
109
|
+
<li>Extend the Agent with trusted Ruby plugins for custom workflows.</li>
|
|
110
|
+
<li>Serve an experimental JSON-RPC backend for UI clients.</li>
|
|
111
|
+
</ul>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
<section class="kward-cta">
|
|
115
|
+
<div>
|
|
116
|
+
<strong>▻</strong>
|
|
117
|
+
<h2>Built for developers. Powered by you.</h2>
|
|
118
|
+
<p>Open source, extensible, and ready for your workflows.</p>
|
|
119
|
+
</div>
|
|
120
|
+
<a class="kward-primary-button" href="https://github.com/kaiwood/kward" target="_blank" rel="noopener noreferrer">View on GitHub</a>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<%= erb(:footer) %>
|
|
124
|
+
</main>
|
|
125
|
+
<% else %>
|
|
126
|
+
<div class="kward-page">
|
|
127
|
+
<main id="main" class="kward-content-page <%= guide_page? ? 'kward-guide-page' : 'kward-api-page' %>">
|
|
128
|
+
<div id="header">
|
|
129
|
+
<%= erb(:breadcrumb) %>
|
|
130
|
+
<%= erb(:search) %>
|
|
131
|
+
<div class="clear"></div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div id="content"><%= yieldall %></div>
|
|
135
|
+
|
|
136
|
+
<%= erb(:footer) %>
|
|
137
|
+
</main>
|
|
138
|
+
</div>
|
|
139
|
+
<% end %>
|
|
140
|
+
</body>
|
|
141
|
+
</html>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
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
|
|
36
|
+
|
|
37
|
+
GUIDE_OVERVIEW = "file.README.html"
|
|
38
|
+
GUIDE_LINKS = ([GUIDE_OVERVIEW] + GUIDE_GROUPS.flat_map { |_title, items| items.map(&:last) }).freeze
|
|
39
|
+
GUIDE_SEARCH_FILES = [
|
|
40
|
+
["Overview", "README.md", GUIDE_OVERVIEW],
|
|
41
|
+
*GUIDE_GROUPS.flat_map do |_title, items|
|
|
42
|
+
items.map do |label, link|
|
|
43
|
+
source = "doc/#{link.delete_prefix("file.").delete_suffix(".html")}.md"
|
|
44
|
+
[label, source, link]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
].freeze
|
|
48
|
+
GUIDE_FILE_LINKS = {
|
|
49
|
+
"doc/getting-started.md" => "file.getting-started.html",
|
|
50
|
+
"doc/usage.md" => "file.usage.html",
|
|
51
|
+
"doc/configuration.md" => "file.configuration.html",
|
|
52
|
+
"doc/authentication.md" => "file.authentication.html",
|
|
53
|
+
"doc/troubleshooting.md" => "file.troubleshooting.html",
|
|
54
|
+
"doc/memory.md" => "file.memory.html",
|
|
55
|
+
"doc/personas.md" => "file.personas.html",
|
|
56
|
+
"doc/extensibility.md" => "file.extensibility.html",
|
|
57
|
+
"doc/plugins.md" => "file.plugins.html",
|
|
58
|
+
"doc/web-search.md" => "file.web-search.html",
|
|
59
|
+
"doc/code-search.md" => "file.code-search.html",
|
|
60
|
+
"doc/rpc.md" => "file.rpc.html",
|
|
61
|
+
"doc/releasing.md" => "file.releasing.html"
|
|
62
|
+
}.freeze
|
|
63
|
+
|
|
64
|
+
def guide_groups
|
|
65
|
+
GUIDE_GROUPS
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def guide_overview
|
|
69
|
+
GUIDE_OVERVIEW
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def guide_search_index_json
|
|
73
|
+
JSON.generate(guide_search_index).gsub("</", "<\\/")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def guide_search_index
|
|
77
|
+
GUIDE_SEARCH_FILES.filter_map do |title, source, link|
|
|
78
|
+
next unless File.file?(source)
|
|
79
|
+
|
|
80
|
+
content = File.read(source)
|
|
81
|
+
{
|
|
82
|
+
title: title,
|
|
83
|
+
path: source,
|
|
84
|
+
url: url_for(link),
|
|
85
|
+
text: content.gsub(/[`*_#>\[\]()]/, " ").gsub(/\s+/, " ").strip
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def current_docs_path
|
|
91
|
+
serializer = options.serializer
|
|
92
|
+
return "index.html" unless serializer
|
|
93
|
+
|
|
94
|
+
File.basename(serializer.serialized_path(object))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def readme_file?
|
|
98
|
+
defined?(@file) && @file&.name == "README"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def guide_file?
|
|
102
|
+
defined?(@file) && GUIDE_FILE_LINKS.key?(@file&.filename.to_s)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def home_page?
|
|
106
|
+
options.index && readme_file?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def guide_page?
|
|
110
|
+
(readme_file? && !options.index) || guide_file? || GUIDE_LINKS.include?(current_docs_path)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def api_page?
|
|
114
|
+
!home_page? && !guide_page?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def diskfile
|
|
118
|
+
@file.attributes[:markup] ||= markup_for_file('', @file.filename)
|
|
119
|
+
data = rewrite_guide_links(htmlify(@file.contents, @file.attributes[:markup]))
|
|
120
|
+
"<div id='filecontents'>" + data + "</div>"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def rewrite_guide_links(html)
|
|
124
|
+
GUIDE_FILE_LINKS.each do |source, target|
|
|
125
|
+
html = html.gsub(%(href="#{source}"), %(href="#{target}"))
|
|
126
|
+
end
|
|
127
|
+
html
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
include KwardDocsNavigation
|
|
132
|
+
|
|
133
|
+
def javascripts
|
|
134
|
+
super + %w(js/kward.js)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def stylesheets
|
|
138
|
+
super + %w(css/kward.css)
|
|
139
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kward
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.69.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kai Wood
|
|
@@ -116,6 +116,7 @@ executables:
|
|
|
116
116
|
extensions: []
|
|
117
117
|
extra_rdoc_files: []
|
|
118
118
|
files:
|
|
119
|
+
- ".github/workflows/pages.yml"
|
|
119
120
|
- ".yardopts"
|
|
120
121
|
- CHANGELOG.md
|
|
121
122
|
- Gemfile
|
|
@@ -129,6 +130,7 @@ files:
|
|
|
129
130
|
- doc/extensibility.md
|
|
130
131
|
- doc/getting-started.md
|
|
131
132
|
- doc/memory.md
|
|
133
|
+
- doc/personas.md
|
|
132
134
|
- doc/plugins.md
|
|
133
135
|
- doc/releasing.md
|
|
134
136
|
- doc/rpc.md
|
|
@@ -161,6 +163,7 @@ files:
|
|
|
161
163
|
- lib/kward/cli/settings.rb
|
|
162
164
|
- lib/kward/cli/slash_commands.rb
|
|
163
165
|
- lib/kward/cli/stats.rb
|
|
166
|
+
- lib/kward/cli/sysprompt.rb
|
|
164
167
|
- lib/kward/cli/tool_summaries.rb
|
|
165
168
|
- lib/kward/cli_transcript_formatter.rb
|
|
166
169
|
- lib/kward/clipboard.rb
|
|
@@ -239,6 +242,8 @@ files:
|
|
|
239
242
|
- lib/kward/tools/base.rb
|
|
240
243
|
- lib/kward/tools/code_search.rb
|
|
241
244
|
- lib/kward/tools/edit_file.rb
|
|
245
|
+
- lib/kward/tools/fetch_content.rb
|
|
246
|
+
- lib/kward/tools/fetch_raw.rb
|
|
242
247
|
- lib/kward/tools/list_directory.rb
|
|
243
248
|
- lib/kward/tools/read_file.rb
|
|
244
249
|
- lib/kward/tools/read_skill.rb
|
|
@@ -246,6 +251,7 @@ files:
|
|
|
246
251
|
- lib/kward/tools/run_shell_command.rb
|
|
247
252
|
- lib/kward/tools/search/code.rb
|
|
248
253
|
- lib/kward/tools/search/web.rb
|
|
254
|
+
- lib/kward/tools/search/web_fetch.rb
|
|
249
255
|
- lib/kward/tools/tool_call.rb
|
|
250
256
|
- lib/kward/tools/web_search.rb
|
|
251
257
|
- lib/kward/tools/write_file.rb
|
|
@@ -253,6 +259,13 @@ files:
|
|
|
253
259
|
- lib/kward/version.rb
|
|
254
260
|
- lib/kward/workspace.rb
|
|
255
261
|
- lib/main.rb
|
|
262
|
+
- templates/default/fulldoc/html/css/kward.css
|
|
263
|
+
- templates/default/fulldoc/html/images/kward_logo.png
|
|
264
|
+
- templates/default/fulldoc/html/js/kward.js
|
|
265
|
+
- templates/default/fulldoc/html/setup.rb
|
|
266
|
+
- templates/default/layout/html/breadcrumb.erb
|
|
267
|
+
- templates/default/layout/html/layout.erb
|
|
268
|
+
- templates/default/layout/html/setup.rb
|
|
256
269
|
homepage: https://github.com/kaiwood/kward
|
|
257
270
|
licenses:
|
|
258
271
|
- MIT
|