@deepseek-insight/dsi 0.5.17 → 0.5.19

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 (103) hide show
  1. package/.agents/skills/dsi-adr/SKILL.md +121 -0
  2. package/.agents/skills/dsi-i18n-migrate/SKILL.md +87 -0
  3. package/.agents/skills/dsi-release/SKILL.md +175 -0
  4. package/.agents/skills/dsi-spcheck/SKILL.md +188 -0
  5. package/.agents/skills/dsi-spec/SKILL.md +310 -0
  6. package/.agents/skills/dsi-spec/references/mermaid-pattern.md +160 -0
  7. package/.agents/skills/dsi-spec/references/tasks-json-template.md +125 -0
  8. package/.agents/skills/dsi-task/SKILL.md +287 -0
  9. package/README.md +6 -23
  10. package/bin/dsi.mjs +67 -15
  11. package/bin/lib/mirror-skills.mjs +56 -0
  12. package/build/client/_app/immutable/assets/0.C4pk9RsQ.css +2 -0
  13. package/build/client/_app/immutable/assets/2.Da1CnqbG.css +1 -0
  14. package/build/client/_app/immutable/chunks/7MCZCUsX.js +1 -0
  15. package/build/client/_app/immutable/chunks/BeeAmuEs.js +2 -0
  16. package/build/client/_app/immutable/chunks/BgF4MPCH.js +1 -0
  17. package/build/client/_app/immutable/chunks/CLNDb8QN.js +6 -0
  18. package/build/client/_app/immutable/chunks/{Bl6W6XsZ.js → Dv2f6AVG.js} +1 -1
  19. package/build/client/_app/immutable/chunks/h_AAZIXW.js +1 -0
  20. package/build/client/_app/immutable/entry/{app.DhFsVx9Q.js → app.C8CwtYTr.js} +2 -2
  21. package/build/client/_app/immutable/entry/start.CCr7WSxA.js +1 -0
  22. package/build/client/_app/immutable/nodes/{0.DSOF-uUw.js → 0.VWq3h0rH.js} +1 -1
  23. package/build/client/_app/immutable/nodes/{1.wYU7SDRw.js → 1.Dnl1P2aB.js} +1 -1
  24. package/build/client/_app/immutable/nodes/2.0fqLf_Nw.js +170 -0
  25. package/build/client/_app/version.json +1 -1
  26. package/build/handler.js +4 -4
  27. package/build/index.js +4 -4
  28. package/build/server/chunks/chunks/app-config.svelte.js-rN4N5yAI.js +305 -0
  29. package/build/server/chunks/chunks/{config.js-D2vQdial.js → config.js-iG_1bK3r.js} +16 -2
  30. package/build/server/chunks/chunks/{db.js-B7zd2qLf.js → db.js-DbbYS9o8.js} +2 -2
  31. package/build/server/chunks/chunks/{db2.js-CCmqoCC8.js → db2.js-BQWa7bEG.js} +52 -41
  32. package/build/server/chunks/chunks/{dsh-connection.js-qRBqNOg4.js → dsh-connection.js-CpcCtA42.js} +3 -3
  33. package/build/server/chunks/chunks/{git-probe.js-Dw7WYQIb.js → git-probe.js-CvkToKO4.js} +2 -2
  34. package/build/server/chunks/chunks/{insight-config.js-BcJiCZlZ.js → insight-config.js-Dw_Qi0fO.js} +60 -11
  35. package/build/server/chunks/chunks/{internal.js-Y8BDUVWO.js → internal.js-BDS_h7zE.js} +3 -3
  36. package/build/server/chunks/chunks/{settings-document.js-BIwLIxnE.js → settings-document.js-BKnRkLVq.js} +2 -2
  37. package/build/server/chunks/chunks/{state.js-C1kUoUk4.js → state.js-eZmA7eF7.js} +3 -3
  38. package/build/server/chunks/chunks/{watcher.js-CUAJN9lS.js → watcher.js-C97Xyorx.js} +4 -4
  39. package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-DZyjEufw.js → _server.ts.js-Cqh1iqgu.js} +5 -5
  40. package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-sqxh02Ab.js → _server.ts.js-DNKD4rZu.js} +7 -7
  41. package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-CUnQYezc.js → _server.ts.js-CJ-FwkrZ.js} +7 -7
  42. package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-BZhBgOdD.js → _server.ts.js-CMM4kYVx.js} +6 -5
  43. package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-Dm7aSfmb.js → _server.ts.js-epVplzwB.js} +5 -5
  44. package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-CLZoIW4v.js → _server.ts.js-bXHb97gT.js} +5 -5
  45. package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-DBklPI3F.js → _server.ts.js-BNOHhtzy.js} +5 -5
  46. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/{_server.ts.js-Cuq-JEHL.js → _server.ts.js-BBi6jeVj.js} +5 -5
  47. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-C2M3e5l-.js → _server.ts.js-N0nKPuqL.js} +5 -5
  48. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-J64QhcMH.js → _server.ts.js-VGFCPAbs.js} +5 -5
  49. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-BSAQMYfS.js → _server.ts.js-na_sUb0Z.js} +5 -5
  50. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-BF9MvjDa.js → _server.ts.js-CeNf028P.js} +5 -5
  51. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-CrJB2-mv.js → _server.ts.js-DfqO-GO4.js} +5 -5
  52. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-BzvIAbjd.js → _server.ts.js-Dj9GYNl7.js} +5 -5
  53. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-CpID_rFC.js → _server.ts.js-DqShsRl9.js} +5 -5
  54. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-B_uu8lbl.js → _server.ts.js-BQSBUf70.js} +5 -5
  55. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-DjvyuSLb.js → _server.ts.js-CeCKDyKB.js} +4 -4
  56. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-DX8nvoLd.js → _server.ts.js-CadTCNUJ.js} +5 -5
  57. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-BSFv1R4Z.js → _server.ts.js-BXzgi8xX.js} +5 -5
  58. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-I0dJrnyq.js → _server.ts.js-BfD5FXiL.js} +5 -5
  59. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-w_eF8BVI.js → _server.ts.js-CiDPC9TY.js} +5 -5
  60. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-Bvw_1JCx.js → _server.ts.js-DGcZgN96.js} +5 -5
  61. package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-DHsKo688.js → _server.ts.js-DnU1CCOw.js} +5 -5
  62. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-BTIrnyBK.js → _server.ts.js-BZ0UV1n2.js} +5 -5
  63. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file-bytes/{_server.ts.js-Deb3yET3.js → _server.ts.js-lxLpuPFL.js} +5 -5
  64. package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-DYifOFKl.js → _server.ts.js-Cw3vl704.js} +5 -5
  65. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-DW12qsWc.js → _server.ts.js-KKnR2UO4.js} +5 -5
  66. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-BeTfsMv4.js → _server.ts.js-I1eSZn60.js} +5 -5
  67. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-BOvj8cc1.js → _server.ts.js-B0zqAKnV.js} +5 -5
  68. package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-CcewTyD0.js → _server.ts.js-C-QN0enK.js} +10 -8
  69. package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-CkFYi6-s.js → _server.ts.js-CwTGUPIp.js} +16 -8
  70. package/build/server/chunks/entries/endpoints/api/prompts/use/_server.ts.js-Bvotxm37.js +45 -0
  71. package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-xkyh8Bsn.js → _server.ts.js-CRRLXk0y.js} +4 -4
  72. package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-C8h4skba.js → _server.ts.js-DY_NDlYl.js} +6 -6
  73. package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-WGYYK-PJ.js → _server.ts.js-DHEaymrG.js} +6 -6
  74. package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-CG64CyF8.js → _server.ts.js-AQgNkfCV.js} +6 -6
  75. package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-DLf26VXY.js → _server.ts.js-C9vxPbB3.js} +6 -6
  76. package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-Db2kgLys.js → _server.ts.js-BlMRR11v.js} +6 -6
  77. package/build/server/chunks/entries/{hooks.server.js-B7FsvqtT.js → hooks.server.js-B_E1wCwG.js} +3 -3
  78. package/build/server/chunks/entries/pages/{_error.svelte.js-BRTO_bKK.js → _error.svelte.js-DIRRk8AK.js} +4 -4
  79. package/build/server/chunks/entries/pages/{_page.server.ts.js-DFo2_s9d.js → _page.server.ts.js-ze2dUhXW.js} +2 -2
  80. package/build/server/chunks/entries/pages/{_page.svelte.js-T6ftg3_-.js → _page.svelte.js-CYT_NWww.js} +728 -492
  81. package/build/server/chunks/{handler-Xeib4Xb9.js → handler-jvQdYpdY.js} +3 -3
  82. package/build/server/chunks/{index.js-DDqgzbFD.js → index.js-BGLJR092.js} +2 -2
  83. package/build/server/chunks/{manifest.js-CmazPK88.js → manifest.js-CIb6Wm5j.js} +43 -43
  84. package/build/server/chunks/nodes/{0.js-DDgHSLf5.js → 0.js-CQ3k21rg.js} +3 -3
  85. package/build/server/chunks/nodes/1.js-NI6SZ6un.js +9 -0
  86. package/build/server/chunks/nodes/{2.js-BIByS7ug.js → 2.js-BG_HkTrm.js} +9 -9
  87. package/package.json +2 -1
  88. package/template/.dsi/prompts.sqlite +0 -0
  89. package/template/.dsi/prompts.sqlite-shm +0 -0
  90. package/template/.dsi/prompts.sqlite-wal +0 -0
  91. package/template/.dsi/settings.yaml +1 -1
  92. package/build/client/_app/immutable/assets/0.BQjO3ESU.css +0 -2
  93. package/build/client/_app/immutable/assets/2.DHY7FpOZ.css +0 -1
  94. package/build/client/_app/immutable/chunks/BuVjHIij.js +0 -1
  95. package/build/client/_app/immutable/chunks/C88qhAPJ.js +0 -1
  96. package/build/client/_app/immutable/chunks/Ct7xT-0E.js +0 -1
  97. package/build/client/_app/immutable/chunks/DFtKrqgo.js +0 -1
  98. package/build/client/_app/immutable/chunks/DQgaJgp5.js +0 -2
  99. package/build/client/_app/immutable/entry/start.CyefizPA.js +0 -1
  100. package/build/client/_app/immutable/nodes/2.Bu226K9e.js +0 -174
  101. package/build/server/chunks/chunks/app-config.svelte.js-BkhuJ9bF.js +0 -52
  102. package/build/server/chunks/entries/endpoints/api/prompts/use/_server.ts.js-yxG3ypD4.js +0 -55
  103. package/build/server/chunks/nodes/1.js-DleiYihI.js +0 -9
