@askdkc/kiokuko 0.1.6 → 0.1.9

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 (137) hide show
  1. package/README.ja.md +33 -11
  2. package/README.ko.md +16 -9
  3. package/README.md +97 -21
  4. package/README.zh-CN.md +16 -9
  5. package/dist/agent-file/render.d.ts +1 -1
  6. package/dist/agent-file/render.d.ts.map +1 -1
  7. package/dist/agent-file/render.js +8 -7
  8. package/dist/agent-file/render.js.map +1 -1
  9. package/dist/akinator/agent-task.d.ts +14 -1
  10. package/dist/akinator/agent-task.d.ts.map +1 -1
  11. package/dist/akinator/agent-task.js +86 -16
  12. package/dist/akinator/agent-task.js.map +1 -1
  13. package/dist/akinator/capabilities.d.ts.map +1 -1
  14. package/dist/akinator/capabilities.js +22 -8
  15. package/dist/akinator/capabilities.js.map +1 -1
  16. package/dist/akinator/domain.d.ts +1 -1
  17. package/dist/akinator/domain.js +1 -1
  18. package/dist/akinator/knowledge-path.d.ts +33 -0
  19. package/dist/akinator/knowledge-path.d.ts.map +1 -0
  20. package/dist/akinator/knowledge-path.js +104 -0
  21. package/dist/akinator/knowledge-path.js.map +1 -0
  22. package/dist/akinator/reasoning.d.ts +8 -0
  23. package/dist/akinator/reasoning.d.ts.map +1 -0
  24. package/dist/akinator/reasoning.js +118 -0
  25. package/dist/akinator/reasoning.js.map +1 -0
  26. package/dist/akinator/types.d.ts +33 -0
  27. package/dist/akinator/types.d.ts.map +1 -1
  28. package/dist/cli.d.ts.map +1 -1
  29. package/dist/cli.js +51 -0
  30. package/dist/cli.js.map +1 -1
  31. package/dist/commands/curator.d.ts +25 -0
  32. package/dist/commands/curator.d.ts.map +1 -0
  33. package/dist/commands/curator.js +58 -0
  34. package/dist/commands/curator.js.map +1 -0
  35. package/dist/commands/doctor.d.ts +2 -0
  36. package/dist/commands/doctor.d.ts.map +1 -1
  37. package/dist/commands/doctor.js +19 -6
  38. package/dist/commands/doctor.js.map +1 -1
  39. package/dist/commands/init.d.ts.map +1 -1
  40. package/dist/commands/init.js +6 -0
  41. package/dist/commands/init.js.map +1 -1
  42. package/dist/commands/setup.d.ts +6 -1
  43. package/dist/commands/setup.d.ts.map +1 -1
  44. package/dist/commands/setup.js +34 -7
  45. package/dist/commands/setup.js.map +1 -1
  46. package/dist/config/paths.d.ts +5 -0
  47. package/dist/config/paths.d.ts.map +1 -1
  48. package/dist/config/paths.js +20 -0
  49. package/dist/config/paths.js.map +1 -1
  50. package/dist/context/delivery.d.ts +9 -2
  51. package/dist/context/delivery.d.ts.map +1 -1
  52. package/dist/context/delivery.js +49 -27
  53. package/dist/context/delivery.js.map +1 -1
  54. package/dist/context/feedback.d.ts.map +1 -1
  55. package/dist/context/feedback.js +21 -6
  56. package/dist/context/feedback.js.map +1 -1
  57. package/dist/context/ranking.d.ts +4 -1
  58. package/dist/context/ranking.d.ts.map +1 -1
  59. package/dist/context/ranking.js +29 -0
  60. package/dist/context/ranking.js.map +1 -1
  61. package/dist/context/scoped-broker.d.ts +63 -0
  62. package/dist/context/scoped-broker.d.ts.map +1 -0
  63. package/dist/context/scoped-broker.js +248 -0
  64. package/dist/context/scoped-broker.js.map +1 -0
  65. package/dist/gateway/agent-service.js +2 -2
  66. package/dist/gateway/agent-service.js.map +1 -1
  67. package/dist/ledger/maintenance.d.ts.map +1 -1
  68. package/dist/ledger/maintenance.js +5 -3
  69. package/dist/ledger/maintenance.js.map +1 -1
  70. package/dist/mcp/server.d.ts.map +1 -1
  71. package/dist/mcp/server.js +103 -7
  72. package/dist/mcp/server.js.map +1 -1
  73. package/dist/memory/curator.d.ts +71 -0
  74. package/dist/memory/curator.d.ts.map +1 -0
  75. package/dist/memory/curator.js +451 -0
  76. package/dist/memory/curator.js.map +1 -0
  77. package/dist/memory/entries.d.ts.map +1 -1
  78. package/dist/memory/entries.js +17 -0
  79. package/dist/memory/entries.js.map +1 -1
  80. package/dist/memory/hybrid-retrieval.d.ts +30 -0
  81. package/dist/memory/hybrid-retrieval.d.ts.map +1 -0
  82. package/dist/memory/hybrid-retrieval.js +228 -0
  83. package/dist/memory/hybrid-retrieval.js.map +1 -0
  84. package/dist/memory/rebuild-search.d.ts +14 -0
  85. package/dist/memory/rebuild-search.d.ts.map +1 -0
  86. package/dist/memory/rebuild-search.js +69 -0
  87. package/dist/memory/rebuild-search.js.map +1 -0
  88. package/dist/memory/retrieval-query.d.ts +21 -0
  89. package/dist/memory/retrieval-query.d.ts.map +1 -0
  90. package/dist/memory/retrieval-query.js +68 -0
  91. package/dist/memory/retrieval-query.js.map +1 -0
  92. package/dist/memory/retrieval.d.ts.map +1 -1
  93. package/dist/memory/retrieval.js +19 -8
  94. package/dist/memory/retrieval.js.map +1 -1
  95. package/dist/memory/scoped-memory.d.ts +19 -0
  96. package/dist/memory/scoped-memory.d.ts.map +1 -1
  97. package/dist/memory/scoped-memory.js +275 -30
  98. package/dist/memory/scoped-memory.js.map +1 -1
  99. package/dist/memory/structured-memory.d.ts +47 -0
  100. package/dist/memory/structured-memory.d.ts.map +1 -0
  101. package/dist/memory/structured-memory.js +172 -0
  102. package/dist/memory/structured-memory.js.map +1 -0
  103. package/dist/serialization/validate.d.ts.map +1 -1
  104. package/dist/serialization/validate.js +26 -1
  105. package/dist/serialization/validate.js.map +1 -1
  106. package/dist/setup/opencode-evidence.d.ts +37 -0
  107. package/dist/setup/opencode-evidence.d.ts.map +1 -0
  108. package/dist/setup/opencode-evidence.js +58 -0
  109. package/dist/setup/opencode-evidence.js.map +1 -0
  110. package/dist/setup/opencode-loop-guard.d.ts +6 -1
  111. package/dist/setup/opencode-loop-guard.d.ts.map +1 -1
  112. package/dist/setup/opencode-loop-guard.js +84 -4
  113. package/dist/setup/opencode-loop-guard.js.map +1 -1
  114. package/dist/setup/render.d.ts.map +1 -1
  115. package/dist/setup/render.js +8 -7
  116. package/dist/setup/render.js.map +1 -1
  117. package/dist/setup/standard-skills.d.ts +13 -0
  118. package/dist/setup/standard-skills.d.ts.map +1 -0
  119. package/dist/setup/standard-skills.js +42 -0
  120. package/dist/setup/standard-skills.js.map +1 -0
  121. package/dist/web/i18n.d.ts +34 -0
  122. package/dist/web/i18n.d.ts.map +1 -1
  123. package/dist/web/i18n.js +136 -0
  124. package/dist/web/i18n.js.map +1 -1
  125. package/dist/web/server.d.ts.map +1 -1
  126. package/dist/web/server.js +22 -0
  127. package/dist/web/server.js.map +1 -1
  128. package/dist/web/ui.d.ts.map +1 -1
  129. package/dist/web/ui.js +224 -6
  130. package/dist/web/ui.js.map +1 -1
  131. package/migrations/005_hybrid_search.sql +85 -0
  132. package/migrations/006_context_v2.sql +4 -0
  133. package/migrations/007_akinator_reasoning.sql +58 -0
  134. package/package.json +2 -1
  135. package/skills/kiokuko-ui-design-soul/SKILL.md +50 -0
  136. package/skills/kiokuko-ui-design-soul/references/ui-checklist.md +71 -0
  137. package/templates/AGENTS.md +8 -7
