superintent 0.0.1

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/bin/superintent.js +2 -0
  4. package/dist/commands/extract.d.ts +2 -0
  5. package/dist/commands/extract.js +66 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +56 -0
  8. package/dist/commands/knowledge.d.ts +2 -0
  9. package/dist/commands/knowledge.js +647 -0
  10. package/dist/commands/search.d.ts +2 -0
  11. package/dist/commands/search.js +153 -0
  12. package/dist/commands/spec.d.ts +2 -0
  13. package/dist/commands/spec.js +283 -0
  14. package/dist/commands/status.d.ts +2 -0
  15. package/dist/commands/status.js +43 -0
  16. package/dist/commands/ticket.d.ts +4 -0
  17. package/dist/commands/ticket.js +942 -0
  18. package/dist/commands/ui.d.ts +2 -0
  19. package/dist/commands/ui.js +954 -0
  20. package/dist/db/client.d.ts +4 -0
  21. package/dist/db/client.js +26 -0
  22. package/dist/db/init-schema.d.ts +2 -0
  23. package/dist/db/init-schema.js +28 -0
  24. package/dist/db/parsers.d.ts +24 -0
  25. package/dist/db/parsers.js +79 -0
  26. package/dist/db/schema.d.ts +7 -0
  27. package/dist/db/schema.js +64 -0
  28. package/dist/db/usage.d.ts +8 -0
  29. package/dist/db/usage.js +24 -0
  30. package/dist/embed/model.d.ts +5 -0
  31. package/dist/embed/model.js +34 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +31 -0
  34. package/dist/types.d.ts +120 -0
  35. package/dist/types.js +1 -0
  36. package/dist/ui/components/index.d.ts +6 -0
  37. package/dist/ui/components/index.js +13 -0
  38. package/dist/ui/components/knowledge.d.ts +33 -0
  39. package/dist/ui/components/knowledge.js +238 -0
  40. package/dist/ui/components/layout.d.ts +1 -0
  41. package/dist/ui/components/layout.js +323 -0
  42. package/dist/ui/components/search.d.ts +15 -0
  43. package/dist/ui/components/search.js +114 -0
  44. package/dist/ui/components/spec.d.ts +11 -0
  45. package/dist/ui/components/spec.js +253 -0
  46. package/dist/ui/components/ticket.d.ts +90 -0
  47. package/dist/ui/components/ticket.js +604 -0
  48. package/dist/ui/components/utils.d.ts +26 -0
  49. package/dist/ui/components/utils.js +34 -0
  50. package/dist/ui/styles.css +2 -0
  51. package/dist/utils/cli.d.ts +21 -0
  52. package/dist/utils/cli.js +31 -0
  53. package/dist/utils/config.d.ts +12 -0
  54. package/dist/utils/config.js +116 -0
  55. package/dist/utils/id.d.ts +6 -0
  56. package/dist/utils/id.js +13 -0
  57. package/dist/utils/io.d.ts +8 -0
  58. package/dist/utils/io.js +15 -0
  59. package/package.json +60 -0