@@ -1 +1 @@
1
- {"version":"1789382806993"}
1
+ {"version":"1789481197834"}
package/build/handler.js CHANGED
@@ -2,9 +2,9 @@ import './shims.js';
2
2
  import 'node:fs';
3
3
  import 'node:path';
4
4
  import 'node:process';
5
- export { h as handler } from './server/chunks/handler-Xeib4Xb9.js';
6
- import './server/chunks/index.js-DDqgzbFD.js';
7
- import './server/chunks/manifest.js-CmazPK88.js';
5
+ export { h as handler } from './server/chunks/handler-jvQdYpdY.js';
6
+ import './server/chunks/index.js-BGLJR092.js';
7
+ import './server/chunks/manifest.js-CIb6Wm5j.js';
8
8
  import './env.js';
9
9
  import 'node:buffer';
10
10
  import 'node:crypto';
@@ -16,7 +16,7 @@ import './server/chunks/chunks/shared.js-HQ0rfhm4.js';
16
16
  import './server/chunks/chunks/uneval.js-BJ9Qq2SF.js';
17
17
  import './server/chunks/chunks/utils.js-CAdNO8Tu.js';
18
18
  import 'node:url';
19
- import './server/chunks/chunks/internal.js-Y8BDUVWO.js';
19
+ import './server/chunks/chunks/internal.js-BDS_h7zE.js';
20
20
  import './server/chunks/chunks/server.js-11-pxAWV.js';