package/dist/web/ui.js CHANGED
@@ -47,6 +47,7 @@ export const WEB_HTML = String.raw `<!doctype html>
47
47
  .button { border:1px solid var(--line); border-radius:12px; background:var(--panel); color:var(--ink); padding:10px 14px; font-weight:700; }
48
48
  .button.primary { background:var(--accent); color:#fff; border-color:var(--accent); }
49
49
  .button:disabled { cursor:not-allowed; opacity:.45; }
50
+ .button:focus-visible,.curator-close:focus-visible { outline:3px solid rgba(37,99,235,.25); outline-offset:2px; }
50
51
  .layout { display:grid; grid-template-columns:220px minmax(300px,1fr) minmax(360px,1.15fr); gap:16px; align-items:start; }
51
52
  .panel { background:rgba(255,255,255,.88); border:1px solid rgba(226,232,240,.9); border-radius:20px; box-shadow:0 14px 44px rgba(15,23,42,.08); overflow:hidden; }
52
53
  .panel-head { padding:18px 20px; border-bottom:1px solid var(--line); display:flex; justify-content:space-between; align-items:center; gap:12px; }
@@ -90,13 +91,48 @@ export const WEB_HTML = String.raw `<!doctype html>
90
91
  .detail-block { border-top:1px solid var(--line); padding-top:12px; margin-top:12px; }
91
92
  .detail-block h3 { margin:0 0 8px; font-size:14px; }
92
93
  .detail-text { white-space:pre-wrap; overflow-wrap:anywhere; color:var(--muted); font-size:13px; }
