@deepseek-insight/dsi 0.6.9 → 0.6.10

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 (44) hide show
  1. package/.agents/skills/dsi-plugin-rack/rack.d.mts +35 -0
  2. package/.agents/skills/dsi-plugin-rack/rack.mjs +168 -26
  3. package/build/client/_app/immutable/assets/{2.BGqV97Jd.css → 2.BuaCdYrs.css} +1 -1
  4. package/build/client/_app/immutable/chunks/B7sQH3rV.js +1 -0
  5. package/build/client/_app/immutable/chunks/{UkmEG9Ws.js → Dg3LIDOQ.js} +1 -1
  6. package/build/client/_app/immutable/chunks/XOquLDe4.js +2 -0
  7. package/build/client/_app/immutable/chunks/ivdzpE2c.js +1 -0
  8. package/build/client/_app/immutable/entry/{app.e7xoYywe.js → app.BTSQLfJD.js} +2 -2
  9. package/build/client/_app/immutable/entry/start.DMTgnn1w.js +1 -0
  10. package/build/client/_app/immutable/nodes/{1.E4wiL8sP.js → 1.Bj5dhQm8.js} +1 -1
  11. package/build/client/_app/immutable/nodes/{2.BBV-AGuB.js → 2.D2grr0LI.js} +195 -195
  12. package/build/client/_app/version.json +1 -1
  13. package/build/handler.js +4 -4
  14. package/build/index.js +4 -4
  15. package/build/server/chunks/chunks/{engine.js-CrdWRSwf.js → engine.js-1gYqtdBP.js} +17 -2
  16. package/build/server/chunks/chunks/{internal.js-BnJXHc1S.js → internal.js-CiymEM9G.js} +2 -2
  17. package/build/server/chunks/chunks/{state.js-CAjHzrRZ.js → state.js-Dve2UFKe.js} +2 -2
  18. package/build/server/chunks/entries/endpoints/api/plugins/apply/{_server.ts.js-CpO--Jwf.js → _server.ts.js-BNEUWbaf.js} +2 -2
  19. package/build/server/chunks/entries/endpoints/api/plugins/reload/{_server.ts.js-BACFXWMj.js → _server.ts.js-Ct28d7_t.js} +2 -2
  20. package/build/server/chunks/entries/endpoints/api/plugins/snapshot/{_server.ts.js-BJg0hLlC.js → _server.ts.js-DswGIO4S.js} +2 -2
  21. package/build/server/chunks/entries/pages/{_error.svelte.js-BXeIuPj-.js → _error.svelte.js-DUc94aLg.js} +3 -3
  22. package/build/server/chunks/entries/pages/{_page.svelte.js-CW-EdJ00.js → _page.svelte.js-CGKKJjvq.js} +154 -6
  23. package/build/server/chunks/{handler-DimUSmTW.js → handler-DndMFIB-.js} +3 -3
  24. package/build/server/chunks/{index.js-BQUp4Xgn.js → index.js-XCtYQ9iw.js} +2 -2
  25. package/build/server/chunks/{manifest.js-BWy6zgHL.js → manifest.js-B6QH7eda.js} +7 -7
  26. package/build/server/chunks/nodes/1.js-DgkiAL8m.js +9 -0
  27. package/build/server/chunks/nodes/{2.js-CzgP_zp4.js → 2.js-DiZQ86Is.js} +4 -4
  28. package/package.json +1 -1
  29. package/skills/dsi-add-plugin/SKILL.md +53 -0
  30. package/skills/dsi-add-plugin/references/allowbuilds-template.md +55 -0
  31. package/skills/dsi-add-plugin/references/checklist.md +98 -0
  32. package/skills/dsi-add-plugin/references/verification.md +67 -0
  33. package/template/.dsi/resources/plugins-reff.md +4 -2
  34. package/build/client/_app/immutable/chunks/C1F3aCiz.js +0 -2
  35. package/build/client/_app/immutable/chunks/CmnxnK6v.js +0 -1
  36. package/build/client/_app/immutable/chunks/jugX_o5s.js +0 -1
  37. package/build/client/_app/immutable/entry/start.CnkkMjJa.js +0 -1
  38. package/build/server/chunks/nodes/1.js-DJ3dsgdq.js +0 -9
  39. package/template/dsh-app-dev-preset/README.md +0 -69
  40. package/template/dsh-app-dev-preset/cordis.patch.yml +0 -219
  41. package/template/dsh-app-dev-preset/package.json +0 -7
  42. package/template/dsh-research-preset/README.md +0 -57
  43. package/template/dsh-research-preset/cordis.patch.yml +0 -118
  44. package/template/dsh-research-preset/package.json +0 -7