21
21
  import './server/chunks/chunks/internal2.js-BcS47nI-.js';
22
22
  import './server/chunks/chunks/exports.js-DH_tjtdv.js';
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import http from 'node:http';
2
2
  import fs__default from 'node:fs';
3
3
  import process from 'node:process';
4
- import { p as polka, h as handler, f as format_listening_address } from './server/chunks/handler-Xeib4Xb9.js';
4
+ import { p as polka, h as handler, f as format_listening_address } from './server/chunks/handler-jvQdYpdY.js';
5
5
  import { env, timeout_env } from './env.js';
6
6
  import { rm } from 'node:fs/promises';
7
7
  import './shims.js';
@@ -14,12 +14,12 @@ import 'node:stream';
14
14
  import './server/chunks/chunks/shared.js-HQ0rfhm4.js';
15
15
  import './server/chunks/chunks/uneval.js-BJ9Qq2SF.js';
16
16
  import './server/chunks/chunks/utils.js-CAdNO8Tu.js';
17
- import './server/chunks/index.js-DDqgzbFD.js';
18
- import './server/chunks/chunks/internal.js-Y8BDUVWO.js';
17
+ import './server/chunks/index.js-BGLJR092.js';
18
+ import './server/chunks/chunks/internal.js-BDS_h7zE.js';
19
19
  import './server/chunks/chunks/server.js-11-pxAWV.js';
20
20
  import './server/chunks/chunks/internal2.js-BcS47nI-.js';
21
21
  import './server/chunks/chunks/exports.js-DH_tjtdv.js';
22
- import './server/chunks/manifest.js-CmazPK88.js';
22
+ import './server/chunks/manifest.js-CIb6Wm5j.js';
23
23
  import 'node:url';
24
24
 
25
25
  const path = env('SOCKET_PATH', false);