94
+ .curator-modal { position:fixed; inset:0; z-index:100; display:grid; place-items:center; padding:24px; background:rgba(15,23,42,.42); backdrop-filter:blur(6px); animation:curator-backdrop-in .18s ease-out both; }
95
+ .curator-modal.is-closing { animation:curator-backdrop-out .18s ease-in both; }
96
+ .curator-dialog { width:min(880px,calc(100vw - 32px)); max-height:min(860px,calc(100vh - 48px)); display:flex; flex-direction:column; min-height:0; background:rgba(255,255,255,.96); box-shadow:0 28px 90px rgba(15,23,42,.28); animation:curator-dialog-in .22s cubic-bezier(.2,.8,.2,1) both; }
97
+ .curator-modal.is-closing .curator-dialog { animation:curator-dialog-out .18s ease-in both; }
98
+ .curator-dialog .panel-body { min-height:0; overflow-y:auto; }
99
+ .curator-dialog-head { align-items:flex-start; }
100
+ .curator-heading { display:flex; align-items:center; gap:10px; min-width:0; flex-wrap:wrap; }
101
+ .curator-header-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; min-width:0; margin-left:auto; }
102
+ .curator-actions { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
103
+ .curator-actions .button { padding:8px 10px; border-radius:10px; font-size:13px; white-space:nowrap; }
104
+ .curator-selection-count { color:var(--muted); font-size:12px; font-weight:700; white-space:nowrap; }
105
+ .curator-selection-summary { display:block; margin:0 0 10px; }
106
+ .curator-close { display:grid; place-items:center; flex:0 0 auto; width:44px; height:44px; border:1px solid var(--line); border-radius:12px; background:var(--panel); color:var(--muted); font-size:26px; line-height:1; }
107
+ .curator-close:hover { border-color:#93c5fd; background:var(--accent-soft); color:var(--accent); }
108
+ .curator-status { margin:0 0 12px; }
109
+ .curator-list { display:grid; gap:12px; }
110
+ .curator-card { border:1px solid var(--line); border-radius:14px; padding:16px; background:var(--panel); }
111
+ .curator-card.selected { border-color:#93c5fd; box-shadow:0 8px 20px rgba(37,99,235,.10); }
112
+ .curator-select { display:flex; gap:8px; align-items:center; color:var(--muted); font-size:13px; font-weight:700; }
113
+ .curator-select input { width:17px; height:17px; accent-color:#2563eb; }
114
+ .curator-workspace { overflow-wrap:anywhere; }
115
+ .curator-card h3 { margin:8px 0; font-size:17px; }
116
+ .curator-overview { margin:8px 0 12px; color:var(--muted); white-space:pre-wrap; line-height:1.55; }
117
+ .curator-draft { margin:12px 0; padding:12px; border:1px solid var(--line); border-radius:12px; background:var(--surface); }
118
+ .curator-draft h4 { margin:0 0 10px; font-size:14px; }
119
+ .curator-draft-label { margin:10px 0 4px; color:var(--muted); font-size:12px; font-weight:700; }
120
+ .curator-draft-value { margin:0; white-space:pre-wrap; overflow-wrap:anywhere; font:inherit; font-size:13px; line-height:1.5; }
121
+ .curator-reasons { color:var(--muted); font-size:12px; white-space:pre-wrap; }
122
+ @keyframes curator-backdrop-in { from { opacity:0; } to { opacity:1; } }
123
+ @keyframes curator-backdrop-out { from { opacity:1; } to { opacity:0; } }
124
+ @keyframes curator-dialog-in { from { opacity:0; transform:translateY(10px) scale(.98); } to { opacity:1; transform:translateY(0) scale(1); } }
125
+ @keyframes curator-dialog-out { from { opacity:1; transform:translateY(0) scale(1); } to { opacity:0; transform:translateY(6px) scale(.985); } }
126
+ @media (prefers-reduced-motion: reduce) {
127
+ .curator-modal,.curator-modal .curator-dialog,.curator-modal.is-closing,.curator-modal.is-closing .curator-dialog { animation-duration:.01ms; }
128
+ }
93
129
  @media (max-width:680px) { .operator-grid { grid-template-columns:1fr; } }
94
130
  @media (max-width:1050px) { .layout { grid-template-columns:180px minmax(280px,1fr); } .editor-panel { grid-column:1 / -1; } }
95
- @media (max-width:680px) { .shell { padding:16px; } .topbar { display:block; position:relative; } .brand { padding-right:56px; } .topbar-side { margin-top:16px; align-self:auto; } .language-picker { position:absolute; top:0; right:0; } .toolbar { justify-content:stretch; margin-top:0; } .topbar-side .control,.topbar-side .search { flex:1 1 100%; width:100%; } .search { min-width:0; } .layout { grid-template-columns:1fr; } .genres-panel { order:0; } .list-panel { order:1; } .editor-panel { order:2; } .entry-list { max-height:none; } }
131
+ @media (max-width:680px) { .shell { padding:16px; } .topbar { display:block; position:relative; } .brand { padding-right:56px; } .topbar-side { margin-top:16px; align-self:auto; } .language-picker { position:absolute; top:0; right:0; } .toolbar { justify-content:stretch; margin-top:0; } .topbar-side .control,.topbar-side .search { flex:1 1 100%; width:100%; } .search { min-width:0; } .layout { grid-template-columns:1fr; } .genres-panel { order:0; } .list-panel { order:1; } .editor-panel { order:2; } .entry-list { max-height:none; } .curator-dialog-head { flex-wrap:wrap; } .curator-header-actions { flex:1 1 100%; width:100%; margin-left:0; } .curator-actions { justify-content:flex-end; } }
96
132
  </style>
97
133
  </head>
98
134
  <body>
99
- <main class="shell">
135
+ <main id="app-main" class="shell">
100
136
  <header class="topbar">
101
137
  <div class="brand">
102
138
  <div class="eyebrow" data-i18n="eyebrow">Local memory console</div>
@@ -113,6 +149,7 @@ export const WEB_HTML = String.raw `<!doctype html>
113
149
  <div class="toolbar">
114
150
  <select id="workspace" class="control" aria-label="Workspace" data-i18n-aria-label="workspaceLabel"></select>
115
151
  <input id="search" class="search" type="search" placeholder="Search memory…" aria-label="Search memory…" data-i18n-placeholder="searchPlaceholder" data-i18n-aria-label="searchPlaceholder">
152
+ <button id="curator-button" class="button" type="button" data-i18n="curator">Curator</button>
116
153
  <button id="refresh" class="button" type="button" data-i18n="refresh">Refresh</button>
117
154
  </div>
118
155
  </div>
@@ -140,6 +177,23 @@ export const WEB_HTML = String.raw `<!doctype html>
140
177
  </div>
141
178
  </section>
142
179
  </main>
180
+ <div id="curator-modal" class="curator-modal" hidden>
181
+ <section id="curator-panel" class="panel curator-dialog" role="dialog" aria-modal="true" aria-labelledby="curator-title" aria-describedby="curator-description">
182
+ <div class="panel-head curator-dialog-head">
183
+ <div class="curator-heading"><h2 id="curator-title" data-i18n="curatorTitle">Curator</h2><span class="badge" data-i18n="curatorBadge">user confirmation required</span></div>
184
+ <div class="curator-header-actions">
185
+ <div id="curator-actions" class="curator-actions"></div>
186
+ <button id="curator-close" class="curator-close" type="button" aria-label="Close Curator" data-i18n-aria-label="curatorClose"><span aria-hidden="true">×</span></button>
187
+ </div>
188
+ </div>
189
+ <div class="panel-body">
190
+ <p id="curator-description" class="subtitle" data-i18n="curatorDescription">Review reusable knowledge candidates and add them to global memory.</p>
191
+ <div id="curator-selection-count" class="curator-selection-count curator-selection-summary">0 selected</div>
192
+ <div id="curator-status" class="status" role="status" aria-live="polite"></div>
193
+ <div id="curator-list" class="curator-list"></div>
194
+ </div>
195
+ </section>
196
+ </div>
143
197
  <script>
144
198
  const i18n = ${WEB_I18N_CONFIG};
145
199
  const localeStorageKey = 'kiokuko.web.locale';
@@ -163,7 +217,7 @@ export const WEB_HTML = String.raw `<!doctype html>
163
217
  const botModes = [
164
218
  ['bot:common', 'bot.common'], ['bot:researcher', 'bot.researcher'], ['bot:builder', 'bot.builder'], ['bot:reviewer', 'bot.reviewer'], ['bot:devops', 'bot.devops'], ['bot:writer', 'bot.writer'], ['bot:analyst', 'bot.analyst']
165
219
  ];
166
- const state = { locale: initialLocale, workspace: '', kind: 'all', tag: '', query: '', entries: [], tags: [], selected: null, runs: [], selectedRun: null, localizedStatus: null };
220
+ const state = { locale: initialLocale, workspace: '', kind: 'all', tag: '', query: '', entries: [], tags: [], selected: null, runs: [], selectedRun: null, curatorCandidates: [], curatorSelected: new Set(), curatorGlobalized: new Set(), curatorOpen: false, curatorPreviousFocus: null, localizedStatus: null };
167
221
  const $ = (id) => document.getElementById(id);
168
222
  const t = (key, parameters = {}) => {
169
223
  const template = i18n.messages[state.locale]?.[key] ?? i18n.messages[i18n.defaultLocale]?.[key] ?? key;
@@ -212,7 +266,7 @@ export const WEB_HTML = String.raw `<!doctype html>
212
266
  state.locale = normalizeLocale(value) || i18n.defaultLocale;
213
267
  try { localStorage.setItem(localeStorageKey, state.locale); } catch {}
214
268
  setLanguageMenuOpen(false, true);
215
- applyTranslations(); renderFilters(); renderEntries(); renderRuns(); renderRunDetail(); updateEditorState();
269
+ applyTranslations(); renderFilters(); renderEntries(); renderRuns(); renderRunDetail(); renderCurator(); updateEditorState();
216
270
  };
217
271
  const applyTranslations = () => {
218
272
  document.documentElement.lang = state.locale;
@@ -233,6 +287,58 @@ export const WEB_HTML = String.raw `<!doctype html>
233
287
  return value;
234
288
  };
235
289
  const escapeTags = (value) => value.split(',').map((item) => item.trim()).filter(Boolean);
290
+ const setCuratorStatus = (message, error = false) => {
291
+ const element = $('curator-status');
292
+ element.textContent = message;
293
+ element.className = error ? 'status notice error curator-status' : 'status curator-status';
294
+ };
295
+ const setCuratorBackgroundBlocked = (blocked) => {
296
+ const appMain = $('app-main');
297
+ appMain.inert = blocked;
298
+ if (blocked) appMain.setAttribute('aria-hidden', 'true');
299
+ else appMain.removeAttribute('aria-hidden');
300
+ };
301
+ function openCurator() {
302
+ if (state.curatorOpen) { $('curator-close')?.focus(); return; }
303
+ state.curatorPreviousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
304
+ state.curatorOpen = true;
305
+ const modal = $('curator-modal');
306
+ modal.classList.remove('is-closing');
307
+ setCuratorBackgroundBlocked(true);
308
+ renderCurator();
309
+ requestAnimationFrame(() => $('curator-close')?.focus());
310
+ }
311
+ function closeCurator() {
312
+ const modal = $('curator-modal');
313
+ if (modal.hidden) return;
314
+ state.curatorOpen = false;
315
+ modal.classList.add('is-closing');
316
+ let finished = false;
317
+ const finish = () => {
318
+ if (finished) return;
319
+ finished = true;
320
+ modal.hidden = true;
321
+ modal.classList.remove('is-closing');
322
+ setCuratorBackgroundBlocked(false);
323
+ const previousFocus = state.curatorPreviousFocus;
324
+ state.curatorPreviousFocus = null;
325
+ if (previousFocus?.isConnected) previousFocus.focus();
326
+ else $('curator-button')?.focus();
327
+ };
328
+ modal.addEventListener('animationend', (event) => { if (event.target === modal) finish(); }, { once: true });
329
+ window.setTimeout(finish, 220);
330
+ }
331
+ function handleCuratorKeydown(event) {
332
+ if (event.key === 'Escape') { event.preventDefault(); closeCurator(); return; }
333
+ if (event.key !== 'Tab') return;
334
+ const modal = $('curator-modal');
335
+ const focusable = [...modal.querySelectorAll('button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')];
336
+ if (!focusable.length) { event.preventDefault(); return; }
337
+ const first = focusable[0];
338
+ const last = focusable.at(-1);
339
+ if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
340
+ else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
341
+ }
236
342
 
237
343
  function filterButton(key, label, count, active, onClick) {
238
344
  const button = document.createElement('button');
@@ -314,6 +420,115 @@ export const WEB_HTML = String.raw `<!doctype html>
314
420
 
315
421
  async function selectEntry(id) { try { const result = await api('/api/entries/' + encodeURIComponent(id) + '?workspace=' + encodeURIComponent(state.workspace)); state.selected = result.entry; renderEntries(); renderEditor(); } catch (error) { setStatus(error.message, true); } }
316
422
 
423
+ const curatorCandidateKey = (candidate) => candidate.workspace + '\u0000' + candidate.entryId;
424
+
425
+ async function applySelectedCurator() {
426
+ const selected = state.curatorCandidates.filter((candidate) => state.curatorSelected.has(curatorCandidateKey(candidate)) && !state.curatorGlobalized.has(curatorCandidateKey(candidate)));
427
+ if (!selected.length) return;
428
+ let added = 0;
429
+ let failed = 0;
430
+ for (const candidate of selected) {
431
+ try {
432
+ await api('/api/curator/globalize?workspace=' + encodeURIComponent(candidate.workspace), {
433
+ method: 'POST',
434
+ headers: { 'content-type': 'application/json' },
435
+ body: JSON.stringify({ workspace: candidate.workspace, entryId: candidate.entryId, expectedRevision: candidate.revision }),
436
+ });
437
+ state.curatorGlobalized.add(curatorCandidateKey(candidate));
438
+ added += 1;
439
+ } catch (error) {
440
+ failed += 1;
441
+ setStatus(error.message, true);
442
+ }
443
+ }
444
+ state.curatorSelected.clear();
445
+ await loadCurator();
446
+ await loadEntries();
447
+ if (added > 0 && failed === 0) { setLocalizedStatus('curatorBatchAdded', { count: added }); setCuratorStatus(t('curatorBatchAdded', { count: added })); }
448
+ else if (added > 0) { setLocalizedStatus('curatorBatchPartial', { added, failed }, true); setCuratorStatus(t('curatorBatchPartial', { added, failed }), true); }
449
+ }
450
+
451
+ function renderCurator() {
452
+ if (!state.curatorOpen) return;
453
+ const modal = $('curator-modal');
454
+ modal.hidden = false;
455
+ const root = $('curator-list');
456
+ const selectable = state.curatorCandidates.filter((candidate) => !state.curatorGlobalized.has(curatorCandidateKey(candidate)));
457
+ $('curator-selection-count').textContent = t('curatorSelected', { count: state.curatorSelected.size });
458
+ const actionRoot = $('curator-actions'); actionRoot.replaceChildren();
459
+ const selectAll = document.createElement('button'); selectAll.type = 'button'; selectAll.className = 'button'; setI18nText(selectAll, 'curatorSelectAll'); selectAll.disabled = selectable.length === 0;
460
+ selectAll.addEventListener('click', () => { selectable.forEach((candidate) => state.curatorSelected.add(curatorCandidateKey(candidate))); renderCurator(); });
461
+ const clearSelection = document.createElement('button'); clearSelection.type = 'button'; clearSelection.className = 'button'; setI18nText(clearSelection, 'curatorClearSelection'); clearSelection.disabled = state.curatorSelected.size === 0;
462
+ clearSelection.addEventListener('click', () => { state.curatorSelected.clear(); renderCurator(); });
463
+ const apply = document.createElement('button'); apply.type = 'button'; apply.className = 'button primary'; setI18nText(apply, 'curatorApplySelected'); apply.disabled = state.curatorSelected.size === 0;
464
+ apply.addEventListener('click', () => applySelectedCurator());
465
+ actionRoot.append(selectAll, clearSelection, apply);
466
+ if (!state.curatorCandidates.length) { const empty = document.createElement('div'); empty.className = 'editor-empty'; setI18nText(empty, 'noCuratorCandidates'); root.replaceChildren(empty); return; }
467
+ const cards = state.curatorCandidates.map((candidate) => {
468
+ const card = document.createElement('article'); card.className = 'curator-card';
469
+ const candidateKey = curatorCandidateKey(candidate);
470
+ const done = state.curatorGlobalized.has(candidateKey);
471
+ const selectLabel = document.createElement('label'); selectLabel.className = 'curator-select';
472
+ const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = state.curatorSelected.has(candidateKey); checkbox.disabled = done;
473
+ checkbox.addEventListener('change', () => { if (checkbox.checked) state.curatorSelected.add(candidateKey); else state.curatorSelected.delete(candidateKey); renderCurator(); });
474
+ const selectText = document.createElement('span'); selectText.textContent = done ? t('globalized') : t('curatorSelectSkill'); selectLabel.append(checkbox, selectText);
475
+ const meta = document.createElement('div'); meta.className = 'entry-meta';
476
+ const kind = document.createElement('span'); kind.className = 'badge'; kind.textContent = labelForKind(candidate.kind);
477
+ const score = document.createElement('span'); score.className = 'badge'; score.textContent = t('curatorScore', { score: candidate.score });
478
+ const workspace = document.createElement('span'); workspace.className = 'badge curator-workspace'; workspace.textContent = t('curatorWorkspace', { workspace: candidate.workspace });
479
+ meta.append(kind, score, workspace);
480
+ if (candidate.knowledge && candidate.knowledge.skillReady) { const ready = document.createElement('span'); ready.className = 'badge'; setI18nText(ready, 'curatorSkillReady'); meta.append(ready); }
481
+ const heading = document.createElement('h3'); heading.textContent = candidate.skillName;
482
+ const overview = document.createElement('div'); overview.className = 'curator-overview'; overview.textContent = candidate.overview.join('\n');
483
+ const knowledge = document.createElement('div'); knowledge.className = 'curator-reasons'; knowledge.textContent = candidate.knowledge ? [
484
+ t('curatorEvidence', { hits: candidate.knowledge.qualifiedHits, runs: candidate.knowledge.independentRuns, workspaces: candidate.knowledge.independentWorkspaces }),
485
+ t('curatorSilo', { value: candidate.knowledge.averageCompleteness }),
486
+ ...(candidate.knowledge.readinessReasons || []),
487
+ ].join('\n') : '';
488
+ const draft = document.createElement('section'); draft.className = 'curator-draft';
489
+ const draftHeading = document.createElement('h4'); setI18nText(draftHeading, 'curatorDraft');
490
+ draft.append(draftHeading);
491
+ const draftFields = [
492
+ ['curatorDraftTitle', candidate.draft.title],
493
+ ['curatorDraftSummary', candidate.draft.summary],
494
+ ['curatorDraftBody', candidate.draft.body],
495
+ ['curatorDraftVersion', candidate.draft.version],
496
+ ['curatorDraftChanges', (candidate.draft.changes || []).map((change) => t({
497
+ 'portable-sections-generated': 'curatorChangePortableSections',
498
+ 'project-references-normalized': 'curatorChangeProjectReferences',
499
+ 'paths-generalized': 'curatorChangePaths',
500
+ 'applicability-retained': 'curatorChangeApplicability',
501
+ }[change] || 'curatorChangeUnknown')).join('\n')],
502
+ ];
503
+ for (const [labelKey, value] of draftFields) {
504
+ const label = document.createElement('div'); label.className = 'curator-draft-label'; setI18nText(label, labelKey);
505
+ const content = document.createElement('pre'); content.className = 'curator-draft-value'; content.textContent = value;
506
+ draft.append(label, content);
507
+ }
508
+ const reasons = document.createElement('div'); reasons.className = 'curator-reasons'; reasons.textContent = [...(candidate.reasons || []), ...(candidate.warnings || []).map((warning) => t('curatorWarning', { warning }))].join('\n');
509
+ card.classList.toggle('selected', checkbox.checked);
510
+ card.append(selectLabel, meta, heading, overview, knowledge, draft, reasons); return card;
511
+ });
512
+ root.replaceChildren(toolbar, ...cards);
513
+ }
514
+
515
+ async function loadCurator() {
516
+ openCurator();
517
+ const panel = $('curator-panel');
518
+ panel.setAttribute('aria-busy', 'true');
519
+ setCuratorStatus(t('curatorLoading'));
520
+ try {
521
+ const result = await api('/api/curator/candidates?limit=50');
522
+ state.curatorCandidates = result.candidates || [];
523
+ const available = new Set(state.curatorCandidates.map((candidate) => curatorCandidateKey(candidate)));
524
+ state.curatorSelected = new Set([...state.curatorSelected].filter((key) => available.has(key)));
525
+ setCuratorStatus('');
526
+ renderCurator();
527
+ }
528
+ catch (error) { setCuratorStatus(error.message, true); }
529
+ finally { panel.removeAttribute('aria-busy'); }
530
+ }
531
+
317
532
  async function loadRunDetail(runId) {
318
533
  try {
319
534
  const result = await api('/api/operator/runs/' + encodeURIComponent(runId));
@@ -369,7 +584,7 @@ export const WEB_HTML = String.raw `<!doctype html>
369
584
 
370
585
  async function loadEntries() { if (!state.workspace) return; try { const params = new URLSearchParams({ workspace: state.workspace }); if (state.kind !== 'all') params.set('kind', state.kind); if (state.tag) params.set('tag', state.tag); if (state.query) params.set('q', state.query); const result = await api('/api/entries?' + params); state.entries = result.entries; if (state.selected && !state.entries.some((entry) => entry.id === state.selected.id)) state.selected = null; renderEntries(); renderFilters(); if (!state.selected && state.entries[0]) await selectEntry(state.entries[0].id); else renderEditor(); setLocalizedCountStatus('displayedCount', result.entries.length); } catch (error) { setStatus(error.message, true); } }
371
586
  async function loadTags() { if (!state.workspace) return; try { const result = await api('/api/tags?workspace=' + encodeURIComponent(state.workspace)); state.tags = result.tags; renderFilters(); } catch (error) { setStatus(error.message, true); } }
372
- async function loadWorkspaces() { try { const result = await api('/api/workspaces'); const select = $('workspace'); select.replaceChildren(...result.workspaces.map((item) => { const option = document.createElement('option'); option.value = item.workspace; option.textContent = (item.displayName || item.workspace) + ' (' + item.count + ')'; return option; })); if (!state.workspace && result.workspaces[0]) state.workspace = result.workspaces[0].workspace; select.value = state.workspace; if (state.workspace) { await loadTags(); await loadEntries(); await loadRuns(); } else setLocalizedStatus('noWorkspace', {}, true); } catch (error) { setStatus(error.message, true); } }
587
+ async function loadWorkspaces() { try { const result = await api('/api/workspaces'); const select = $('workspace'); select.replaceChildren(...result.workspaces.map((item) => { const option = document.createElement('option'); option.value = item.workspace; option.textContent = (item.displayName || item.workspace) + ' (' + item.count + ')'; return option; })); if (!state.workspace && result.workspaces[0]) state.workspace = result.workspaces[0].workspace; select.value = state.workspace; if (state.workspace) { await loadTags(); await loadEntries(); await loadRuns(); } if (state.curatorOpen) await loadCurator(); else if (!state.workspace) setLocalizedStatus('noWorkspace', {}, true); } catch (error) { setStatus(error.message, true); } }
373
588
  $('language-toggle').addEventListener('click', () => {
374
589
  setLanguageMenuOpen($('language-toggle').getAttribute('aria-expanded') !== 'true');
375
590
  });
@@ -391,7 +606,10 @@ export const WEB_HTML = String.raw `<!doctype html>
391
606
  });
392
607
  document.addEventListener('click', (event) => { if (!$('language-picker')?.contains(event.target)) setLanguageMenuOpen(false); });
393
608
  document.addEventListener('focusin', (event) => { if (!$('language-picker')?.contains(event.target)) setLanguageMenuOpen(false); });
394
- $('workspace').addEventListener('change', (event) => { state.workspace = event.target.value; state.selected = null; state.selectedRun = null; state.runs = []; state.tag = ''; loadTags().then(loadEntries).then(loadRuns); });
609
+ $('workspace').addEventListener('change', (event) => { if (state.curatorOpen) closeCurator(); state.workspace = event.target.value; state.selected = null; state.selectedRun = null; state.runs = []; state.tag = ''; state.curatorCandidates = []; state.curatorSelected = new Set(); state.curatorGlobalized = new Set(); state.curatorOpen = false; loadTags().then(loadEntries).then(loadRuns); });
610
+ $('curator-button').addEventListener('click', () => loadCurator());
611
+ $('curator-close').addEventListener('click', () => closeCurator());
612
+ $('curator-modal').addEventListener('keydown', handleCuratorKeydown);
395
613
  $('refresh').addEventListener('click', () => loadWorkspaces());
396
614
  let searchTimer; $('search').addEventListener('input', (event) => { clearTimeout(searchTimer); state.query = event.target.value.trim(); searchTimer = setTimeout(() => loadEntries(), 180); });
397
615
  applyTranslations();
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE7F,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC;IACrC,aAAa,EAAE,kBAAkB;IACjC,YAAY,EAAE,iBAAiB;IAC/B,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;CACvB,CAAC;KACC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC;KAC/B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEnC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAmIf,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiQ1B,CAAC"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE7F,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC;IACrC,aAAa,EAAE,kBAAkB;IACjC,YAAY,EAAE,iBAAiB;IAC/B,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;CACvB,CAAC;KACC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC;KAC/B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEnC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAyLf,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqa1B,CAAC"}
@@ -0,0 +1,85 @@
1
+ -- Additive hybrid-search projections. The original entries_fts index remains
2
+ -- intact so databases can be upgraded and rolled back without losing the v1
3
+ -- lexical search path.
4
+ CREATE VIRTUAL TABLE entries_trigram USING fts5(
5
+ title,
6
+ body,
7
+ summary,
8
+ tags_text,
9
+ tokenize='trigram'
10
+ );
11
+
12
+ CREATE TABLE entry_search_signals (
13
+ entry_id TEXT NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
14
+ signal_type TEXT NOT NULL CHECK (
15
+ signal_type IN ('language', 'framework', 'runtime', 'database', 'tool',
16
+ 'platform', 'package', 'symbol', 'path', 'error', 'command', 'tag')
17
+ ),
18
+ normalized_value TEXT NOT NULL,
19
+ PRIMARY KEY (entry_id, signal_type, normalized_value)
20
+ );
21
+
22
+ CREATE INDEX idx_entry_search_signals_lookup
23
+ ON entry_search_signals(signal_type, normalized_value);
24
+
25
+ CREATE TRIGGER entries_trigram_ai AFTER INSERT ON entries BEGIN
26
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
27
+ VALUES (
28
+ new.rowid,
29
+ new.title,
30
+ new.body,
31
+ COALESCE(new.summary, ''),
32
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = new.id), '')
33
+ );
34
+ END;
35
+
36
+ CREATE TRIGGER entries_trigram_ad AFTER DELETE ON entries BEGIN
37
+ DELETE FROM entries_trigram WHERE rowid = old.rowid;
38
+ END;
39
+
40
+ CREATE TRIGGER entries_trigram_au AFTER UPDATE ON entries BEGIN
41
+ DELETE FROM entries_trigram WHERE rowid = old.rowid;
42
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
43
+ VALUES (
44
+ new.rowid,
45
+ new.title,
46
+ new.body,
47
+ COALESCE(new.summary, ''),
48
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = new.id), '')
49
+ );
50
+ END;
51
+
52
+ CREATE TRIGGER tags_trigram_ai AFTER INSERT ON tags BEGIN
53
+ DELETE FROM entries_trigram WHERE rowid = (SELECT rowid FROM entries WHERE id = new.entry_id);
54
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
55
+ SELECT entries.rowid, entries.title, entries.body, COALESCE(entries.summary, ''),
56
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = entries.id), '')
57
+ FROM entries WHERE entries.id = new.entry_id;
58
+ END;
59
+
60
+ CREATE TRIGGER tags_trigram_ad AFTER DELETE ON tags BEGIN
61
+ DELETE FROM entries_trigram WHERE rowid = (SELECT rowid FROM entries WHERE id = old.entry_id);
62
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
63
+ SELECT entries.rowid, entries.title, entries.body, COALESCE(entries.summary, ''),
64
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = entries.id), '')
65
+ FROM entries WHERE entries.id = old.entry_id;
66
+ END;
67
+
68
+ CREATE TRIGGER tags_trigram_au AFTER UPDATE OF tag ON tags BEGIN
69
+ DELETE FROM entries_trigram WHERE rowid = (SELECT rowid FROM entries WHERE id = old.entry_id);
70
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
71
+ SELECT entries.rowid, entries.title, entries.body, COALESCE(entries.summary, ''),
72
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = entries.id), '')
73
+ FROM entries WHERE entries.id = new.entry_id;
74
+ END;
75
+
76
+ INSERT INTO entries_trigram(rowid, title, body, summary, tags_text)
77
+ SELECT entries.rowid,
78
+ entries.title,
79
+ entries.body,
80
+ COALESCE(entries.summary, ''),
81
+ COALESCE((SELECT group_concat(tag, ' ') FROM tags WHERE entry_id = entries.id), '')
82
+ FROM entries;
83
+
84
+ INSERT INTO entry_search_signals(entry_id, signal_type, normalized_value)
85
+ SELECT entry_id, 'tag', lower(trim(tag)) FROM tags WHERE length(trim(tag)) > 0;
@@ -0,0 +1,4 @@
1
+ -- Versioned context metadata. Existing v1 rows retain their original meaning
2
+ -- through defaults; new deliveries may identify global-origin entries.
3
+ ALTER TABLE context_deliveries ADD COLUMN score_schema_version INTEGER NOT NULL DEFAULT 1 CHECK (score_schema_version >= 1);
4
+ ALTER TABLE context_delivery_entries ADD COLUMN origin_scope TEXT NOT NULL DEFAULT 'project' CHECK (origin_scope IN ('project', 'global'));
@@ -0,0 +1,58 @@
1
+ -- Successful Akinator narrowing paths are durable evidence for Curator.
2
+ -- Retrieval impressions are deliberately excluded: only checkpointed runs can
3
+ -- insert rows into this table through the application service.
4
+ CREATE TABLE akinator_reasoning_paths (
5
+ path_id TEXT PRIMARY KEY,
6
+ concept_key TEXT NOT NULL CHECK (
7
+ length(concept_key) = 64
8
+ AND concept_key NOT GLOB '*[^0-9a-f]*'
9
+ ),
10
+ entry_id TEXT NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
11
+ entry_revision INTEGER NOT NULL CHECK (entry_revision > 0),
12
+ run_id TEXT NOT NULL REFERENCES ledger_runs(run_id) ON DELETE CASCADE,
13
+ intake_session_id TEXT NOT NULL REFERENCES akinator_sessions(id) ON DELETE CASCADE,
14
+ workspace TEXT NOT NULL,
15
+ policy_version TEXT NOT NULL,
16
+ task_type TEXT NOT NULL,
17
+ intent TEXT NOT NULL,
18
+ hypotheses_json TEXT NOT NULL,
19
+ question_path_json TEXT NOT NULL,
20
+ selected_action TEXT NOT NULL,
21
+ conditions_json TEXT NOT NULL,
22
+ verification_json TEXT NOT NULL,
23
+ stop_conditions_json TEXT NOT NULL,
24
+ silo_completeness REAL NOT NULL CHECK (silo_completeness >= 0 AND silo_completeness <= 1),
25
+ outcome TEXT NOT NULL CHECK (outcome IN ('completed', 'failed', 'cancelled', 'interrupted')),
26
+ qualified INTEGER NOT NULL CHECK (qualified IN (0, 1)),
27
+ disqualification_reasons_json TEXT NOT NULL,
28
+ created_at TEXT NOT NULL,
29
+ UNIQUE (run_id, entry_id, entry_revision)
30
+ );
31
+
32
+ CREATE INDEX idx_akinator_reasoning_paths_concept
33
+ ON akinator_reasoning_paths(concept_key, qualified, created_at DESC);
34
+
35
+ CREATE INDEX idx_akinator_reasoning_paths_entry
36
+ ON akinator_reasoning_paths(entry_id, entry_revision, created_at DESC);
37
+
38
+ CREATE TRIGGER akinator_reasoning_paths_link_guard
39
+ BEFORE INSERT ON akinator_reasoning_paths
40
+ BEGIN
41
+ SELECT RAISE(ABORT, 'akinator reasoning path link mismatch')
42
+ WHERE NOT EXISTS (
43
+ SELECT 1
44
+ FROM run_intakes AS ri
45
+ JOIN ledger_runs AS lr ON lr.run_id = ri.run_id
46
+ JOIN akinator_sessions AS a ON a.id = ri.session_id
47
+ WHERE ri.run_id = NEW.run_id
48
+ AND ri.session_id = NEW.intake_session_id
49
+ AND lr.workspace = NEW.workspace
50
+ AND a.workspace = NEW.workspace
51
+ ) OR NOT EXISTS (
52
+ SELECT 1
53
+ FROM entries AS e
54
+ WHERE e.id = NEW.entry_id
55
+ AND e.revision = NEW.entry_revision
56
+ AND e.workspace IN (NEW.workspace, 'global')
57
+ );
58
+ END;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdkc/kiokuko",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Model-agnostic external memory for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "dist/",
17
17
  "migrations/",