@@ -0,0 +1,35 @@
1
+ /** Type surface of the rack engine (rack.mjs) for test files. */
2
+ declare const CACHE_VERSION: 2;
3
+ export interface RackRow {
4
+ n: string;
5
+ id: string;
6
+ group?: 'owned' | 'external';
7
+ repo: string;
8
+ author?: string | null;
9
+ authorUrl?: string | null;
10
+ version?: string | null;
11
+ description?: string | null;
12
+ order?: number;
13
+ installSpec?: string;
14
+ installed: boolean;
15
+ pkg: string | null;
16
+ bundle: string | null;
17
+ }
18
+ export interface GardenRow {
19
+ id: string;
20
+ group: 'owned';
21
+ version: string | null;
22
+ description: string | null;
23
+ order: number;
24
+ installSpec: string;
25
+ repo: string;
26
+ }
27
+ export function scanGarden(gardenPath: string | undefined | null): GardenRow[];
28
+ export function parseReff(text: string): { plugins: { id: string; repo: string; author: string | null; authorUrl: string | null }[]; warnings: { code: string; detail: string }[] };
29
+ export function findDepKey(manifest: { deps: Record<string, string> }, id: string, repo?: string): string | null;
30
+ export function readManifest(path: string): { deps: Record<string, string>; bundles: string[]; missing: boolean };
31
+ export function isInstalled(manifest: { deps: Record<string, string> }, id: string, repo?: string): boolean;
32
+ export function buildSnapshot(args: { reffPath?: string; manifestPath?: string; gardenPath?: string; profile?: string }): { snapshot: { v: number; generatedAt: string; profile: string; plugins: RackRow[] }; warnings: { code: string; detail: string }[] };
33
+ export function toInstallSpec(repo: string): string;
34
+ export function installOwned(manifestPath: string, row: { id: string; installSpec: string }): string;
35
+ export function restoreClobberedBundles(manifestPath: string, preOwnedRows: { id: string; installSpec?: string; pkg?: string | null }[]): boolean;
@@ -14,12 +14,12 @@
14
14
  * dependencies[id] AND dsh.profile.bundles includes id. Gates surface, they
15
15
  * never auto-clear (D6).
16
16
  */
17
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
17
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
18
18
  import { homedir } from 'node:os';
19
19
  import { dirname, join, resolve } from 'node:path';
20
20
  import { spawnSync } from 'node:child_process';
21
21
 
22
- const CACHE_VERSION = 1;
22
+ export const CACHE_VERSION = 2;
23
23
 