@@ -0,0 +1,305 @@
1
+ import { D as DEFAULT_PROMPT_TAGS, a as DEFAULT_SIDEBAR_PLACEMENT, b as DEFAULT_PANEL_MAX_ZOOM, c as DEFAULT_PANEL_MIN_ZOOM, d as DEFAULT_A2A_WATCH_TIMEOUT_MS, e as DEFAULT_A2A_FAST_POLL_MS, f as DEFAULT_HOME_REFRESH_MS, g as DEFAULT_STATS_BAR_MODE, h as DEFAULT_POLL_IDLE_MS, j as DEFAULT_ACCESS_CONFIRM_TIMEOUT_MS } from './config.js-iG_1bK3r.js';
2
+
3
+ //#region src/lib/services/chat/prompt-trigger.ts
4
+ /** Which mode a trigger query is in — the char after nothing: the FIRST
5
+ * character of the query string decides ("?load" → find, "!oci" → run).
6
+ * A non-trigger string answers 'find' (callers gate on getTrigger). */
7
+ function triggerMode(query) {
8
+ return query[0] === "!" ? "run" : "find";
9
+ }
10
+ /**
11
+ * Extract the finder trigger from input text + caret position.
12
+ * Active when the text before the caret starts with a trigger char
13
+ * ("?" find, "!" run — identical rules) and carries at least one more
14
+ * character, with no newline in the before-caret slice (the finder is
15
+ * single-line). Spaces are fine — multi-keyword queries are the
16
+ * ";"-and-whitespace AND search.
17
+ * Returns the query including the trigger char, or null when inactive.
18
+ */
19
+ function getTrigger(text, caret) {
20
+ const before = text.slice(0, caret);
21
+ const lead = before[0];
22
+ if (lead !== "?" && lead !== "!") return null;
23
+ if (before.length < 2) return null;
24
+ if (before.includes("\n")) return null;
25
+ return before;
26
+ }
27
+ /** Query-less search key sent to the server ("?load" → "load"; "!oci"
28
+ * → "oci" — the bang strips exactly like the question mark). */
29
+ function searchKey(query) {
30
+ return query.slice(1);
31
+ }
32
+ /**
33
+ * Split a search key into keyword terms (AND semantics): split on ";" and
34
+ * whitespace, trim, drop empties, dedupe case-insensitively (first
35
+ * spelling wins). Mirrors the server-side splitQueryTerms in prompts/db.ts
36
+ * — a shared unit test pins the parity (a single source would cross the
37
+ * server/client boundary). `?load;skill;feature-spec` → ["load", "skill",
38
+ * "feature-spec"].
39
+ */
40
+ function splitKey(key) {
41
+ const seen = /* @__PURE__ */ new Set();
42
+ const terms = [];
43
+ for (const frag of key.split(/[;\s]+/)) {
44
+ const raw = frag.trim();
45
+ if (!raw) continue;
46
+ const lower = raw.toLowerCase();
47
+ if (seen.has(lower)) continue;
48
+ seen.add(lower);
49
+ terms.push(raw);
50
+ }
51
+ return terms;
52
+ }
53
+ /** The tag grammar: 1..32 chars of lowercase letters, digits, dash, underscore. */
54
+ var TAG_PATTERN = /^[a-z0-9_-]{1,32}$/;
55
+ /** Is this (already-lowercase) word a legal tag? Callers normalize before asking. */
56
+ function isValidTagWord(word) {
57
+ return TAG_PATTERN.test(word);
58
+ }
59
+ /**
60
+ * Should this sent text be recorded (use-count upsert)? Plain prompts
61
+ * only — slash commands, @mentions, and "."-, "?"- or "!"-prefixed lines
62
+ * are control traffic and never touch the ledger.
63
+ */
64
+ function shouldRecordPrompt(text) {
65
+ const trimmed = text.trim();
66
+ if (!trimmed) return false;
67
+ if (trimmed.startsWith("/") || trimmed.startsWith("@") || trimmed.startsWith(".") || trimmed.startsWith("?") || trimmed.startsWith("!")) return false;
68
+ return true;
69
+ }
70
+ /**
71
+ * One-line preview for strip rows (ADR D8): first non-empty line of the
72
+ * label-or-text handle. Multiline rows preview their first line and carry
73
+ * the ⏎ badge in the strip instead of spilling newlines into one line.
74
+ */
75
+ function previewOf(row) {
76
+ return (row.label ?? row.text).split("\n").find((l) => l.trim().length > 0) ?? "";
77
+ }
78
+ /**
79
+ * First non-empty line of the row's TEXT only — the label is never
80
+ * consulted. For surfaces that render the label as its own element beside
81
+ * the preview (the manager's label tag + text preview) so the two never
82
+ * duplicate each other.
83
+ */
84
+ function textPreviewOf(row) {
85
+ return row.text.split("\n").find((l) => l.trim().length > 0) ?? "";
86
+ }
87
+ /** Similarity bar a fuzzy word must clear to be highlighted (below = noise). */
88
+ var WORD_HL_FLOOR = .5;
89
+ /**
90
+ * Split a strip preview into plain/highlighted segments (F4).
91
+ *
92
+ * - No query / not a "?" query → single plain previewOf segment.
93
+ * - "?" contains hit (S4) → first line containing ALL terms previews with
94
+ * EVERY term's span marked, longest-match-first (no overlapping spans);
95
+ * label first, then text lines, so a match on line 3 previews line 3.
96
+ * - "?" contains miss (S4) → per-term best-matching word highlighted when
97
+ * it clears 0.5 similarity ("lod" → "load" 0.75); weaker words stay
98
+ * plain.
99
+ *
100
+ * Matching is case-insensitive (parity with the server LIKE/FTS); segments
101
+ * render the row's original casing.
102
+ */
103
+ function previewSegments(row, query) {
104
+ const plain = [{
105
+ text: previewOf(row),
106
+ hit: false
107
+ }];
108
+ if (!query.startsWith("?")) return plain;
109
+ const key = searchKey(query).toLowerCase();
110
+ if (!key) return plain;
111
+ const lines = row.label !== null ? [row.label, ...row.text.split("\n")] : row.text.split("\n");
112
+ const terms = splitKey(key);
113
+ if (terms.length > 0) for (const line of lines) {
114
+ const spans = markTermSpans(line, terms);
115
+ if (spans) return segmentsFromSpans(line, spans);
116
+ }
117
+ const perTerm = terms.length > 0 ? terms : key.split(/\s+/).filter(Boolean);
118
+ let bestLine = null;
119
+ let bestScore = 0;
120
+ for (const line of lines) {
121
+ let lineScore = 0;
122
+ const spans = [];
123
+ for (const t of perTerm) {
124
+ let best = null;
125
+ let bestTermScore = 0;
126
+ for (const word of line.split(/\s+/)) {
127
+ if (!word) continue;
128
+ const score = wordSimilarity(t, word.toLowerCase());
129
+ if (score > bestTermScore) {
130
+ bestTermScore = score;
131
+ best = {
132
+ at: line.indexOf(word),
133
+ len: word.length
134
+ };
135
+ }
136
+ }
137
+ lineScore += bestTermScore;
138
+ if (best && bestTermScore >= WORD_HL_FLOOR) spans.push(best);
139
+ }
140
+ if (spans.length > 0 && lineScore > bestScore) {
141
+ bestScore = lineScore;
142
+ bestLine = {
143
+ line,
144
+ spans: resolveOverlaps(spans)
145
+ };
146
+ }
147
+ }
148
+ if (bestLine) return segmentsFromSpans(bestLine.line, bestLine.spans);
149
+ return plain;
150
+ }
151
+ /** Tier-1 span set for one line against every term: null when ANY term is
152
+ * missing (the line must contain ALL terms to preview — AND semantics);
153
+ * spans collected for every term, longest-first overlap-resolved. */
154
+ function markTermSpans(line, terms) {
155
+ const l = line.toLowerCase();
156
+ const spans = [];
157
+ for (const t of terms) {
158
+ const at = l.indexOf(t);
159
+ if (at === -1) return null;
160
+ spans.push({
161
+ at,
162
+ len: t.length
163
+ });
164
+ }
165
+ return resolveOverlaps(spans);
166
+ }
167
+ /** Overlap resolution (S4): longest-match-first — a longer term's span wins
168
+ * any overlap, shorter overlapping spans are dropped entirely (never
169
+ * nested-rendered). Non-overlapping spans pass through unchanged. */
170
+ function resolveOverlaps(spans) {
171
+ const sorted = [...spans].sort((a, b) => b.at + b.len - (a.at + a.len) || b.len - a.len || a.at - b.at);
172
+ const kept = [];
173
+ for (const span of sorted) {
174
+ if (kept.some((k) => span.at < k.at + k.len && k.at < span.at + span.len)) continue;
175
+ kept.push(span);
176
+ }
177
+ return kept.sort((a, b) => a.at - b.at);
178
+ }
179
+ /** Render a line as plain/hit segments over the given hit spans. Full-width
180
+ * rows: the whole line renders — no sliding window, no ellipses; CSS
181
+ * ellipsis on the label guards only the extreme case. */
182
+ function segmentsFromSpans(line, spans) {
183
+ const segments = [];
184
+ let cursor = 0;
185
+ for (const sp of spans) {
186
+ if (sp.at > cursor) segments.push({
187
+ text: line.slice(cursor, sp.at),
188
+ hit: false
189
+ });
190
+ segments.push({
191
+ text: line.slice(sp.at, sp.at + sp.len),
192
+ hit: true
193
+ });
194
+ cursor = sp.at + sp.len;
195
+ }
196
+ if (cursor < line.length) segments.push({
197
+ text: line.slice(cursor),
198
+ hit: false
199
+ });
200
+ return segments;
201
+ }
202
+ /**
203
+ * Trigram-bag Dice coefficient (Sørensen): 2·|A ∩ B| / (|A| + |B|) over
204
+ * multiset trigram counts. Returns 0 when either side has no trigrams
205
+ * (length < 3). Parity copy of the server's fuzzy math (prompts/db.ts) —
206
+ * the boundary keeps the two implementations separate; unit vectors pin
207
+ * them together.
208
+ */
209
+ function diceSimilarity(a, b) {
210
+ const grams = /* @__PURE__ */ new Map();
211
+ for (let i = 0; i + 3 <= a.length; i++) {
212
+ const g = a.slice(i, i + 3);
213
+ grams.set(g, (grams.get(g) ?? 0) + 1);
214
+ }
215
+ if (grams.size === 0) return 0;
216
+ let bCount = 0;
217
+ let shared = 0;
218
+ for (let i = 0; i + 3 <= b.length; i++) {
219
+ bCount++;
220
+ const g = b.slice(i, i + 3);
221
+ const have = grams.get(g) ?? 0;
222
+ if (have > 0) {
223
+ shared++;
224
+ if (have === 1) grams.delete(g);
225
+ else grams.set(g, have - 1);
226
+ }
227
+ }
228
+ if (bCount === 0) return 0;
229
+ const aCount = a.length - 2;
230
+ return 2 * shared / (aCount + bCount);
231
+ }
232
+ /**
233
+ * Word-level similarity: max of Dice and normalized edit distance — Dice is
234
+ * meaningless for words shorter than 3 trigrams/chars, edit covers them
235
+ * ("lod" vs "load": Dice 0, edit 0.75 → 0.75).
236
+ */
237
+ function wordSimilarity(a, b) {
238
+ const edit = 1 - levenshtein(a, b) / Math.max(a.length, b.length);
239
+ return Math.max(diceSimilarity(a, b), edit);
240
+ }
241
+ /** Wagner–Fischer edit distance (two-row DP), case handled by callers. */
242
+ function levenshtein(a, b) {
243
+ if (a === b) return 0;
244
+ if (!a.length) return b.length;
245
+ if (!b.length) return a.length;
246
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
247
+ for (let i = 1; i <= a.length; i++) {
248
+ const cur = [i];
249
+ for (let j = 1; j <= b.length; j++) cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
250
+ prev = cur;
251
+ }
252
+ return prev[b.length];
253
+ }
254
+ //#endregion
255
+ //#region src/lib/services/config/app-config.svelte.ts
256
+ function freshDefault() {
257
+ return {
258
+ chat: {
259
+ input: { maxRows: 15 },
260
+ accessConfirmTimeoutMs: DEFAULT_ACCESS_CONFIRM_TIMEOUT_MS,
261
+ macro: {
262
+ maxLines: 25,
263
+ maxDepth: 3
264
+ }
265
+ },
266
+ conversation: {
267
+ pollRunningMs: 500,
268
+ pollIdleMs: DEFAULT_POLL_IDLE_MS,
269
+ statsBar: DEFAULT_STATS_BAR_MODE,
270
+ collapsable: false,
271
+ progressiveFold: false
272
+ },
273
+ home: { refreshMs: DEFAULT_HOME_REFRESH_MS },
274
+ a2a: {
275
+ fastPollMs: DEFAULT_A2A_FAST_POLL_MS,
276
+ watchTimeoutMs: DEFAULT_A2A_WATCH_TIMEOUT_MS,
277
+ retentionDays: 90
278
+ },
279
+ panel: {
280
+ defaultWidth: 730,
281
+ minWidth: 480,
282
+ maxWidth: 860,
283
+ minZoom: DEFAULT_PANEL_MIN_ZOOM,
284
+ maxZoom: DEFAULT_PANEL_MAX_ZOOM
285
+ },
286
+ sidebar: {
287
+ defaultWidth: 280,
288
+ minWidth: 200,
289
+ maxWidth: 500,
290
+ placement: DEFAULT_SIDEBAR_PLACEMENT
291
+ },
292
+ prompts: { tags: [...DEFAULT_PROMPT_TAGS] }
293
+ };
294
+ }
295
+ var config = freshDefault();
296
+ /**
297
+ * Reactive config view. Read it inside an $effect/$derived — it updates
298
+ * when the load lands (e.g. the prompt clamp re-measures on arrival).
299
+ */
300
+ function appConfig() {
301
+ return config;
302
+ }
303
+
304
+ export { appConfig as a, triggerMode as b, shouldRecordPrompt as c, getTrigger as g, isValidTagWord as i, previewSegments as p, searchKey as s, textPreviewOf as t };
305
+ //# sourceMappingURL=app-config.svelte.js-rN4N5yAI.js.map
@@ -59,6 +59,20 @@ function resolveA2aConfig(section) {
59
59
  retentionDays: intWithin(rec.retentionDays, 1, 3650, 90)
60
60
  };