18
+ "skills/",
18
19
  "templates/",
19
20
  "README.md",
20
21
  "README.ja.md",
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: kiokuko-ui-design-soul
3
+ description: Apply HIG principles to app and web UI design, implementation, and review.
4
+ ---
5
+
6
+ <!-- KIOKUKO MANAGED STANDARD SKILL: kiokuko-ui-design-soul -->
7
+
8
+ # UI design soul
9
+
10
+ Use this skill when designing, implementing, or reviewing application and web interfaces. Do not invoke it for backend-only work, image generation alone, or tasks unrelated to an interface.
11
+
12
+ Treat Purpose, Agency, Responsibility, Familiarity, Flexibility, Simplicity, Craft, and Delight as decision principles, not as a mandate to copy Apple's visual style. Product requirements, the existing design system, accessibility standards, and safety and privacy requirements take precedence.
13
+
14
+ ## Establish the intent
15
+
16
+ Before choosing components or styling, state or infer from evidence:
17
+
18
+ - the interface's purpose;
19
+ - the people it serves;
20
+ - their primary task;
21
+ - how they should feel while completing it.
22
+
23
+ If any of these would materially change the design and cannot be inferred, ask one focused question. Otherwise, proceed and state the assumption.
24
+
25
+ ## Design the interaction contract
26
+
27
+ For every relevant action, cover the states that can occur: actionable, pressed or focused, processing, success, empty, failure, offline, permission denied, destructive confirmation, and recovered. Omit only states that are genuinely impossible.
28
+
29
+ - Give buttons immediate press and focus feedback.
30
+ - Keep asynchronous feedback in the action's context: update its label or status, show progress, and prevent accidental duplicate execution.
31
+ - Use determinate progress when completion can be measured and indeterminate progress otherwise.
32
+ - If progress stalls, explain why and identify the next action. Offer cancellation when the operation can be interrupted safely.
33
+ - Preserve input and completed work on failure. Provide a recovery path such as retry, undo, or back.
34
+ - Match confirmation strength to risk. Prefer undo for safely reversible actions; require explicit confirmation when recovery is unavailable or harm is material.
35
+ - Use motion only to clarify state, continuity, or causality. Never make animation the only carrier of information, and respect Reduced Motion.
36
+
37
+ ## Adapt to the platform
38
+
39
+ For web interfaces, do not force an Apple-like appearance. Follow the existing design system, semantic HTML, WCAG 2.2, and the conventions of the target platform. Support keyboard, screen reader, touch, pointer, responsive layouts, and relevant system preferences.
40
+
41
+ Treat Delight as the combined result of task completion, confidence, recoverability, and careful detail—not as an amount of decoration.
42
+
43
+ ## Deliver and verify
44
+
45
+ 1. Identify the primary flow and interaction states before polishing the happy path.
46
+ 2. Implement feedback and recovery beside the action that triggers them.
47
+ 3. Test with real input methods and accessibility settings, not screenshots alone.
48
+ 4. Report covered states, verified behaviors, and any known gaps.
49
+
50
+ Read [references/ui-checklist.md](references/ui-checklist.md) when producing or reviewing a concrete interface, when asynchronous or destructive behavior is involved, or when accessibility validation is in scope.
@@ -0,0 +1,71 @@
1
+ <!-- KIOKUKO MANAGED STANDARD SKILL: kiokuko-ui-design-soul -->
2
+
3
+ # UI design and review checklist
4
+
5
+ Last reviewed against the official sources: 2026-08-22.
6
+
7
+ This checklist paraphrases decision principles. It does not reproduce Apple text or require Apple-styled visuals.
8
+
9
+ ## Eight-principle map
10
+
11
+ | Principle | Practical question |
12
+ | --- | --- |
13
+ | Purpose | Does every important element help the user complete the primary task? |
14
+ | Agency | Can the user understand, initiate, interrupt where safe, and recover from actions? |
15
+ | Responsibility | Does the interface protect privacy, attention, safety, and user-created work? |
16
+ | Familiarity | Do labels, controls, navigation, and feedback follow the target platform's conventions? |
17
+ | Flexibility | Does the flow adapt to ability, input method, device, content size, and context? |
18
+ | Simplicity | Is the next meaningful action clear without hiding necessary information or control? |
19
+ | Craft | Are states, spacing, copy, timing, focus, and edge cases implemented consistently? |
20
+ | Delight | Does successful, calm, recoverable use feel better because the details work together? |
21
+
22
+ ## State coverage
23
+
24
+ For each primary action, verify the applicable states:
25
+
26
+ - **Actionable:** the label predicts the result; enabled and disabled states are distinguishable without color alone.
27
+ - **Pressed and focused:** feedback is immediate; keyboard focus is visible; focus order follows the task.
28
+ - **Processing:** status remains near the initiating control; duplicate submission is prevented without trapping the user.
29
+ - **Progress:** measurable work uses a determinate value; unmeasurable work uses an indeterminate indicator and an accessible status message.
30
+ - **Long or stalled work:** expectations are updated; a reason and next step are shown; cancellation is available only when it is safe and real.
31
+ - **Success:** completion is perceivable visually and programmatically; the interface moves focus only when that helps the next task.
32
+ - **Empty:** the state explains what is absent and offers a relevant next action rather than presenting a dead end.
33
+ - **Failure:** input and completed work are retained; the message says what happened in actionable language; retry, undo, or back is available where meaningful.
34
+ - **Offline:** unavailable behavior is explicit; queued or local work is not implied unless it is actually preserved.
35
+ - **Permission denied:** explain the missing capability and provide a safe route to settings, an alternative, or back.
36
+ - **Destructive:** communicate scope and consequence; prefer undo for reversible actions and use explicit confirmation for material irreversible harm.
37
+ - **Recovered:** clear stale errors and busy states; restore a coherent focus position; avoid re-running the action unexpectedly.
38
+
39
+ ## Accessibility and adaptation
40
+
41
+ - Use semantic HTML or native controls before recreating their behavior.
42
+ - Verify the full primary flow with keyboard only, including visible focus and escape from overlays.
43
+ - Verify names, roles, values, errors, progress, and status announcements with a screen reader.
44
+ - Test touch, pointer, keyboard, and relevant alternate input; do not require hover, precise pointing, or a single gesture.
45
+ - Test narrow and wide layouts, zoom or text resizing, longer translated copy, and dynamic content.
46
+ - Do not encode meaning with color, motion, shape, or sound alone.
47
+ - Respect Reduced Motion and provide equivalent state information without animation.
48
+ - Keep time limits adjustable or avoid them unless the task itself requires one.
49
+ - On the web, apply the existing design system and WCAG 2.2; do not imitate iOS merely because these principles originated in Apple HIG.
50
+
51
+ ## Async-action spot check
52
+
53
+ For a save, upload, generation, import, or other asynchronous action, confirm:
54
+
55
+ 1. Press or focus feedback is immediate.
56
+ 2. The busy state names the ongoing action and blocks accidental duplicates.
57
+ 3. Progress type matches what the system can measure.
58
+ 4. Cancellation has defined semantics and leaves data consistent.
59
+ 5. Success and failure are announced in the same task context.
60
+ 6. A failure keeps user input and provides a tested recovery path.
61
+
62
+ ## Official sources
63
+
64
+ - Apple Human Interface Guidelines — Design principles: https://developer.apple.com/design/human-interface-guidelines/design-principles
65
+ - Apple Human Interface Guidelines — Buttons: https://developer.apple.com/design/human-interface-guidelines/buttons
66
+ - Apple Human Interface Guidelines — Loading: https://developer.apple.com/design/human-interface-guidelines/loading
67
+ - Apple Human Interface Guidelines — Progress indicators: https://developer.apple.com/design/human-interface-guidelines/progress-indicators
68
+ - Apple Human Interface Guidelines — Feedback: https://developer.apple.com/design/human-interface-guidelines/feedback
69
+ - Apple Human Interface Guidelines — Motion: https://developer.apple.com/design/human-interface-guidelines/motion
70
+ - Apple Human Interface Guidelines — Accessibility: https://developer.apple.com/design/human-interface-guidelines/accessibility
71
+ - W3C Web Content Accessibility Guidelines 2.2: https://www.w3.org/TR/WCAG22/