24
24
  function parseArgs(argv) {
25
25
  const out = { command: argv[0], reload: false, reff: null, cache: null, manifest: null, profile: process.env.DSH_PROFILE ?? 'web', dshCmd: null, targets: [] };
@@ -30,6 +30,8 @@ function parseArgs(argv) {
30
30
  else if (a === '--cache') out.cache = argv[++i];
31
31
  else if (a === '--manifest') out.manifest = argv[++i];
32
32
  else if (a === '--profile') out.profile = argv[++i];
33
+ else if (a === '--garden') out.garden = argv[++i];
34
+ else if (a === '--garden-repo') out.gardenRepo = argv[++i];
33
35
  else if (a === '--dsh-cmd') out.dshCmd = argv[++i];
34
36
  else out.targets.push(a);
35
37
  }
@@ -54,6 +56,58 @@ export function parseReff(text) {
54
56
  return { plugins, warnings };
55
57
  }
56
58
 
59
+
60
+ /** Owned-species enumeration (ADR "The Plugin Garden" 2026-10-07, D1/D2):
61
+ * every direct child of the garden dir with a package.json wiring
62
+ * dsh.bundle.patch is one owned rack row, identified by its OWN package
63
+ * name. Rows carry version/description from the same file and an optional
64
+ * dsh.rack.order for display sort. Returns [] for a missing garden. */
65
+ export function scanGarden(gardenPath) {
66
+ if (!gardenPath || !existsSync(gardenPath)) return [];
67
+ const rows = [];
68
+ for (const child of readdirSync(gardenPath, { withFileTypes: true })) {
69
+ if (!child.isDirectory()) continue;
70
+ const pkgPath = join(gardenPath, child.name, 'package.json');
71
+ if (!existsSync(pkgPath)) continue;
72
+ let pkg;
73
+ try { pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); } catch { continue; }
74
+ const patch = pkg && pkg.dsh && pkg.dsh.bundle && pkg.dsh.bundle.patch;
75
+ if (!patch) continue;
76
+ const order = (pkg.dsh && pkg.dsh.rack && pkg.dsh.rack.order) ?? Number.POSITIVE_INFINITY;
77
+ rows.push({
78
+ id: pkg.name,
79
+ group: 'owned',
80
+ version: pkg.version ?? null,
81
+ description: pkg.description ?? null,
82
+ order,
83
+ installSpec: 'link:' + join(gardenPath, child.name),
84
+ repo: 'link:' + join(gardenPath, child.name)
85
+ });
86
+ }
87
+ return rows.sort((a, b) => (a.order - b.order) || a.id.localeCompare(b.id));
88
+ }
89
+
90
+ /** Repository identity (ADR-0016 D1): a catalog entry identifies a
91
+ * REPOSITORY, not a URL string. Strip protocol, .git suffix, trailing
92
+ * slashes and any /tree/<ref>(/...)? browse subtree, then keep the
93
+ * owner/repo tail. Two URLs with the same identity are one rack group. */
94
+ export function repoIdentity(url) {
95
+ const s = String(url || '')
96
+ .replace(/^[a-z+]+:(\/\/)?/i, '') // protocol (https://, git+, github:)
97
+ .split('/tree/')[0] // a github /tree/<ref>/... browse URL is a view INSIDE a repo
98
+ .replace(/\/+$/, '')
99
+ .replace(/\.git$/i, '')
100
+ .replace(/\/+$/, '');
101
+ return s.split('/').slice(-2).join('/');
102
+ }
103
+
104
+ /** Shared tail helper: last two path segments. findDepKey keeps a residual
105
+ * subtree (deps may point at subfolders); repoIdentity cuts it entirely. */
106
+ function repoTail(url) {
107
+ const s = String(url || '').replace(/\.git$/i, '').replace(/\/+$/, '');
108
+ return s.split('/').slice(-2).join('/');
109
+ }
110
+
57
111
  /** D4 (amended 2026-09-27): the manifest's DEPENDENCIES are the install
58
112
  * authority. pnpm records a git plugin under the package's REAL name from
59
113
  * its own package.json (e.g. '@temoa/dsh-rules-paths'), not the reff id -
@@ -64,8 +118,11 @@ export function parseReff(text) {
64
118
  * install gate. Returns the recorded dep key (the remove target) or null. */