@@ -0,0 +1,114 @@
1
+ // Search-related UI components
2
+ import { escapeHtml } from './utils.js';
3
+ // Helper to render search view
4
+ export function renderSearchView() {
5
+ return `
6
+ <div class="max-w-3xl mx-auto">
7
+ <h1 class="text-2xl font-bold text-gray-800 mb-6">Semantic Search</h1>
8
+
9
+ <div class="relative">
10
+ <input type="text"
11
+ id="search-input"
12
+ name="query"
13
+ placeholder="Search knowledge base..."
14
+ autocomplete="off"
15
+ class="w-full px-4 py-3 text-lg border rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none"
16
+ hx-get="/partials/search-results"
17
+ hx-trigger="input changed delay:300ms, keyup[key=='Enter']"
18
+ hx-target="#search-results"
19
+ hx-include="[name='category'],[name='limit']"
20
+ hx-indicator="#search-spinner">
21
+ <div id="search-spinner" class="htmx-indicator absolute right-3 top-3.5">
22
+ <svg class="animate-spin h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24">
23
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
24
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
25
+ </svg>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="flex gap-3 mt-4">
30
+ <select name="category" class="border rounded-lg px-3 py-2 text-sm bg-white"
31
+ hx-get="/partials/search-results"
32
+ hx-trigger="change"
33
+ hx-target="#search-results"
34
+ hx-include="[name='query'],[name='limit']">
35
+ <option value="">All Categories</option>
36
+ <option value="pattern">Pattern</option>
37
+ <option value="truth">Truth</option>
38
+ <option value="principle">Principle</option>
39
+ <option value="architecture">Architecture</option>
40
+ <option value="gotcha">Gotcha</option>
41
+ </select>
42
+
43
+ <select name="limit" class="border rounded-lg px-3 py-2 text-sm bg-white"
44
+ hx-get="/partials/search-results"
45
+ hx-trigger="change"
46
+ hx-target="#search-results"
47
+ hx-include="[name='query'],[name='category']">
48
+ <option value="5">5 results</option>
49
+ <option value="10">10 results</option>
50
+ <option value="20">20 results</option>
51
+ </select>
52
+ </div>
53
+
54
+ <div id="search-results" class="mt-6">
55
+ <p class="text-gray-500 text-center py-8">Enter a search query to find relevant knowledge</p>
56
+ </div>
57
+ </div>
58
+ `;
59
+ }
60
+ // Helper to render search results
61
+ export function renderSearchResults(results) {
62
+ if (results.length === 0) {
63
+ return '<p class="text-gray-500 text-center py-8">No results found</p>';
64
+ }
65
+ const categoryColors = {
66
+ pattern: 'purple',
67
+ truth: 'green',
68
+ principle: 'orange',
69
+ architecture: 'blue',
70
+ gotcha: 'red',
71
+ };
72
+ return `
73
+ <div class="space-y-4">
74
+ ${results.map(r => {
75
+ const color = categoryColors[r.category || ''] || 'gray';
76
+ const scorePercent = Math.round(r.score * 100);
77
+ const inactiveClass = !r.active ? 'opacity-60 border-dashed' : '';
78
+ return `
79
+ <div class="bg-white rounded-lg shadow-card p-4 hover:shadow-card-hover transition-shadow cursor-pointer ${inactiveClass}"
80
+ hx-get="/partials/knowledge-modal/${encodeURIComponent(r.id)}"
81
+ hx-target="#modal-content"
82
+ hx-trigger="click"
83
+ onclick="showModal()">
84
+ <div class="flex items-start justify-between gap-4">
85
+ <div class="flex-1">
86
+ <div class="flex items-center gap-2">
87
+ <h3 class="font-semibold text-gray-800">${escapeHtml(r.title)}</h3>
88
+ ${!r.active ? '<span class="px-2 py-0.5 text-xs rounded bg-gray-200 text-gray-600">Inactive</span>' : ''}
89
+ </div>
90
+ <p class="text-sm text-gray-600 mt-1 line-clamp-3">${escapeHtml(r.content.slice(0, 300))}${r.content.length > 300 ? '...' : ''}</p>
91
+ </div>
92
+ <div class="text-right flex-shrink-0">
93
+ <div class="text-lg font-bold text-${color}-600">${scorePercent}%</div>
94
+ <div class="text-xs text-gray-400">match</div>
95
+ </div>
96
+ </div>
97
+ <div class="flex flex-wrap gap-4 mt-3 text-xs text-gray-500">
98
+ <span><span class="text-gray-400">Namespace:</span> ${escapeHtml(r.namespace)}</span>
99
+ <span><span class="text-gray-400">Source:</span> ${r.source || 'manual'}${r.source === 'ticket' && r.origin_ticket_type ? ` (${r.origin_ticket_type})` : ''}</span>
100
+ ${r.category ? `<span><span class="text-gray-400">Category:</span> <span class="text-${color}-600 font-medium">${r.category}</span></span>` : ''}
101
+ <span><span class="text-gray-400">Scope:</span> ${r.decision_scope}</span>
102
+ ${(r.tags || []).length > 0 ? `<span><span class="text-gray-400">Tags:</span> ${(r.tags || []).slice(0, 3).map(t => escapeHtml(t)).join(', ')}</span>` : ''}
103
+ </div>
104
+ <div class="mt-2">
105
+ <div class="w-full bg-gray-100 rounded-full h-1">
106
+ <div class="bg-${color}-500 h-1 rounded-full" style="width: ${scorePercent}%"></div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ `;
111
+ }).join('')}
112
+ </div>
113
+ `;
114
+ }
@@ -0,0 +1,11 @@
1
+ import type { Spec } from '../../types.js';
2
+ export declare function renderSpecView(): string;
3
+ export declare function renderSpecList(specs: Spec[], ticketCounts?: Record<string, number>): string;
4
+ export declare function renderSpecCard(spec: Spec, ticketCount?: number): string;
5
+ export declare function renderSpecModal(spec: Spec, relatedTickets?: {
6
+ id: string;
7
+ title?: string;
8
+ status: string;
9
+ }[]): string;
10
+ export declare function renderNewSpecModal(): string;
11
+ export declare function renderEditSpecModal(spec: Spec): string;
@@ -0,0 +1,253 @@
1
+ // Spec-related UI components
2
+ import { escapeHtml, renderMarkdownEditor } from './utils.js';
3
+ // Helper to render spec view
4
+ export function renderSpecView() {
5
+ return `
6
+ <div>
7
+ <div class="flex items-center justify-between mb-4">
8
+ <h1 class="text-2xl font-bold text-gray-800">Specs</h1>
9
+ <button type="button"
10
+ class="text-xs px-2 py-1 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded transition-colors"
11
+ hx-get="/partials/new-spec-modal"
12
+ hx-target="#modal-content"
13
+ hx-trigger="click"
14
+ onclick="showModal()">
15
+ + Add
16
+ </button>
17
+ </div>
18
+ <div id="spec-list" hx-get="/partials/spec-list" hx-trigger="load, poll-refresh" hx-swap="innerHTML">
19
+ </div>
20
+ </div>
21
+ `;
22
+ }
23
+ // Helper to render spec list
24
+ export function renderSpecList(specs, ticketCounts) {
25
+ if (specs.length === 0) {
26
+ return '<p class="text-gray-500 text-center py-8">No specs found.</p>';
27
+ }
28
+ return `
29
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
30
+ ${specs.map(s => renderSpecCard(s, ticketCounts?.[s.id] || 0)).join('')}
31
+ </div>
32
+ `;
33
+ }
34
+ // Helper to render spec card
35
+ export function renderSpecCard(spec, ticketCount = 0) {
36
+ return `
37
+ <div class="bg-white rounded-lg shadow-card hover:shadow-card-hover transition-shadow cursor-pointer overflow-hidden flex flex-col group"
38
+ hx-get="/partials/spec-modal/${encodeURIComponent(spec.id)}"
39
+ hx-target="#modal-content"
40
+ hx-trigger="click"
41
+ onclick="showModal()">
42
+ <div class="p-4 flex flex-col flex-1">
43
+ <div class="flex items-start justify-between mb-1">
44
+ <div class="text-xs font-mono text-gray-400">${escapeHtml(spec.id)}</div>
45
+ <button class="opacity-0 group-hover:opacity-100 p-1 -mt-1 -mr-1 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded transition-all"
46
+ title="Edit spec"
47
+ hx-get="/partials/edit-spec-modal/${encodeURIComponent(spec.id)}"
48
+ hx-target="#modal-content"
49
+ hx-trigger="click"
50
+ onclick="event.stopPropagation(); showModal()">
51
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
52
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
53
+ </svg>
54
+ </button>
55
+ </div>
56
+ <div class="text-sm font-medium text-gray-800 line-clamp-2">${escapeHtml(spec.title)}</div>
57
+ <p class="text-xs text-gray-500 mt-2 line-clamp-3 flex-1">${escapeHtml(spec.content.slice(0, 150))}${spec.content.length > 150 ? '...' : ''}</p>
58
+ ${ticketCount > 0 ? `
59
+ <div class="mt-2 text-xs text-gray-400 inline-flex items-center gap-1">
60
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
61
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
62
+ </svg>
63
+ ${ticketCount} ticket${ticketCount !== 1 ? 's' : ''}
64
+ </div>
65
+ ` : ''}
66
+ </div>
67
+ </div>
68
+ `;
69
+ }
70
+ // Helper to render spec modal
71
+ export function renderSpecModal(spec, relatedTickets) {
72
+ return `
73
+ <div class="p-6">
74
+ <div class="flex items-start justify-between mb-4">
75
+ <div>
76
+ <div class="flex items-center gap-2 mb-1">
77
+ <span class="text-xs font-mono text-gray-400">${escapeHtml(spec.id)}</span>
78
+ <button type="button"
79
+ class="p-0.5 text-gray-400 hover:text-blue-600 rounded transition-colors"
80
+ title="Copy spec ID"
81
+ onclick="navigator.clipboard.writeText('${escapeHtml(spec.id)}').then(() => { const svg = this.querySelector('svg'); const originalPath = svg.innerHTML; svg.innerHTML = '<path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M5 13l4 4L19 7&quot;></path>'; this.classList.add('text-green-600'); setTimeout(() => { svg.innerHTML = originalPath; this.classList.remove('text-green-600'); }, 1500); })">
82
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
83
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
84
+ </svg>
85
+ </button>
86
+ </div>
87
+ <h2 class="text-xl font-bold text-gray-800">${escapeHtml(spec.title)}</h2>
88
+ </div>
89
+ <button onclick="hideModal()" class="text-gray-400 hover:text-gray-600 p-1">
90
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
91
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
92
+ </svg>
93
+ </button>
94
+ </div>
95
+
96
+ <!-- Content -->
97
+ <div class="mb-4">
98
+ <h3 class="text-sm font-semibold text-gray-700 mb-2">Content</h3>
99
+ <div class="text-sm text-gray-700 bg-gray-50 rounded-lg p-4 leading-relaxed markdown-content" data-markdown>${escapeHtml(spec.content)}</div>
100
+ </div>
101
+
102
+ ${relatedTickets?.length ? `
103
+ <!-- Derived Tickets -->
104
+ <div class="mb-4">
105
+ <h3 class="text-sm font-semibold text-gray-700 mb-2">Derived Tickets</h3>
106
+ <div class="flex flex-wrap gap-2">
107
+ ${relatedTickets.map(t => {
108
+ const statusColors = {
109
+ 'Backlog': { bg: 'bg-gray-50', text: 'text-gray-700' },
110
+ 'In Progress': { bg: 'bg-yellow-50', text: 'text-yellow-700' },
111
+ 'In Review': { bg: 'bg-blue-50', text: 'text-blue-700' },
112
+ 'Done': { bg: 'bg-green-50', text: 'text-green-700' },
113
+ 'Blocked': { bg: 'bg-red-50', text: 'text-red-700' },
114
+ 'Paused': { bg: 'bg-orange-50', text: 'text-orange-700' },
115
+ 'Abandoned': { bg: 'bg-gray-100', text: 'text-gray-500' },
116
+ 'Superseded': { bg: 'bg-purple-50', text: 'text-purple-700' },
117
+ };
118
+ const style = statusColors[t.status] || { bg: 'bg-gray-50', text: 'text-gray-700' };
119
+ return `
120
+ <span class="inline-flex items-center px-2 py-1 text-xs rounded-lg ${style.bg} ${style.text} hover:opacity-80 cursor-pointer font-mono font-medium"
121
+ hx-get="/partials/ticket-modal/${encodeURIComponent(t.id)}"
122
+ hx-target="#modal-content"
123
+ hx-trigger="click">
124
+ <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
125
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
126
+ </svg>
127
+ ${escapeHtml(t.id)}
128
+ </span>
129
+ `;
130
+ }).join('')}
131
+ </div>
132
+ </div>
133
+ ` : ''}
134
+
135
+ <!-- Actions & Metadata Footer -->
136
+ <div class="mt-6 pt-4 border-t flex items-center justify-between">
137
+ <div class="text-xs text-gray-400">
138
+ <span>Created: ${spec.created_at || 'N/A'}</span>
139
+ <span class="ml-4">Updated: ${spec.updated_at || 'N/A'}</span>
140
+ </div>
141
+ <div class="flex gap-2">
142
+ <button type="button"
143
+ class="px-3 py-1.5 text-xs font-medium text-blue-600 bg-blue-50 rounded hover:bg-blue-100 transition-colors"
144
+ hx-get="/partials/edit-spec-modal/${encodeURIComponent(spec.id)}"
145
+ hx-target="#modal-content"
146
+ hx-trigger="click">
147
+ Edit
148
+ </button>
149
+ <button type="button"
150
+ class="px-3 py-1.5 text-xs font-medium text-red-600 bg-red-50 rounded hover:bg-red-100 transition-colors"
151
+ hx-delete="/api/specs/${encodeURIComponent(spec.id)}"
152
+ hx-target="#spec-list"
153
+ hx-swap="innerHTML"
154
+ hx-confirm="Delete spec ${escapeHtml(spec.id)}?"
155
+ hx-on::after-request="hideModal()">
156
+ Delete
157
+ </button>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ `;
162
+ }
163
+ // Helper to render new spec modal (manual spec creation form)
164
+ export function renderNewSpecModal() {
165
+ return `
166
+ <div class="p-6">
167
+ <div class="flex items-center justify-between mb-6">
168
+ <h2 class="text-xl font-bold text-gray-800">New Spec</h2>
169
+ <button onclick="hideModal()" class="text-gray-400 hover:text-gray-600 p-1">
170
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
171
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
172
+ </svg>
173
+ </button>
174
+ </div>
175
+
176
+ <form hx-post="/api/specs/quick"
177
+ hx-target="#spec-list"
178
+ hx-on::after-request="hideModal()">
179
+ <div class="space-y-4">
180
+ <div>
181
+ <label class="block text-sm font-medium text-gray-700 mb-1">Title <span class="text-red-500">*</span></label>
182
+ <input type="text" name="title" required
183
+ placeholder="Feature or spec name"
184
+ class="w-full px-3 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none">
185
+ </div>
186
+
187
+ <div>
188
+ <label class="block text-sm font-medium text-gray-700 mb-1">Content <span class="text-red-500">*</span></label>
189
+ ${renderMarkdownEditor({ name: 'content', id: 'spec-content-editor', placeholder: 'Describe the feature spec, goals, requirements, and any relevant details. Supports **markdown**.', rows: 8, required: true })}
190
+ </div>
191
+ </div>
192
+
193
+ <div class="flex justify-end gap-3 mt-6 pt-4 border-t">
194
+ <button type="button" onclick="hideModal()"
195
+ class="px-3 py-1.5 text-xs font-medium text-gray-700 bg-gray-100 rounded hover:bg-gray-200 transition-colors">
196
+ Cancel
197
+ </button>
198
+ <button type="submit"
199
+ class="px-3 py-1.5 text-xs font-medium text-white bg-blue-500 rounded hover:bg-blue-600 transition-colors">
200
+ Create Spec
201
+ </button>
202
+ </div>
203
+ </form>
204
+ </div>
205
+ `;
206
+ }
207
+ // Helper to render edit spec modal
208
+ export function renderEditSpecModal(spec) {
209
+ return `
210
+ <div class="p-6">
211
+ <div class="flex items-center justify-between mb-6">
212
+ <div>
213
+ <h2 class="text-xl font-bold text-gray-800">Edit Spec</h2>
214
+ <span class="text-xs font-mono text-gray-400">${escapeHtml(spec.id)}</span>
215
+ </div>
216
+ <button onclick="hideModal()" class="text-gray-400 hover:text-gray-600 p-1">
217
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
218
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
219
+ </svg>
220
+ </button>
221
+ </div>
222
+
223
+ <form hx-patch="/api/specs/${encodeURIComponent(spec.id)}"
224
+ hx-target="#modal-content">
225
+ <div class="space-y-4">
226
+ <div>
227
+ <label class="block text-sm font-medium text-gray-700 mb-1">Title <span class="text-red-500">*</span></label>
228
+ <input type="text" name="title" required
229
+ value="${escapeHtml(spec.title)}"
230
+ placeholder="Feature or spec name"
231
+ class="w-full px-3 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none">
232
+ </div>
233
+
234
+ <div>
235
+ <label class="block text-sm font-medium text-gray-700 mb-1">Content <span class="text-red-500">*</span></label>
236
+ ${renderMarkdownEditor({ name: 'content', id: 'spec-edit-content-editor', placeholder: 'Describe the feature spec, goals, requirements, and any relevant details. Supports **markdown**.', rows: 12, required: true, value: escapeHtml(spec.content) })}
237
+ </div>
238
+ </div>
239
+
240
+ <div class="flex justify-end gap-3 mt-6 pt-4 border-t">
241
+ <button type="button" onclick="hideModal()"
242
+ class="px-3 py-1.5 text-xs font-medium text-gray-700 bg-gray-100 rounded hover:bg-gray-200 transition-colors">
243
+ Cancel
244
+ </button>
245
+ <button type="submit"
246
+ class="px-3 py-1.5 text-xs font-medium text-white bg-blue-500 rounded hover:bg-blue-600 transition-colors">
247
+ Save Changes
248
+ </button>
249
+ </div>
250
+ </form>
251
+ </div>
252
+ `;
253
+ }
@@ -0,0 +1,90 @@
1
+ import { ColumnData } from './utils.js';
2
+ export declare function renderTicketCard(ticket: {
3
+ id: string;
4
+ type?: string;
5
+ title?: string;
6
+ intent: string;
7
+ change_class?: string;
8
+ change_class_reason?: string;
9
+ tasks?: {
10
+ text: string;
11
+ done: boolean;
12
+ }[];
13
+ }, options?: {
14
+ isBacklog?: boolean;
15
+ }): string;
16
+ export declare function renderKanbanView(): string;
17
+ export declare function renderKanbanColumns(columns: ColumnData[]): string;
18
+ export declare function renderColumnMore(tickets: {
19
+ id: string;
20
+ type?: string;
21
+ title?: string;
22
+ intent: string;
23
+ change_class?: string;
24
+ change_class_reason?: string;
25
+ tasks?: {
26
+ text: string;
27
+ done: boolean;
28
+ }[];
29
+ }[], status: string, nextOffset: number, hasMore: boolean): string;
30
+ export declare function renderTicketModal(ticket: {
31
+ id: string;
32
+ type?: string;
33
+ title?: string;
34
+ status: string;
35
+ intent: string;
36
+ context?: string;
37
+ constraints_use?: string[];
38
+ constraints_avoid?: string[];
39
+ assumptions?: string[];
40
+ tasks?: {
41
+ text: string;
42
+ done: boolean;
43
+ }[];
44
+ definition_of_done?: {
45
+ text: string;
46
+ done: boolean;
47
+ }[];
48
+ change_class?: string;
49
+ change_class_reason?: string;
50
+ origin_spec_id?: string;
51
+ plan?: {
52
+ files: string[];
53
+ taskSteps: {
54
+ task: string;
55
+ steps: string[];
56
+ }[];
57
+ dodVerification: {
58
+ dod: string;
59
+ verify: string;
60
+ }[];
61
+ decisions: {
62
+ choice: string;
63
+ reason: string;
64
+ }[];
65
+ tradeOffs: {
66
+ considered: string;
67
+ rejected: string;
68
+ }[];
69
+ rollback?: {
70
+ steps: string[];
71
+ reversibility: 'full' | 'partial' | 'none';
72
+ };
73
+ irreversibleActions: string[];
74
+ edgeCases: string[];
75
+ };
76
+ derived_knowledge?: string[];
77
+ comments?: {
78
+ text: string;
79
+ timestamp: string;
80
+ }[];
81
+ created_at?: string;
82
+ updated_at?: string;
83
+ }): string;
84
+ export declare function renderNewTicketModal(): string;
85
+ export declare function renderEditTicketModal(ticket: {
86
+ id: string;
87
+ type?: string;
88
+ title?: string;
89
+ intent: string;
90
+ }): string;