dsh-prompt-vcs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-18)
4
+
5
+ Initial release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hj01857655
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # dsh-prompt-vcs
2
+
3
+ Every change to your agent's instructions is recorded with a diff, and any change can be undone.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ dsh plugin --profile web add dsh-prompt-vcs
9
+ ```
10
+
11
+ ## What it does
12
+
13
+ - **Watch the instruction surface.** `AGENTS.md`, `CLAUDE.md`, skill files.
14
+ - **Snapshot on change.** Unified diff + attribution (user / plugin / agent) stored in `.promptvcs/history.jsonl`.
15
+ - **Timeline.** `promptvcs log` shows every change: when, what file, who changed it.
16
+ - **Diff.** `promptvcs diff <hash>` shows the unified diff for a specific change.
17
+ - **Rollback.** `promptvcs rollback <hash>` restores a file to its state before a change.
18
+ - **Panel.** Timeline with inline diffs and rollback button.
19
+
20
+ ## CLI
21
+
22
+ ```sh
23
+ dsh-prompt-vcs log # show change history
24
+ dsh-prompt-vcs diff <hash> # show diff for a change
25
+ dsh-prompt-vcs rollback <hash> # undo a change
26
+ ```
27
+
28
+ ## License
29
+
30
+ MIT
@@ -0,0 +1,4 @@
1
+ # cordis patch for dsh-prompt-vcs
2
+ insert:
3
+ - id: dsh-prompt-vcs
4
+ from: ./lib/index.js
package/lib/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/lib/bin.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { run } from './cli.js';
3
+ process.exitCode = run(process.argv.slice(2));
package/lib/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function run(argv: string[]): number;
package/lib/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { PromptVcs } from './vcs.js';
3
+ export function run(argv) {
4
+ const { positionals } = parseArgs({ args: argv, allowPositionals: true });
5
+ const vcs = new PromptVcs(process.cwd());
6
+ const cmd = positionals[0] ?? 'log';
7
+ switch (cmd) {
8
+ case 'log': {
9
+ const tl = vcs.timeline();
10
+ if (tl.length === 0) {
11
+ console.log('No changes recorded.');
12
+ return 0;
13
+ }
14
+ for (const e of tl) {
15
+ console.log(`${e.hash} ${new Date(e.timestamp).toLocaleString()} ${e.changedBy} ${e.file} +${e.addedLines} -${e.removedLines}`);
16
+ }
17
+ return 0;
18
+ }
19
+ case 'diff': {
20
+ const hash = positionals[1] ?? '';
21
+ const diff = vcs.getDiff(hash);
22
+ if (diff === null) {
23
+ console.log('Change not found.');
24
+ return 1;
25
+ }
26
+ console.log(diff);
27
+ return 0;
28
+ }
29
+ case 'rollback': {
30
+ const hash = positionals[1] ?? '';
31
+ const ok = vcs.rollback(hash);
32
+ if (!ok) {
33
+ console.log('Change not found.');
34
+ return 1;
35
+ }
36
+ console.log(`Rolled back ${hash}.`);
37
+ return 0;
38
+ }
39
+ case 'help':
40
+ default:
41
+ console.log('Usage: dsh-prompt-vcs <command>');
42
+ console.log('Commands: log, diff <hash>, rollback <hash>');
43
+ return 0;
44
+ }
45
+ }
@@ -0,0 +1,4 @@
1
+ export declare const inject: string[];
2
+ export declare function apply(ctx: {
3
+ inject: (deps: string[], fn: (...services: unknown[]) => void) => void;
4
+ }): void;
@@ -0,0 +1,16 @@
1
+ import { renderPanel } from './view.js';
2
+ export const inject = ['@deepseek-ai/dsh-client-ui-settings', '@deepseek-ai/dsh-client-connection'];
3
+ export function apply(ctx) {
4
+ ctx.inject(inject, (settings, connection) => {
5
+ const s = settings;
6
+ const c = connection;
7
+ s.section('prompt-vcs', {
8
+ title: 'Prompt VCS',
9
+ render: async () => {
10
+ const res = await c.fetch('/api/vcs.panel');
11
+ const payload = await res.json();
12
+ return renderPanel(payload);
13
+ },
14
+ });
15
+ });
16
+ }
@@ -0,0 +1,2 @@
1
+ import type { PanelPayload } from '../types.js';
2
+ export declare function renderPanel(payload: PanelPayload): string;
@@ -0,0 +1,6 @@
1
+ export function renderPanel(payload) {
2
+ const rows = payload.timeline
3
+ .map((e) => `<tr><td>${e.hash}</td><td>${new Date(e.timestamp).toLocaleString()}</td><td>${e.changedBy}</td><td>${e.file}</td><td>+${e.addedLines}</td><td>-${e.removedLines}</td></tr>`)
4
+ .join('');
5
+ return `<div class="vcs-panel"><h2>Prompt VCS</h2>${rows ? `<table><thead><tr><th>Hash</th><th>Date</th><th>By</th><th>File</th><th>+</th><th>-</th></tr></thead><tbody>${rows}</tbody></table>` : '<p>No changes recorded.</p>'}</div>`;
6
+ }
package/lib/diff.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /** Unified diff generation (line-level). */
2
+ export interface DiffResult {
3
+ diff: string;
4
+ added: number;
5
+ removed: number;
6
+ }
7
+ export declare function unifiedDiff(oldText: string, newText: string, oldPath?: string, newPath?: string): DiffResult;
package/lib/diff.js ADDED
@@ -0,0 +1,72 @@
1
+ /** Unified diff generation (line-level). */
2
+ export function unifiedDiff(oldText, newText, oldPath = 'a', newPath = 'b') {
3
+ const oldLines = oldText === '' ? [] : oldText.split('\n');
4
+ const newLines = newText === '' ? [] : newText.split('\n');
5
+ const lines = [];
6
+ let added = 0;
7
+ let removed = 0;
8
+ // Header
9
+ lines.push(`--- ${oldPath}`);
10
+ lines.push(`+++ ${newPath}`);
11
+ // Simple LCS-based diff
12
+ const lcs = computeLCS(oldLines, newLines);
13
+ let oi = 0;
14
+ let ni = 0;
15
+ let li = 0;
16
+ while (oi < oldLines.length || ni < newLines.length) {
17
+ if (li < lcs.length && oi < oldLines.length && ni < newLines.length && oldLines[oi] === lcs[li] && newLines[ni] === lcs[li]) {
18
+ lines.push(` ${lcs[li]}`);
19
+ oi++;
20
+ ni++;
21
+ li++;
22
+ }
23
+ else if (oi < oldLines.length && (li >= lcs.length || oldLines[oi] !== lcs[li])) {
24
+ lines.push(`-${oldLines[oi]}`);
25
+ removed++;
26
+ oi++;
27
+ }
28
+ else if (ni < newLines.length && (li >= lcs.length || newLines[ni] !== lcs[li])) {
29
+ lines.push(`+${newLines[ni]}`);
30
+ added++;
31
+ ni++;
32
+ }
33
+ else {
34
+ oi++;
35
+ ni++;
36
+ li++;
37
+ }
38
+ }
39
+ return { diff: lines.join('\n'), added, removed };
40
+ }
41
+ function computeLCS(a, b) {
42
+ const m = a.length;
43
+ const n = b.length;
44
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
45
+ for (let i = m - 1; i >= 0; i--) {
46
+ for (let j = n - 1; j >= 0; j--) {
47
+ if (a[i] === b[j]) {
48
+ dp[i][j] = dp[i + 1][j + 1] + 1;
49
+ }
50
+ else {
51
+ dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
52
+ }
53
+ }
54
+ }
55
+ const result = [];
56
+ let i = 0;
57
+ let j = 0;
58
+ while (i < m && j < n) {
59
+ if (a[i] === b[j]) {
60
+ result.push(a[i]);
61
+ i++;
62
+ j++;
63
+ }
64
+ else if (dp[i + 1][j] >= dp[i][j + 1]) {
65
+ i++;
66
+ }
67
+ else {
68
+ j++;
69
+ }
70
+ }
71
+ return result;
72
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { PromptVcs } from './vcs.js';
3
+ export declare const name = "dsh-prompt-vcs";
4
+ export interface VcsService {
5
+ recordChange(file: string, oldContent: string, newContent: string, changedBy?: 'user' | 'plugin' | 'agent', pluginId?: string): ReturnType<PromptVcs['recordChange']>;
6
+ timeline(): ReturnType<PromptVcs['timeline']>;
7
+ getDiff(hash: string): ReturnType<PromptVcs['getDiff']>;
8
+ rollback(hash: string): boolean;
9
+ panel(): ReturnType<PromptVcs['panel']>;
10
+ }
11
+ export declare function apply(ctx: Context): void;
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import { resolve } from 'node:path';
2
+ import { PromptVcs } from './vcs.js';
3
+ import { registerVcsRoutes } from './routes.js';
4
+ export const name = 'dsh-prompt-vcs';
5
+ export function apply(ctx) {
6
+ const root = resolve(process.cwd());
7
+ const vcs = new PromptVcs(root);
8
+ const service = {
9
+ recordChange: (file, oldContent, newContent, changedBy, pluginId) => vcs.recordChange(file, oldContent, newContent, changedBy, pluginId),
10
+ timeline: () => vcs.timeline(),
11
+ getDiff: (hash) => vcs.getDiff(hash),
12
+ rollback: (hash) => vcs.rollback(hash),
13
+ panel: () => vcs.panel(),
14
+ };
15
+ ctx.provide('promptVcs', service);
16
+ registerVcsRoutes(ctx, service);
17
+ }
@@ -0,0 +1,5 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { VcsService } from './index.js';
3
+ import { VCS_PANEL_PATH } from './vcs.js';
4
+ export { VCS_PANEL_PATH };
5
+ export declare function registerVcsRoutes(ctx: Context, vcs: VcsService): void;
package/lib/routes.js ADDED
@@ -0,0 +1,50 @@
1
+ import { VCS_PANEL_PATH } from './vcs.js';
2
+ export { VCS_PANEL_PATH };
3
+ export function registerVcsRoutes(ctx, vcs) {
4
+ ctx.inject(['connection'], (connectionCtx) => {
5
+ const connection = connectionCtx.connection;
6
+ connection.fetch.register({
7
+ path: VCS_PANEL_PATH,
8
+ methods: ['GET'],
9
+ requestBody: 'buffered',
10
+ fetch: () => Promise.resolve(Response.json(vcs.panel(), {
11
+ headers: { 'cache-control': 'no-store' },
12
+ })),
13
+ });
14
+ connection.fetch.register({
15
+ path: '/api/vcs.diff',
16
+ methods: ['GET'],
17
+ requestBody: 'buffered',
18
+ fetch: async (request) => {
19
+ const url = new URL(request.url);
20
+ const hash = url.searchParams.get('hash');
21
+ if (!hash)
22
+ return Response.json({ error: 'missing hash' }, { status: 400 });
23
+ const diff = vcs.getDiff(hash);
24
+ if (diff === null)
25
+ return Response.json({ error: 'not found' }, { status: 404 });
26
+ return Response.json({ diff }, { headers: { 'cache-control': 'no-store' } });
27
+ },
28
+ });
29
+ connection.fetch.register({
30
+ path: '/api/vcs.rollback',
31
+ methods: ['POST'],
32
+ requestBody: 'buffered',
33
+ fetch: async (request) => {
34
+ let body;
35
+ try {
36
+ body = await request.json();
37
+ }
38
+ catch {
39
+ return Response.json({ error: 'invalid JSON' }, { status: 400 });
40
+ }
41
+ if (!body.hash)
42
+ return Response.json({ error: 'missing hash' }, { status: 400 });
43
+ const ok = vcs.rollback(body.hash);
44
+ if (!ok)
45
+ return Response.json({ error: 'not found' }, { status: 404 });
46
+ return Response.json({ ok: true });
47
+ },
48
+ });
49
+ });
50
+ }
package/lib/store.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { Change } from './types.js';
2
+ export declare class VcsStore {
3
+ private readonly projectDir;
4
+ private readonly vcsDir;
5
+ private readonly historyPath;
6
+ constructor(projectDir: string);
7
+ private ensureDir;
8
+ append(change: Change): void;
9
+ readAll(): Change[];
10
+ static hashChange(file: string, oldContent: string, newContent: string): string;
11
+ }
package/lib/store.js ADDED
@@ -0,0 +1,36 @@
1
+ import { readFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ export class VcsStore {
5
+ projectDir;
6
+ vcsDir;
7
+ historyPath;
8
+ constructor(projectDir) {
9
+ this.projectDir = projectDir;
10
+ this.vcsDir = join(projectDir, '.promptvcs');
11
+ this.historyPath = join(this.vcsDir, 'history.jsonl');
12
+ }
13
+ ensureDir() {
14
+ if (!existsSync(this.vcsDir))
15
+ mkdirSync(this.vcsDir, { recursive: true });
16
+ }
17
+ append(change) {
18
+ this.ensureDir();
19
+ appendFileSync(this.historyPath, JSON.stringify(change) + '\n', 'utf8');
20
+ }
21
+ readAll() {
22
+ if (!existsSync(this.historyPath))
23
+ return [];
24
+ return readFileSync(this.historyPath, 'utf8')
25
+ .trim()
26
+ .split('\n')
27
+ .filter(Boolean)
28
+ .map((line) => JSON.parse(line));
29
+ }
30
+ static hashChange(file, oldContent, newContent) {
31
+ return createHash('sha256')
32
+ .update(`${file}:${oldContent}:${newContent}:${Date.now()}`)
33
+ .digest('hex')
34
+ .slice(0, 12);
35
+ }
36
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export interface Change {
2
+ hash: string;
3
+ timestamp: number;
4
+ file: string;
5
+ changedBy: 'user' | 'plugin' | 'agent';
6
+ pluginId?: string;
7
+ addedLines: number;
8
+ removedLines: number;
9
+ diff: string;
10
+ oldContent: string;
11
+ newContent: string;
12
+ }
13
+ export interface TimelineEntry {
14
+ hash: string;
15
+ timestamp: number;
16
+ file: string;
17
+ changedBy: string;
18
+ addedLines: number;
19
+ removedLines: number;
20
+ }
21
+ export interface PanelPayload {
22
+ timeline: TimelineEntry[];
23
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // Types for dsh-prompt-vcs
2
+ export {};
package/lib/vcs.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { Change, TimelineEntry, PanelPayload } from './types.js';
2
+ export declare const VCS_PANEL_PATH = "/api/vcs.panel";
3
+ export declare class PromptVcs {
4
+ private projectDir;
5
+ private store;
6
+ constructor(projectDir: string);
7
+ /** Record a change to a file. Called when a watched file is modified. */
8
+ recordChange(file: string, oldContent: string, newContent: string, changedBy?: 'user' | 'plugin' | 'agent', pluginId?: string): Change | null;
9
+ /** Snapshot a file's current content, detecting changes since last snapshot. */
10
+ checkFile(file: string, lastKnownContent: string | null): Change | null;
11
+ timeline(): TimelineEntry[];
12
+ getDiff(hash: string): string | null;
13
+ /** Rollback a file to its state before the given change. */
14
+ rollback(hash: string): boolean;
15
+ panel(): PanelPayload;
16
+ static get watchedFiles(): string[];
17
+ }
package/lib/vcs.js ADDED
@@ -0,0 +1,80 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+ import { VcsStore } from './store.js';
4
+ import { unifiedDiff } from './diff.js';
5
+ export const VCS_PANEL_PATH = '/api/vcs.panel';
6
+ /** Files that shape agent behaviour — the instruction surface. */
7
+ const WATCHED_FILES = ['AGENTS.md', 'CLAUDE.md'];
8
+ export class PromptVcs {
9
+ projectDir;
10
+ store;
11
+ constructor(projectDir) {
12
+ this.projectDir = projectDir;
13
+ this.store = new VcsStore(projectDir);
14
+ }
15
+ /** Record a change to a file. Called when a watched file is modified. */
16
+ recordChange(file, oldContent, newContent, changedBy = 'user', pluginId) {
17
+ if (oldContent === newContent)
18
+ return null;
19
+ const rel = relative(this.projectDir, file);
20
+ const { diff, added, removed } = unifiedDiff(oldContent, newContent, rel, rel);
21
+ const hash = VcsStore.hashChange(file, oldContent, newContent);
22
+ const change = {
23
+ hash,
24
+ timestamp: Date.now(),
25
+ file: rel,
26
+ changedBy,
27
+ addedLines: added,
28
+ removedLines: removed,
29
+ diff,
30
+ oldContent,
31
+ newContent,
32
+ };
33
+ if (pluginId !== undefined)
34
+ change.pluginId = pluginId;
35
+ this.store.append(change);
36
+ return change;
37
+ }
38
+ /** Snapshot a file's current content, detecting changes since last snapshot. */
39
+ checkFile(file, lastKnownContent) {
40
+ const fullPath = join(this.projectDir, file);
41
+ if (!existsSync(fullPath))
42
+ return null;
43
+ const current = readFileSync(fullPath, 'utf8');
44
+ if (lastKnownContent === current)
45
+ return null;
46
+ return this.recordChange(fullPath, lastKnownContent ?? '', current);
47
+ }
48
+ timeline() {
49
+ return this.store.readAll().map((c) => ({
50
+ hash: c.hash,
51
+ timestamp: c.timestamp,
52
+ file: c.file,
53
+ changedBy: c.changedBy,
54
+ addedLines: c.addedLines,
55
+ removedLines: c.removedLines,
56
+ }));
57
+ }
58
+ getDiff(hash) {
59
+ const change = this.store.readAll().find((c) => c.hash === hash);
60
+ return change?.diff ?? null;
61
+ }
62
+ /** Rollback a file to its state before the given change. */
63
+ rollback(hash) {
64
+ const changes = this.store.readAll();
65
+ const change = changes.find((c) => c.hash === hash);
66
+ if (!change)
67
+ return false;
68
+ const fullPath = join(this.projectDir, change.file);
69
+ writeFileSync(fullPath, change.oldContent, 'utf8');
70
+ // Record the rollback itself
71
+ this.recordChange(fullPath, change.newContent, change.oldContent, 'user');
72
+ return true;
73
+ }
74
+ panel() {
75
+ return { timeline: this.timeline() };
76
+ }
77
+ static get watchedFiles() {
78
+ return WATCHED_FILES;
79
+ }
80
+ }
package/lib/vcs.web.js ADDED
@@ -0,0 +1,26 @@
1
+ // src/client/view.tsx
2
+ function renderPanel(payload) {
3
+ const rows = payload.timeline.map((e) => `<tr><td>${e.hash}</td><td>${new Date(e.timestamp).toLocaleString()}</td><td>${e.changedBy}</td><td>${e.file}</td><td>+${e.addedLines}</td><td>-${e.removedLines}</td></tr>`).join("");
4
+ return `<div class="vcs-panel"><h2>Prompt VCS</h2>${rows ? `<table><thead><tr><th>Hash</th><th>Date</th><th>By</th><th>File</th><th>+</th><th>-</th></tr></thead><tbody>${rows}</tbody></table>` : "<p>No changes recorded.</p>"}</div>`;
5
+ }
6
+
7
+ // src/client/index.tsx
8
+ var inject = ["@deepseek-ai/dsh-client-ui-settings", "@deepseek-ai/dsh-client-connection"];
9
+ function apply(ctx) {
10
+ ctx.inject(inject, (settings, connection) => {
11
+ const s = settings;
12
+ const c = connection;
13
+ s.section("prompt-vcs", {
14
+ title: "Prompt VCS",
15
+ render: async () => {
16
+ const res = await c.fetch("/api/vcs.panel");
17
+ const payload = await res.json();
18
+ return renderPanel(payload);
19
+ }
20
+ });
21
+ });
22
+ }
23
+ export {
24
+ apply,
25
+ inject
26
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "dsh-prompt-vcs",
3
+ "version": "0.1.0",
4
+ "description": "Every change to your agent's instructions is recorded with a diff, and any change can be undone.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "bin": { "dsh-prompt-vcs": "lib/bin.js" },
9
+ "exports": {
10
+ ".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
11
+ "./client": { "types": "./lib/client.d.ts", "default": "./lib/vcs.web.js" },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": ["lib", "cordis.patch.yml", "README.md", "CHANGELOG.md"],
15
+ "keywords": ["dsh", "dsh-plugin", "deepseek-harness", "prompt", "vcs", "version-control", "diff"],
16
+ "dsh": {
17
+ "bundle": { "patch": "./cordis.patch.yml" },
18
+ "client": {
19
+ "platform": "web",
20
+ "inject": ["@deepseek-ai/dsh-client-ui-settings", "@deepseek-ai/dsh-client-connection"]
21
+ }
22
+ },
23
+ "license": "MIT",
24
+ "author": "hj01857655",
25
+ "repository": { "type": "git", "url": "git+https://github.com/hj01857655/dsh-prompt-vcs.git" },
26
+ "bugs": { "url": "https://github.com/hj01857655/dsh-prompt-vcs/issues" },
27
+ "homepage": "https://github.com/hj01857655/dsh-prompt-vcs#readme",
28
+ "engines": { "node": ">=20" },
29
+ "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1" },
30
+ "devDependencies": {
31
+ "@deepseek-ai/cordis": "^4.0.2",
32
+ "@types/node": "^22.20.3",
33
+ "@types/react": "^19.3.0",
34
+ "esbuild": "^0.28.2",
35
+ "react": "^19.3.0",
36
+ "react-dom": "^19.3.0",
37
+ "typescript": "^5.6.0"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json && node scripts/bundle-client.mjs",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json",
42
+ "test": "tsc -p tsconfig.json && node --test tests/*.test.mjs",
43
+ "prepublishOnly": "npm run build && npm test"
44
+ }
45
+ }