65
119
  export function findDepKey(manifest, id, repo) {
66
120
  if (manifest.deps[id] !== undefined) return id;
67
- const tail = String(repo || '').replace(/\\.git$/, '').replace(/\/+$/, '').split('/').slice(-2).join('/');
68
- if (!tail) return null;
121
+ // A github /tree/<ref>/ browse URL is the same repo, not a distinct plugin
122
+ // (identity rule: repoIdentity, ADR-0016 D1); a residual subtree segment is
123
+ // kept here because deps may legitimately point at subfolders.
124
+ const tail = repoTail(String(repo || '').replace(/\/tree\/[^/]+(\/|$)/, '/'));
125
+ if (!tail || tail === '/') return null;
69
126
  for (const [key, value] of Object.entries(manifest.deps)) {
70
127
  if (typeof value === 'string' && value.includes(tail)) return key;
71
128
  }
@@ -89,39 +146,71 @@ export function isInstalled(manifest, id, repo) {
89
146
  return findDepKey(manifest, id, repo) !== null;
90
147
  }
91
148
 
92
- export function buildSnapshot({ reffPath, manifestPath, profile }) {
149
+ export function buildSnapshot({ reffPath, manifestPath, gardenPath, gardenRepo, profile }) {
93
150
  const warnings = [];
94
- if (!existsSync(reffPath)) warnings.push({ code: 'reff-missing', detail: reffPath });
95
- const parsed = existsSync(reffPath) ? parseReff(readFileSync(reffPath, 'utf8')) : { plugins: [], warnings: [] };
96
- warnings.push(...parsed.warnings);
97
151
  const manifest = readManifest(manifestPath);
98
152
  if (manifest.missing) warnings.push({ code: 'manifest-missing', detail: manifestPath });
153
+ const reconcile = (row) => {
154
+ const depKey = findDepKey(manifest, row.id, row.repo);
155
+ return { ...row, installed: depKey !== null, pkg: depKey, bundle: (depKey !== null && manifest.bundles.includes(depKey)) || null };
156
+ };
157
+ // Owned species first (garden scan), then external (reff) - flat rows,
158
+ // renumbered once; sources[] groups them by repo (shelf grammar).
159
+ const owned = scanGarden(gardenPath).map((row) => reconcile({ ...row, repo: gardenRepo ?? row.repo }));
160
+ const parsed = existsSync(reffPath) ? parseReff(readFileSync(reffPath, 'utf8')) : { plugins: [], warnings: [] };
161
+ warnings.push(...parsed.warnings);
162
+ // ADR-0016 D2/D3: an external reff row whose repository the garden already
163
+ // enumerates is SHADOWED — dropped here with a warning; the reff file itself
164
+ // is never rewritten and parseReff output is untouched.
165
+ const ownedIdents = new Set(owned.map((row) => repoIdentity(row.repo)));
166
+ const external = [];
167
+ for (const p of parsed.plugins) {
168
+ if (ownedIdents.has(repoIdentity(p.repo))) {
169
+ warnings.push({ code: 'reff-shadowed-by-garden', detail: p.id });
170
+ continue;
171
+ }
172
+ external.push(reconcile({
173
+ n: null,
174
+ id: p.id,
175
+ group: 'external',
176
+ repo: p.repo,
177
+ version: null,
178
+ description: null,
179
+ author: p.author,
180
+ ...(p.authorUrl ? { authorUrl: p.authorUrl } : {})
181
+ }));
182
+ }
183
+ const plugins = [...owned, ...external].map((row, i) => ({ ...row, n: String(i + 1) }));
184
+ // sources[]: one collapsible group per REPOSITORY (Shelf Chrome grammar),
185
+ // keyed by repoIdentity (ADR-0016 D2) so /tree browse URLs never split a repo.
186
+ const sources = [];
187
+ const byRepo = new Map();
188
+ for (const row of plugins) {
189
+ const key = repoIdentity(row.repo);
190
+ if (!byRepo.has(key)) {
191
+ sources.push({
192
+ id: key,
193
+ name: key.split('/').pop(),
194
+ author: row.author ?? null,
195
+ repo: row.repo,
196
+ plugins: []
197
+ });
198
+ byRepo.set(key, sources[sources.length - 1]);
199
+ }
200
+ byRepo.get(key).plugins.push(row);
201
+ }
99
202
  return {
100
203
  snapshot: {
101
204
  v: CACHE_VERSION,
102
205
  generatedAt: new Date().toISOString(),
103
206
  profile,
104
- plugins: parsed.plugins.map((p, i) => {
105
- const depKey = findDepKey(manifest, p.id, p.repo);
106
- return {
107
- n: String(i + 1),
108
- id: p.id,
109
- repo: p.repo,
110
- author: p.author,
111
- ...(p.authorUrl ? { authorUrl: p.authorUrl } : {}),
112
- installed: depKey !== null,
113
- // The recorded dependency name (pnpm's truth) - the remove target.
114
- pkg: depKey,
115
- bundle: (depKey !== null && manifest.bundles.includes(depKey)) || null
116
- };
117
- })
207
+ sources,
208
+ plugins
118
209
  },
119
210
  warnings
120
211
  };
121
212
  }
122
213
 
123
- /** D3: the reff URL becomes the dsh add spec: git+<url>.git (append .git
124
- * only when absent — the operator's line stays the single source). */
125
214
  export function toInstallSpec(repo) {
126
215
  return 'git+' + repo + (repo.endsWith('.git') ? '' : '.git');
127
216
  }
@@ -132,6 +221,47 @@ function resolveDshCommand(flags) {
132
221
  return 'npx --yes @deepseek-ai/dsh@latest';
133
222
  }
134
223
 
224
+
225
+ /** Owned-species install (Plugin Garden ADR, D3): write the link dependency,
226
+ * APPEND (never rewrite) the bundle entry, idempotent on re-run. The only
227
+ * manifest write the rack performs itself - scoped to @local link species;
228
+ * git/npm species keep the dsh add delegation (D3). Returns the dep key. */
229
+ export function installOwned(manifestPath, row) {
230
+ const doc = JSON.parse(readFileSync(manifestPath, 'utf8'));
231
+ doc.dependencies = doc.dependencies ?? {};
232
+ doc.dependencies[row.id] = row.installSpec;
233
+ const bundles = doc.dsh && doc.dsh.profile && Array.isArray(doc.dsh.profile.bundles) ? doc.dsh.profile.bundles : [];
234
+ if (!bundles.includes(row.id)) bundles.push(row.id);
235
+ if (doc.dsh && doc.dsh.profile) doc.dsh.profile.bundles = bundles;
236
+ writeFileSync(manifestPath, JSON.stringify(doc, null, 2));
237
+ return row.id;
238
+ }
239
+
240
+ /** Clobber guard (Plugin Garden ADR, 2026-10-07): a dsh plugin add rewrites
241
+ * the bundles array to just the new plugin and may drop earlier deps.
242
+ * Re-append every still-installed owned row's bundle entry and dep entry.
243
+ * Rows already recorded pass through untouched. */
244
+ export function restoreClobberedBundles(manifestPath, preOwnedRows) {
245
+ const doc = JSON.parse(readFileSync(manifestPath, 'utf8'));
246
+ let changed = false;
247
+ doc.dependencies = doc.dependencies ?? {};
248
+ const bundles = doc.dsh && doc.dsh.profile && Array.isArray(doc.dsh.profile.bundles) ? doc.dsh.profile.bundles : [];
249
+ for (const row of preOwnedRows) {
250
+ if (!row.pkg && !row.installSpec) continue;
251
+ if (doc.dependencies[row.id] === undefined) {
252
+ doc.dependencies[row.id] = row.installSpec;
253
+ changed = true;
254
+ }
255
+ if (!bundles.includes(row.id)) {
256
+ bundles.push(row.id);
257
+ changed = true;
258
+ }
259
+ }
260
+ if (doc.dsh && doc.dsh.profile) doc.dsh.profile.bundles = bundles;
261
+ if (changed) writeFileSync(manifestPath, JSON.stringify(doc, null, 2));
262
+ return changed;
263
+ }
264
+
135
265
  /** D3/D6: run dsh, relay its own diagnostics verbatim on failure. */
136
266
  export function runDsh(flags, action, spec) {
137
267
  const cmd = resolveDshCommand(flags) + ' plugin --profile ' + flags.profile + ' ' + action + ' ' + spec;
@@ -166,7 +296,7 @@ function main() {
166
296
  console.log(JSON.stringify({ v: CACHE_VERSION, ok: true, reused: true, snapshot: loadSnapshot(cachePath) }));
167
297
  return;
168
298
  }
169
- const { snapshot, warnings } = buildSnapshot({ reffPath, manifestPath, profile: args.profile });
299
+ const { snapshot, warnings } = buildSnapshot({ reffPath, manifestPath, gardenPath: args.garden, gardenRepo: args.gardenRepo, profile: args.profile });
170
300
  mkdirSync(dirname(cachePath), { recursive: true });
171
301
  writeFileSync(cachePath, JSON.stringify(snapshot, null, 2));
172
302
  console.log(JSON.stringify({ v: CACHE_VERSION, ok: true, snapshot, ...(warnings.length ? { errors: warnings.map((w) => w.code + ': ' + w.detail) } : {}) }));
@@ -186,6 +316,17 @@ function main() {
186
316
  if (!plugin) { results.push({ n: t, ok: false, error: 'not on the rack: ' + t }); continue; }
187
317
  if (action === 'install') {
188
318
  if (plugin.installed) { results.push({ n: plugin.n, id: plugin.id, ok: true, already: true }); continue; }
319
+ if (plugin.group === 'owned') {
320
+ // Owned species (Plugin Garden ADR, D3-scoped): link dep + bundles append + pnpm install.
321
+ const res = installOwned(manifestPath, plugin);
322
+ const inst = spawnSync('pnpm', ['install'], { cwd: dirname(manifestPath), encoding: 'utf8', timeout: 300000 });
323
+ if (inst.status !== 0) { results.push({ n: plugin.n, id: plugin.id, ok: false, error: 'pnpm install failed (exit ' + (inst.status ?? 1) + '): ' + String(inst.stderr ?? '').slice(-400) }); continue; }
324
+ plugin.installed = true;
325
+ plugin.pkg = res;
326
+ plugin.bundle = true;
327
+ results.push({ n: plugin.n, id: plugin.id, pkg: res, ok: true });
328
+ continue;
329
+ }
189
330
  const r = runDsh(args, 'add', toInstallSpec(plugin.repo));
190
331
  if (!r.ok) { results.push({ n: plugin.n, id: plugin.id, ok: false, error: r.error }); continue; }
191
332
  const manifest = readManifest(manifestPath);
@@ -234,4 +375,5 @@ function fail(errors) {
234
375
  process.exit(1);
235
376
  }
236
377
 
237
- main();
378
+ // Run only when executed directly (not when imported by tests/other tools).
379
+ if (process.argv[1] && import.meta.url === new URL('file://' + process.argv[1].replace(/\\/g, '/')).href) main();