61
61
  }
62
+ /**
63
+ * `prompts.tags` default — the tag vocabulary offered by the save popup
64
+ * and the manager chip row (The Prompt Tags ADR, 2026-09-14, D5). The
65
+ * operator overrides the list in settings.yaml; the grammar gate itself
66
+ * lives with the server truth (prompts/tags.ts) and its client mirror
67
+ * (prompt-trigger.ts), so both readers here filter with their side's copy.
68
+ */
69
+ var DEFAULT_PROMPT_TAGS = [
70
+ "session",
71
+ "git",
72
+ "plan",
73
+ "rca",
74
+ "kb"
75
+ ];
62
76
  var DEFAULT_PANEL_MIN_ZOOM = .25;
63
77
  var DEFAULT_PANEL_MAX_ZOOM = 1.25;
64
78
  /** `sidebar.placement` default — the out-of-zoom rail (by-design layout). */
@@ -126,5 +140,5 @@ function isConfigRecord(v) {
126
140
  return typeof v === "object" && v !== null && !Array.isArray(v);
127
141
  }
128
142
 
129
- export { DEFAULT_SIDEBAR_PLACEMENT as D, UI_LOCALES as U, DEFAULT_PANEL_MAX_ZOOM as a, DEFAULT_PANEL_MIN_ZOOM as b, DEFAULT_A2A_WATCH_TIMEOUT_MS as c, DEFAULT_A2A_FAST_POLL_MS as d, DEFAULT_HOME_REFRESH_MS as e, DEFAULT_STATS_BAR_MODE as f, DEFAULT_POLL_IDLE_MS as g, DEFAULT_ACCESS_CONFIRM_TIMEOUT_MS as h, isConfigRecord as i, resolveSidebarPlacement as j, resolveWidthBounds as k, resolveZoomBounds as l, intWithin as m, resolveConversationFlags as n, resolveStatsBarMode as o, dshBaseUrl as p, resolveA2aConfig as r };
130
- //# sourceMappingURL=config.js-D2vQdial.js.map
143
+ export { DEFAULT_PROMPT_TAGS as D, UI_LOCALES as U, DEFAULT_SIDEBAR_PLACEMENT as a, DEFAULT_PANEL_MAX_ZOOM as b, DEFAULT_PANEL_MIN_ZOOM as c, DEFAULT_A2A_WATCH_TIMEOUT_MS as d, DEFAULT_A2A_FAST_POLL_MS as e, DEFAULT_HOME_REFRESH_MS as f, DEFAULT_STATS_BAR_MODE as g, DEFAULT_POLL_IDLE_MS as h, isConfigRecord as i, DEFAULT_ACCESS_CONFIRM_TIMEOUT_MS as j, resolveSidebarPlacement as k, resolveWidthBounds as l, resolveZoomBounds as m, intWithin as n, resolveConversationFlags as o, resolveStatsBarMode as p, dshBaseUrl as q, resolveA2aConfig as r };
144
+ //# sourceMappingURL=config.js-iG_1bK3r.js.map
@@ -1,4 +1,4 @@
1
- import { D as DSI_CONFIG_PATH } from './insight-config.js-BcJiCZlZ.js';
1
+ import { D as DSI_CONFIG_PATH } from './insight-config.js-Dw_Qi0fO.js';
2
2
  import { mkdirSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { DatabaseSync } from 'node:sqlite';
@@ -117,4 +117,4 @@ function pruneTerminalBefore(cutoffMs, db = openA2aDb()) {
117
117
  }
118
118
 
119
119
  export { listRecent as a, getRow as g, insertWaiting as i, listWaiting as l, openA2aDb as o, pruneTerminalBefore as p, settleRow as s };
120
- //# sourceMappingURL=db.js-B7zd2qLf.js.map
120
+ //# sourceMappingURL=db.js-DbbYS9o8.js.map
@@ -1,4 +1,4 @@
1
- import { j as readPromptsConfig, D as DSI_CONFIG_PATH } from './insight-config.js-BcJiCZlZ.js';
1
+ import { k as joinTagWords, p as parseTagWords, d as readPromptsConfig, D as DSI_CONFIG_PATH } from './insight-config.js-Dw_Qi0fO.js';
2
2
  import { mkdirSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { DatabaseSync } from 'node:sqlite';
@@ -72,6 +72,7 @@ CREATE TABLE IF NOT EXISTS prompts (
72
72
  macro INTEGER NOT NULL DEFAULT 0,
73
73
  last_used_at TEXT NOT NULL DEFAULT (datetime('now')),
74
74
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
75
+ tags TEXT NOT NULL DEFAULT '',
75
76
  UNIQUE(text)
76
77
  );
77
78
  CREATE INDEX IF NOT EXISTS idx_prompts_text ON prompts(text);
@@ -108,8 +109,11 @@ END;
108
109
  * v0: original table — text/use_count/last_used_at only (label-less).
109
110
  * v1: adds label, created_at, prune index, UNIQUE(text) upsert semantics.
110
111
  * v2: adds prompts_fts trigram index + sync triggers (ADR v1.2 F3).
111
- * v3: adds the macro flag column (0 = ordinary, 1 = macro). */
112
- var SCHEMA_VERSION = 3;
112
+ * v3: adds the macro flag column (0 = ordinary, 1 = macro).
113
+ * v4: appends the tags column (The Prompt Tags ADR, 2026-09-14, D2) —
114
+ * tags have no column-order constraint, so migration is the cheap
115
+ * ALTER + filter index, never the v3 prompts_new rebuild. */
116
+ var SCHEMA_VERSION = 4;
113
117
  var defaultDb = null;
114
118
  /**
115
119
  * Open (creating if needed) the library DB and ensure schema. Idempotent:
@@ -133,6 +137,8 @@ function openPromptsDb(path) {
133
137
  try {
134
138
  mkdirSync(dirname(file), { recursive: true });
135
139
  const db = new DatabaseSync(file);
140
+ db.exec("PRAGMA busy_timeout = 3000");
141
+ db.exec("PRAGMA journal_mode = WAL");
136
142
  db.exec(SCHEMA);
137
143
  migrate(db);
138
144
  if (path === void 0) defaultDb = db;
@@ -174,7 +180,7 @@ function migrate(db) {
174
180
  db.exec("INSERT INTO prompts_fts(rowid, label, text) SELECT id, label, text FROM prompts");
175
181
  }
176
182
  if (current < 3) {
177
- const want = [
183
+ const want7 = [
178
184
  "id",
179
185
  "label",
180
186
  "text",
@@ -184,6 +190,7 @@ function migrate(db) {
184
190
  "created_at"
185
191
  ];
186
192
  let cols = db.prepare("PRAGMA table_info(prompts)").all().map((c) => c.name);
193
+ const want = cols.includes("tags") ? [...want7, "tags"] : want7;
187
194
  if (!cols.includes("macro")) {
188
195
  db.exec("ALTER TABLE prompts ADD COLUMN macro INTEGER NOT NULL DEFAULT 0");
189
196
  cols = db.prepare("PRAGMA table_info(prompts)").all().map((c) => c.name);
@@ -220,46 +227,39 @@ function migrate(db) {
220
227
  db.exec("INSERT INTO prompts_fts(rowid, label, text) SELECT id, label, text FROM prompts");
221
228
  }
222
229
  }
230
+ if (current < 4) {
231
+ if (!db.prepare("PRAGMA table_info(prompts)").all().map((c) => c.name).includes("tags")) db.exec("ALTER TABLE prompts ADD COLUMN tags TEXT NOT NULL DEFAULT ''");
232
+ db.exec("CREATE INDEX IF NOT EXISTS idx_prompts_tags ON prompts(tags)");
233
+ }
223
234
  db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_prompts_label ON prompts(label) WHERE label IS NOT NULL");
224
235
  db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);
225
236
  } catch (e) {
226
237
  warn("migration failed:", e);
227
238
  }
228
239
  }
229
- var SELECT_COLS = "id, label, text, use_count, macro, last_used_at, created_at";
230
- /** Find a prompt by exact (trimmed) text. Null on miss/failure. */
231
- function findPromptByText(text) {
232
- const trimmed = text.trim();
233
- if (!trimmed) return null;
234
- const db = openPromptsDb();
235
- if (!db) return null;
236
- try {
237
- return db.prepare(`SELECT ${SELECT_COLS} FROM prompts WHERE text = ?`).get(trimmed) ?? null;
238
- } catch (e) {
239
- warn("findPromptByText failed:", e);
240
- return null;
241
- }
242
- }
240
+ var SELECT_COLS = "id, label, text, use_count, macro, last_used_at, created_at, tags";
243
241
  /**
244
- * Record a sent prompt (ADR D5/D15): insert on first send, increment
245
- * use_count + refresh last_used_at afterwards. Enforces the row cap.
246
- * Returns the updated record, or null on failure (DB unavailable/empty text).
242
+ * Count-only use recording (The Prompt Tags ADR, 2026-09-14, D7): bump
243
+ * use_count + refresh last_used_at for an EXISTING row; unknown text
244
+ * returns { counted: false } and inserts NOTHING — the wire's only path
245
+ * to a row is an operator save. Null on failure (DB unavailable/empty text).
247
246
  */
248
- function recordPromptUse(text) {
247
+ function countPromptUse(text) {
249
248
  const trimmed = text.trim();
250
249
  if (!trimmed) return null;
251
250
  const db = openPromptsDb();
252
251
  if (!db) return null;
253
252
  try {
254
- db.prepare(`INSERT INTO prompts (label, text, use_count, last_used_at, created_at)
255
- VALUES (NULL, ?, 1, datetime('now'), datetime('now'))
256
- ON CONFLICT(text) DO UPDATE SET
257
- use_count = use_count + 1,
258
- last_used_at = datetime('now')`).run(trimmed);
259
- pruneIfNeeded(db);
260
- return db.prepare(`SELECT ${SELECT_COLS} FROM prompts WHERE text = ?`).get(trimmed) ?? null;
253
+ if (db.prepare(`UPDATE prompts
254
+ SET use_count = use_count + 1, last_used_at = datetime('now')
255
+ WHERE text = ?`).run(trimmed).changes === 0) return { counted: false };
256
+ const row = db.prepare(`SELECT ${SELECT_COLS} FROM prompts WHERE text = ?`).get(trimmed);
257
+ return row ? {
258
+ counted: true,
259
+ record: row
260
+ } : null;
261
261
  } catch (e) {
262
- warn("recordPromptUse failed:", e);
262
+ warn("countPromptUse failed:", e);
263
263
  return null;
264
264
  }
265
265
  }
@@ -659,21 +659,27 @@ function listPrompts(opts) {
659
659
  const dir = opts.dir === "asc" ? "asc" : "desc";
660
660
  const orderBy = SORT_MAP[sortKey][dir];
661
661
  const q = opts.q?.trim();
662
+ const tagWords = opts.tags ?? [];
663
+ const tagConds = tagWords.map(() => "(' ' || tags || ' ') LIKE ? ESCAPE '\\'");
664
+ const tagParams = tagWords.map((t) => `% ${escLike(t)} %`);
665
+ const qCond = "(label LIKE ? ESCAPE '\\' OR text LIKE ? ESCAPE '\\')";
662
666
  try {
663
667
  if (q) {
664
668
  const like = `${escLike(q)}%`;
669
+ const where = [qCond, ...tagConds].join(" AND ");
665
670
  return {
666
671
  rows: db.prepare(`SELECT ${SELECT_COLS} FROM prompts
667
- WHERE label LIKE ? ESCAPE '\\' OR text LIKE ? ESCAPE '\\'
672
+ WHERE ${where}
668
673
  ORDER BY ${orderBy}
669
- LIMIT ? OFFSET ?`).all(like, like, limit, offset),
670
- total: db.prepare(`SELECT COUNT(*) AS n FROM prompts
671
- WHERE label LIKE ? ESCAPE '\\' OR text LIKE ? ESCAPE '\\'`).get(like, like).n
674
+ LIMIT ? OFFSET ?`).all(like, like, ...tagParams, limit, offset),
675
+ total: db.prepare(`SELECT COUNT(*) AS n FROM prompts WHERE ${where}`).get(like, like, ...tagParams).n
672
676
  };
673
677
  }
678
+ const where = tagConds.join(" AND ");
679
+ const whereSql = where ? ` WHERE ${where}` : "";
674
680
  return {
675
- rows: db.prepare(`SELECT ${SELECT_COLS} FROM prompts ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(limit, offset),
676
- total: db.prepare("SELECT COUNT(*) AS n FROM prompts").get().n
681
+ rows: db.prepare(`SELECT ${SELECT_COLS} FROM prompts${whereSql} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...tagParams, limit, offset),
682
+ total: db.prepare(`SELECT COUNT(*) AS n FROM prompts${whereSql}`).get(...tagParams).n
677
683
  };
678
684
  } catch (e) {
679
685
  warn("listPrompts failed:", e);
@@ -688,7 +694,7 @@ function listPrompts(opts) {
688
694
  * existing row as a 409-info object instead of inserting. Manual create ≠
689
695
  * upsert — never silently bumps use_count.
690
696
  */
691
- function createPrompt(text, label) {
697
+ function createPrompt(text, label, tags) {
692
698
  const trimmed = text.trim();
693
699
  if (!trimmed) return {
694
700
  ok: false,
@@ -706,8 +712,9 @@ function createPrompt(text, label) {
706
712
  existing
707
713
  };
708
714
  const cleanLabel = label ? label.trim().slice(0, 60) || null : null;
709
- db.prepare(`INSERT INTO prompts (label, text, use_count, last_used_at, created_at)
710
- VALUES (?, ?, 1, datetime('now'), datetime('now'))`).run(cleanLabel, trimmed);
715
+ const cleanTags = joinTagWords(tags ?? []);
716
+ db.prepare(`INSERT INTO prompts (label, text, use_count, last_used_at, created_at, tags)
717
+ VALUES (?, ?, 1, datetime('now'), datetime('now'), ?)`).run(cleanLabel, trimmed, cleanTags);
711
718
  pruneIfNeeded(db);
712
719
  return {
713
720
  ok: true,
@@ -750,6 +757,10 @@ function updatePrompt(id, fields) {
750
757
  sets.push("macro = ?");
751
758
  args.push(fields.macro ? 1 : 0);
752
759
  }
760
+ if (fields.tags !== void 0) {
761
+ sets.push("tags = ?");
762
+ args.push(joinTagWords(parseTagWords(fields.tags)));
763
+ }
753
764
  if (sets.length === 0) return null;
754
765
  args.push(id);
755
766
  if (db.prepare(`UPDATE prompts SET ${sets.join(", ")} WHERE id = ?`).run(...args).changes === 0) return null;
@@ -760,5 +771,5 @@ function updatePrompt(id, fields) {
760
771
  }
761
772
  }
762
773
 
763
- export { renamePromptLabel as a, createPrompt as c, deletePrompt as d, findPromptByText as f, listPrompts as l, recordPromptUse as r, searchPrompts as s, updatePrompt as u };
764
- //# sourceMappingURL=db2.js-CCmqoCC8.js.map
774
+ export { countPromptUse as a, createPrompt as c, deletePrompt as d, listPrompts as l, renamePromptLabel as r, searchPrompts as s, updatePrompt as u };
775
+ //# sourceMappingURL=db2.js-BQWa7bEG.js.map
@@ -1,5 +1,5 @@
1
- import { p as dshBaseUrl } from './config.js-D2vQdial.js';
2
- import { r as readDshPresetEnglishConfig, a as readDshAuthConfig, b as readServerConfig } from './insight-config.js-BcJiCZlZ.js';
1
+ import { q as dshBaseUrl } from './config.js-iG_1bK3r.js';
2
+ import { r as readDshPresetEnglishConfig, a as readDshAuthConfig, b as readServerConfig } from './insight-config.js-Dw_Qi0fO.js';
3
3
 
4
4
  //#region src/lib/server/dsh-rpc.ts
5
5
  /**
@@ -2013,4 +2013,4 @@ function normalizePreset(row, overlayEnglish = true) {
2013
2013
  }
2014
2014
 
2015
2015
  export { DshRpcError as D, isPromptImageMediaType as a, buildPromptContent as b, getDshConnection as g, isSubagentRejection as i, mapRpcFailure as m, statusFor as s };
2016
- //# sourceMappingURL=dsh-connection.js-qRBqNOg4.js.map
2016
+ //# sourceMappingURL=dsh-connection.js-CpcCtA42.js.map
@@ -1,4 +1,4 @@
1
- import { g as getDshConnection } from './dsh-connection.js-qRBqNOg4.js';
1
+ import { g as getDshConnection } from './dsh-connection.js-CpcCtA42.js';
2
2
  import { h as historyToEvents } from './dsh-events.js-BtDxaV6Q.js';
3
3
  import { a as foldKnobs, f as filterKnobEvents, F as FULL_ACCESS_PRESET } from './permission-state.js-yvwUSnug.js';
4
4
  import { promises } from 'node:fs';
@@ -216,4 +216,4 @@ async function gitStatus(repo) {
216
216
  }
217
217
 
218
218
  export { GitUnavailableError as G, gitStatus as a, gitFileHead as b, detectRepos as d, gitGateEnabled as g, isInsideRoot as i, parsePorcelainStatus as p, resolveEnclosingRepo as r };
219
- //# sourceMappingURL=git-probe.js-Dw7WYQIb.js.map
219
+ //# sourceMappingURL=git-probe.js-CvkToKO4.js.map