diffity 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/.claude/settings.local.json +11 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/development.md +156 -0
- package/package.json +32 -0
- package/packages/cli/build.js +38 -0
- package/packages/cli/package.json +51 -0
- package/packages/cli/src/agent.ts +187 -0
- package/packages/cli/src/db.ts +58 -0
- package/packages/cli/src/index.ts +196 -0
- package/packages/cli/src/review-routes.ts +150 -0
- package/packages/cli/src/server.ts +370 -0
- package/packages/cli/src/session.ts +48 -0
- package/packages/cli/src/threads.ts +238 -0
- package/packages/cli/tsconfig.json +13 -0
- package/packages/git/package.json +24 -0
- package/packages/git/src/commits.ts +28 -0
- package/packages/git/src/diff.ts +97 -0
- package/packages/git/src/exec.ts +35 -0
- package/packages/git/src/index.ts +5 -0
- package/packages/git/src/repo.ts +63 -0
- package/packages/git/src/status.ts +9 -0
- package/packages/git/src/types.ts +12 -0
- package/packages/git/tsconfig.json +9 -0
- package/packages/parser/package.json +26 -0
- package/packages/parser/src/index.ts +12 -0
- package/packages/parser/src/parse.ts +299 -0
- package/packages/parser/src/types.ts +52 -0
- package/packages/parser/src/word-diff.ts +155 -0
- package/packages/parser/tests/fixtures/binary-deleted.diff +4 -0
- package/packages/parser/tests/fixtures/binary-file.diff +4 -0
- package/packages/parser/tests/fixtures/binary-modified.diff +3 -0
- package/packages/parser/tests/fixtures/copied-file.diff +12 -0
- package/packages/parser/tests/fixtures/deleted-file.diff +9 -0
- package/packages/parser/tests/fixtures/empty.diff +0 -0
- package/packages/parser/tests/fixtures/hunk-with-context.diff +12 -0
- package/packages/parser/tests/fixtures/mode-change-with-content.diff +10 -0
- package/packages/parser/tests/fixtures/mode-change.diff +3 -0
- package/packages/parser/tests/fixtures/multi-file.diff +22 -0
- package/packages/parser/tests/fixtures/new-file.diff +9 -0
- package/packages/parser/tests/fixtures/no-newline.diff +10 -0
- package/packages/parser/tests/fixtures/renamed-file.diff +12 -0
- package/packages/parser/tests/fixtures/single-file-additions.diff +11 -0
- package/packages/parser/tests/fixtures/single-file-deletions.diff +11 -0
- package/packages/parser/tests/fixtures/single-file-mixed.diff +15 -0
- package/packages/parser/tests/fixtures/single-file-multi-hunk.diff +22 -0
- package/packages/parser/tests/fixtures/spaces-in-path.diff +9 -0
- package/packages/parser/tests/fixtures/submodule.diff +7 -0
- package/packages/parser/tests/fixtures/unicode-content.diff +11 -0
- package/packages/parser/tests/parse.test.ts +312 -0
- package/packages/parser/tests/word-diff-integration.test.ts +52 -0
- package/packages/parser/tests/word-diff.test.ts +121 -0
- package/packages/parser/tsconfig.json +10 -0
- package/packages/skills/diffity-resolve/SKILL.md +55 -0
- package/packages/skills/diffity-review/SKILL.md +74 -0
- package/packages/skills/diffity-start/SKILL.md +25 -0
- package/packages/ui/index.html +13 -0
- package/packages/ui/package.json +35 -0
- package/packages/ui/public/brand.svg +12 -0
- package/packages/ui/public/favicon.svg +15 -0
- package/packages/ui/src/app.tsx +14 -0
- package/packages/ui/src/components/comment-bubble.tsx +78 -0
- package/packages/ui/src/components/comment-form-row.tsx +58 -0
- package/packages/ui/src/components/comment-form.tsx +78 -0
- package/packages/ui/src/components/comment-line-number.tsx +60 -0
- package/packages/ui/src/components/comment-thread.tsx +209 -0
- package/packages/ui/src/components/commit-list.tsx +100 -0
- package/packages/ui/src/components/dashboard.tsx +84 -0
- package/packages/ui/src/components/diff-line.tsx +90 -0
- package/packages/ui/src/components/diff-page.tsx +332 -0
- package/packages/ui/src/components/diff-stats.tsx +20 -0
- package/packages/ui/src/components/diff-view.tsx +278 -0
- package/packages/ui/src/components/expand-row.tsx +45 -0
- package/packages/ui/src/components/file-block.tsx +536 -0
- package/packages/ui/src/components/file-tree-item.tsx +84 -0
- package/packages/ui/src/components/file-tree.tsx +72 -0
- package/packages/ui/src/components/general-comments.tsx +174 -0
- package/packages/ui/src/components/hunk-block-split.tsx +357 -0
- package/packages/ui/src/components/hunk-block.tsx +161 -0
- package/packages/ui/src/components/hunk-header.tsx +144 -0
- package/packages/ui/src/components/hunk-with-gap.tsx +113 -0
- package/packages/ui/src/components/icons/arrow-down-icon.tsx +7 -0
- package/packages/ui/src/components/icons/arrow-up-icon.tsx +7 -0
- package/packages/ui/src/components/icons/check-circle-icon.tsx +8 -0
- package/packages/ui/src/components/icons/check-icon.tsx +9 -0
- package/packages/ui/src/components/icons/chevron-down-icon.tsx +11 -0
- package/packages/ui/src/components/icons/chevron-icon.tsx +20 -0
- package/packages/ui/src/components/icons/chevron-up-down-icon.tsx +7 -0
- package/packages/ui/src/components/icons/chevron-up-icon.tsx +11 -0
- package/packages/ui/src/components/icons/comment-icon.tsx +9 -0
- package/packages/ui/src/components/icons/copy-icon.tsx +10 -0
- package/packages/ui/src/components/icons/eye-icon.tsx +10 -0
- package/packages/ui/src/components/icons/eye-off-icon.tsx +12 -0
- package/packages/ui/src/components/icons/file-icon.tsx +7 -0
- package/packages/ui/src/components/icons/folder-icon.tsx +19 -0
- package/packages/ui/src/components/icons/git-branch-icon.tsx +13 -0
- package/packages/ui/src/components/icons/keyboard-icon.tsx +13 -0
- package/packages/ui/src/components/icons/moon-icon.tsx +9 -0
- package/packages/ui/src/components/icons/plus-icon.tsx +9 -0
- package/packages/ui/src/components/icons/search-icon.tsx +10 -0
- package/packages/ui/src/components/icons/sidebar-icon.tsx +10 -0
- package/packages/ui/src/components/icons/spinner.tsx +7 -0
- package/packages/ui/src/components/icons/split-view-icon.tsx +10 -0
- package/packages/ui/src/components/icons/sun-icon.tsx +17 -0
- package/packages/ui/src/components/icons/trash-icon.tsx +11 -0
- package/packages/ui/src/components/icons/undo-icon.tsx +9 -0
- package/packages/ui/src/components/icons/unified-view-icon.tsx +12 -0
- package/packages/ui/src/components/icons/x-icon.tsx +10 -0
- package/packages/ui/src/components/line-number-cell.tsx +18 -0
- package/packages/ui/src/components/markdown-content.tsx +139 -0
- package/packages/ui/src/components/orphaned-threads.tsx +80 -0
- package/packages/ui/src/components/overview-file-list.tsx +57 -0
- package/packages/ui/src/components/render-expansion-rows.tsx +47 -0
- package/packages/ui/src/components/shortcut-modal.tsx +93 -0
- package/packages/ui/src/components/sidebar.tsx +80 -0
- package/packages/ui/src/components/skeleton.tsx +9 -0
- package/packages/ui/src/components/stale-diff-banner.tsx +21 -0
- package/packages/ui/src/components/summary-bar.tsx +39 -0
- package/packages/ui/src/components/toolbar.tsx +246 -0
- package/packages/ui/src/components/ui/badge.tsx +17 -0
- package/packages/ui/src/components/ui/confirm-dialog.tsx +52 -0
- package/packages/ui/src/components/ui/icon-button.tsx +23 -0
- package/packages/ui/src/components/ui/status-badge.tsx +57 -0
- package/packages/ui/src/components/ui/thread-badge.tsx +35 -0
- package/packages/ui/src/components/word-diff.tsx +126 -0
- package/packages/ui/src/hooks/use-comment-actions.ts +97 -0
- package/packages/ui/src/hooks/use-commits.ts +12 -0
- package/packages/ui/src/hooks/use-copy.ts +18 -0
- package/packages/ui/src/hooks/use-diff-staleness.ts +58 -0
- package/packages/ui/src/hooks/use-diff.ts +12 -0
- package/packages/ui/src/hooks/use-highlighter.ts +190 -0
- package/packages/ui/src/hooks/use-info.ts +12 -0
- package/packages/ui/src/hooks/use-keyboard.ts +55 -0
- package/packages/ui/src/hooks/use-line-selection.ts +157 -0
- package/packages/ui/src/hooks/use-overview.ts +12 -0
- package/packages/ui/src/hooks/use-review-threads.ts +12 -0
- package/packages/ui/src/hooks/use-search-params.ts +26 -0
- package/packages/ui/src/hooks/use-theme.ts +34 -0
- package/packages/ui/src/hooks/use-thread-navigation.ts +43 -0
- package/packages/ui/src/lib/api.ts +232 -0
- package/packages/ui/src/lib/cn.ts +6 -0
- package/packages/ui/src/lib/context-expansion.ts +122 -0
- package/packages/ui/src/lib/diff-utils.ts +268 -0
- package/packages/ui/src/lib/dom-utils.ts +13 -0
- package/packages/ui/src/lib/file-tree.ts +122 -0
- package/packages/ui/src/lib/query-client.ts +10 -0
- package/packages/ui/src/lib/render-content.tsx +23 -0
- package/packages/ui/src/lib/syntax-token.ts +4 -0
- package/packages/ui/src/main.tsx +14 -0
- package/packages/ui/src/queries/commits.ts +9 -0
- package/packages/ui/src/queries/diff.ts +9 -0
- package/packages/ui/src/queries/file.ts +10 -0
- package/packages/ui/src/queries/info.ts +9 -0
- package/packages/ui/src/queries/overview.ts +9 -0
- package/packages/ui/src/styles/app.css +178 -0
- package/packages/ui/src/types/comment.ts +61 -0
- package/packages/ui/src/vite-env.d.ts +1 -0
- package/packages/ui/tests/context-expansion.test.ts +279 -0
- package/packages/ui/tests/diff-utils.test.ts +409 -0
- package/packages/ui/tsconfig.json +14 -0
- package/packages/ui/vite.config.ts +23 -0
- package/scripts/build-skills.ts +26 -0
- package/scripts/build.ts +15 -0
- package/scripts/dev.ts +32 -0
- package/scripts/lib/transformers/claude-code.ts +11 -0
- package/scripts/lib/transformers/codex.ts +17 -0
- package/scripts/lib/transformers/cursor.ts +17 -0
- package/scripts/lib/transformers/index.ts +3 -0
- package/scripts/lib/utils.ts +70 -0
- package/scripts/link-dev.ts +54 -0
- package/skills/diffity-resolve/SKILL.md +55 -0
- package/skills/diffity-review/SKILL.md +74 -0
- package/skills/diffity-start/SKILL.md +27 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [tailwindcss(), react()],
|
|
7
|
+
test: {
|
|
8
|
+
include: ['tests/**/*.test.ts'],
|
|
9
|
+
},
|
|
10
|
+
server: {
|
|
11
|
+
proxy: {
|
|
12
|
+
'/api': {
|
|
13
|
+
target: 'http://localhost:5391',
|
|
14
|
+
changeOrigin: true,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
build: {
|
|
19
|
+
outDir: '../cli/dist/ui',
|
|
20
|
+
emptyOutDir: true,
|
|
21
|
+
chunkSizeWarningLimit: 1000,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { join, dirname } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { readSkills, renderSkill, writeFile, cleanDir } from './lib/utils.js';
|
|
4
|
+
import { claudeCode } from './lib/transformers/index.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const rootDir = join(__dirname, '..');
|
|
8
|
+
const sourceDir = join(rootDir, 'packages', 'skills');
|
|
9
|
+
const outputDir = join(rootDir, 'skills');
|
|
10
|
+
const localClaudeSkillsDir = join(rootDir, '.claude', 'skills');
|
|
11
|
+
|
|
12
|
+
const skills = readSkills(sourceDir);
|
|
13
|
+
console.log(`Found ${skills.length} skills`);
|
|
14
|
+
|
|
15
|
+
cleanDir(outputDir);
|
|
16
|
+
for (const skill of skills) {
|
|
17
|
+
const content = renderSkill(skill, { binary: 'diffity' });
|
|
18
|
+
writeFile(join(outputDir, skill.name, 'SKILL.md'), content);
|
|
19
|
+
}
|
|
20
|
+
console.log(`Built ${skills.length} skills to skills/`);
|
|
21
|
+
|
|
22
|
+
cleanDir(localClaudeSkillsDir);
|
|
23
|
+
for (const skill of skills) {
|
|
24
|
+
claudeCode(skill, rootDir, { binary: 'diffity-dev', namePrefix: 'diffity-dev', slashPrefix: '/diffity-dev-', installHint: 'run `npm run dev` from the diffity repo root to link the CLI' });
|
|
25
|
+
}
|
|
26
|
+
console.log(`Synced ${skills.length} dev skills to .claude/skills/`);
|
package/scripts/build.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
const steps = [
|
|
6
|
+
'npm run build:skills',
|
|
7
|
+
'npm run build -w @diffity/parser',
|
|
8
|
+
'npm run build -w @diffity/git',
|
|
9
|
+
'npm run build -w @diffity/ui',
|
|
10
|
+
'npm run build -w diffity',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
for (const step of steps) {
|
|
14
|
+
execSync(step, { stdio: 'inherit' });
|
|
15
|
+
}
|
package/scripts/dev.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { dirname, resolve } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import concurrently from 'concurrently';
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const rootDir = resolve(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
execSync('tsx scripts/link-dev.ts && npm run build:skills', {
|
|
12
|
+
cwd: rootDir,
|
|
13
|
+
stdio: 'inherit',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
concurrently(
|
|
17
|
+
[
|
|
18
|
+
{ command: 'npm run dev -w @diffity/parser', name: 'parser' },
|
|
19
|
+
{ command: 'npm run dev -w @diffity/git', name: 'git' },
|
|
20
|
+
{ command: 'npm run dev:watch -w diffity', name: 'cli' },
|
|
21
|
+
// `vite build --watch` instead of `vite dev` so the output lands in dist/ui
|
|
22
|
+
// where the CLI server can serve it. `vite dev` only serves from memory.
|
|
23
|
+
{ command: 'npx -w @diffity/ui vite build --watch', name: 'ui' },
|
|
24
|
+
{
|
|
25
|
+
command: 'tsx --watch-path=packages/skills scripts/build-skills.ts',
|
|
26
|
+
name: 'skills',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
{
|
|
30
|
+
prefixColors: ['blue', 'green', 'yellow', 'magenta', 'cyan'],
|
|
31
|
+
}
|
|
32
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { renderSkill, writeFile, type Skill, type TransformOptions } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
export function transform(skill: Skill, outputDir: string, options: TransformOptions): void {
|
|
5
|
+
const content = renderSkill(skill, options);
|
|
6
|
+
const dirName = options.namePrefix
|
|
7
|
+
? skill.name.replace('diffity-', `${options.namePrefix}-`)
|
|
8
|
+
: skill.name;
|
|
9
|
+
const outputPath = join(outputDir, '.claude', 'skills', dirName, 'SKILL.md');
|
|
10
|
+
writeFile(outputPath, content);
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import { writeFile, type Skill, type TransformOptions } from '../utils.js';
|
|
4
|
+
|
|
5
|
+
export function transform(skill: Skill, outputDir: string, options: TransformOptions): void {
|
|
6
|
+
const body = skill.content.replaceAll('{{binary}}', options.binary);
|
|
7
|
+
const data = {
|
|
8
|
+
name: skill.data.name,
|
|
9
|
+
description: skill.data.description,
|
|
10
|
+
};
|
|
11
|
+
if (options.namePrefix) {
|
|
12
|
+
data.name = data.name.replace('diffity-', `${options.namePrefix}-`);
|
|
13
|
+
}
|
|
14
|
+
const content = matter.stringify(body, data);
|
|
15
|
+
const outputPath = join(outputDir, '.codex', 'skills', skill.name, 'SKILL.md');
|
|
16
|
+
writeFile(outputPath, content);
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import { writeFile, type Skill, type TransformOptions } from '../utils.js';
|
|
4
|
+
|
|
5
|
+
export function transform(skill: Skill, outputDir: string, options: TransformOptions): void {
|
|
6
|
+
const body = skill.content.replaceAll('{{binary}}', options.binary);
|
|
7
|
+
const data = {
|
|
8
|
+
name: skill.data.name,
|
|
9
|
+
description: skill.data.description,
|
|
10
|
+
};
|
|
11
|
+
if (options.namePrefix) {
|
|
12
|
+
data.name = data.name.replace('diffity-', `${options.namePrefix}-`);
|
|
13
|
+
}
|
|
14
|
+
const content = matter.stringify(body, data);
|
|
15
|
+
const outputPath = join(outputDir, '.cursor', 'skills', skill.name, 'SKILL.md');
|
|
16
|
+
writeFile(outputPath, content);
|
|
17
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import matter from 'gray-matter';
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync, existsSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
|
|
5
|
+
export interface SkillData {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Skill {
|
|
12
|
+
name: string;
|
|
13
|
+
data: SkillData;
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TransformOptions {
|
|
18
|
+
binary: string;
|
|
19
|
+
namePrefix?: string;
|
|
20
|
+
slashPrefix?: string;
|
|
21
|
+
installHint?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function readSkills(sourceDir: string): Skill[] {
|
|
25
|
+
const skills: Skill[] = [];
|
|
26
|
+
const entries = readdirSync(sourceDir, { withFileTypes: true });
|
|
27
|
+
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
if (!entry.isDirectory()) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const skillPath = join(sourceDir, entry.name, 'SKILL.md');
|
|
34
|
+
if (!existsSync(skillPath)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const raw = readFileSync(skillPath, 'utf-8');
|
|
39
|
+
const { data, content } = matter(raw);
|
|
40
|
+
skills.push({ name: entry.name, data: data as SkillData, content });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return skills;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function renderSkill(skill: Skill, { binary, namePrefix, slashPrefix, installHint }: TransformOptions): string {
|
|
47
|
+
const slash = slashPrefix ?? '/diffity-';
|
|
48
|
+
const hint = installHint ?? 'install it with `npm install -g diffity`';
|
|
49
|
+
const body = skill.content
|
|
50
|
+
.replaceAll('{{binary}}', binary)
|
|
51
|
+
.replaceAll('{{slash}}', slash)
|
|
52
|
+
.replaceAll('{{install_hint}}', hint);
|
|
53
|
+
const data = { ...skill.data };
|
|
54
|
+
if (namePrefix) {
|
|
55
|
+
data.name = data.name.replace('diffity-', `${namePrefix}-`);
|
|
56
|
+
}
|
|
57
|
+
return matter.stringify(body, data);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function writeFile(filePath: string, content: string): void {
|
|
61
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
62
|
+
writeFileSync(filePath, content);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function cleanDir(dir: string): void {
|
|
66
|
+
if (existsSync(dir)) {
|
|
67
|
+
rmSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
mkdirSync(dir, { recursive: true });
|
|
70
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { writeFileSync, readFileSync, chmodSync, unlinkSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { resolve, dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const rootDir = resolve(__dirname, '..');
|
|
8
|
+
const binDir = join(rootDir, '.bin');
|
|
9
|
+
const cliEntry = join(rootDir, 'packages', 'cli', 'dist', 'index.js');
|
|
10
|
+
const watchDir = join(rootDir, 'packages', 'cli', 'dist');
|
|
11
|
+
const linkPath = join(binDir, 'diffity-dev');
|
|
12
|
+
|
|
13
|
+
if (!existsSync(cliEntry)) {
|
|
14
|
+
console.log('CLI not built yet, building packages first...');
|
|
15
|
+
execSync('npm run build', { cwd: rootDir, stdio: 'inherit' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
mkdirSync(binDir, { recursive: true });
|
|
19
|
+
|
|
20
|
+
if (existsSync(linkPath)) {
|
|
21
|
+
unlinkSync(linkPath);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const script = `#!/usr/bin/env bash
|
|
25
|
+
# Only use --watch for the server (no subcommand). One-off commands like "agent list" run and exit.
|
|
26
|
+
if [ $# -eq 0 ] || [ "$1" = "--no-open" ]; then
|
|
27
|
+
exec node --watch-path="${watchDir}" "${cliEntry}" --no-open "$@"
|
|
28
|
+
else
|
|
29
|
+
exec node "${cliEntry}" "$@"
|
|
30
|
+
fi
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
writeFileSync(linkPath, script);
|
|
34
|
+
chmodSync(linkPath, 0o755);
|
|
35
|
+
|
|
36
|
+
const pathIncludes = process.env.PATH?.includes(binDir);
|
|
37
|
+
console.log(`Created diffity-dev (auto-restarts on changes)`);
|
|
38
|
+
if (!pathIncludes) {
|
|
39
|
+
const profileName = process.env.SHELL?.includes('zsh') ? '.zshrc' : '.bashrc';
|
|
40
|
+
const profilePath = resolve(process.env.HOME || '~', profileName);
|
|
41
|
+
const exportLine = `export PATH="${binDir}:$PATH"`;
|
|
42
|
+
|
|
43
|
+
if (existsSync(profilePath)) {
|
|
44
|
+
const content = readFileSync(profilePath, 'utf-8');
|
|
45
|
+
if (!content.includes(binDir)) {
|
|
46
|
+
writeFileSync(profilePath, content + `\n# diffity dev CLI\n${exportLine}\n`);
|
|
47
|
+
console.log(`\nAdded .bin to PATH in ~/${profileName}`);
|
|
48
|
+
console.log(`Run: source ~/${profileName}`);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
console.log(`\nAdd this to your shell profile:`);
|
|
52
|
+
console.log(` ${exportLine}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diffity-resolve
|
|
3
|
+
description: Read open review comments and resolve them by making code fixes
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Diffity Resolve Skill
|
|
8
|
+
|
|
9
|
+
You are reading open review comments and resolving them by making the requested code changes.
|
|
10
|
+
|
|
11
|
+
## Arguments
|
|
12
|
+
|
|
13
|
+
- `thread-id` (optional): Resolve a specific thread by ID instead of all open threads. Example: `/diffity-resolve abc123`
|
|
14
|
+
|
|
15
|
+
## CLI Reference
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
diffity agent list [--status open|resolved|dismissed] [--json]
|
|
19
|
+
diffity agent comment --file <path> --line <n> [--end-line <n>] [--side new|old] --body "<text>"
|
|
20
|
+
diffity agent general-comment --body "<text>"
|
|
21
|
+
diffity agent resolve <id> [--summary "<text>"]
|
|
22
|
+
diffity agent dismiss <id> [--reason "<text>"]
|
|
23
|
+
diffity agent reply <id> --body "<text>"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- `--file`, `--line`, `--body` are required for `comment`
|
|
27
|
+
- `--end-line` defaults to `--line` (single-line comment)
|
|
28
|
+
- `--side` defaults to `new`
|
|
29
|
+
- `general-comment` creates a diff-level comment not tied to any file or line
|
|
30
|
+
- `<id>` accepts full UUID or 8-char prefix
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
|
|
35
|
+
2. Check that a review session exists: run `cat .diffity/current-session`. If the file doesn't exist or is stale, tell the user to start diffity first.
|
|
36
|
+
|
|
37
|
+
## Instructions
|
|
38
|
+
|
|
39
|
+
1. List open comment threads with full details:
|
|
40
|
+
```
|
|
41
|
+
diffity agent list --status open --json
|
|
42
|
+
```
|
|
43
|
+
If a `thread-id` argument was provided, filter to just that thread. The JSON output includes the full comment body, file path, line numbers, and side for each thread.
|
|
44
|
+
2. If there are no open threads, tell the user there's nothing to resolve.
|
|
45
|
+
3. For each open thread:
|
|
46
|
+
a. **Skip** general comments (filePath `__general__`) — these are summaries, not actionable code changes.
|
|
47
|
+
b. **Skip** threads tagged `[question]` or `[nit]` — these don't require code changes. Tell the user you skipped them and why.
|
|
48
|
+
c. Read the comment body from the JSON output and understand what change is requested.
|
|
49
|
+
d. Read the relevant source file to understand the full context around the commented lines, then make the requested code change using the Edit tool.
|
|
50
|
+
e. After making the change, resolve the thread with a summary:
|
|
51
|
+
```
|
|
52
|
+
diffity agent resolve <thread-id> --summary "Fixed: <brief description of what was changed>"
|
|
53
|
+
```
|
|
54
|
+
4. After resolving all applicable threads, run `diffity agent list` to confirm status.
|
|
55
|
+
5. Tell the user to check the browser — resolved status will appear within 2 seconds via polling.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diffity-review
|
|
3
|
+
description: Review current diff and leave comments using diffity agent commands
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Diffity Review Skill
|
|
8
|
+
|
|
9
|
+
You are reviewing a diff and leaving inline comments using the `diffity agent` CLI.
|
|
10
|
+
|
|
11
|
+
## Arguments
|
|
12
|
+
|
|
13
|
+
- `focus` (optional): Focus the review on a specific area. One of: `security`, `performance`, `naming`, `errors`, `types`, `logic`. If omitted, review everything.
|
|
14
|
+
|
|
15
|
+
## CLI Reference
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
diffity agent list [--status open|resolved|dismissed] [--json]
|
|
19
|
+
diffity agent comment --file <path> --line <n> [--end-line <n>] [--side new|old] --body "<text>"
|
|
20
|
+
diffity agent general-comment --body "<text>"
|
|
21
|
+
diffity agent resolve <id> [--summary "<text>"]
|
|
22
|
+
diffity agent dismiss <id> [--reason "<text>"]
|
|
23
|
+
diffity agent reply <id> --body "<text>"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- `--file`, `--line`, `--body` are required for `comment`
|
|
27
|
+
- `--end-line` defaults to `--line` (single-line comment)
|
|
28
|
+
- `--side` defaults to `new`
|
|
29
|
+
- `general-comment` creates a diff-level comment not tied to any file or line
|
|
30
|
+
- `<id>` accepts full UUID or 8-char prefix
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
|
|
35
|
+
2. Check that a review session exists: run `cat .diffity/current-session`. If the file doesn't exist or is stale, tell the user to start diffity first (e.g. `diffity`).
|
|
36
|
+
|
|
37
|
+
## Instructions
|
|
38
|
+
|
|
39
|
+
1. Read the current diff using `git diff`. Check `.diffity/current-session` to determine which ref is active.
|
|
40
|
+
2. For each changed file, read the **entire file** (not just the diff hunks) to understand the full context. This prevents false positives from missing surrounding code.
|
|
41
|
+
3. Analyze the code changes thoroughly. If a `focus` argument was provided, concentrate on that area. Otherwise look for:
|
|
42
|
+
- Bugs, logic errors, off-by-one errors
|
|
43
|
+
- Security issues (injection, XSS, auth bypass)
|
|
44
|
+
- Performance problems
|
|
45
|
+
- Missing error handling at system boundaries
|
|
46
|
+
- Race conditions
|
|
47
|
+
- API contract violations
|
|
48
|
+
- Unclear or misleading naming
|
|
49
|
+
4. **Only comment on code that was changed in the diff.** Do not flag pre-existing issues in unchanged code — this is a review of the diff, not an audit of the entire file. The only exception is if a change in the diff introduces a bug in combination with existing code.
|
|
50
|
+
5. **Prioritize signal over volume.** A clean diff should get a clean review. Do not manufacture findings to appear thorough. If a diff with 5 changed lines only has 1 real issue, leave 1 comment.
|
|
51
|
+
6. **Do not repeat the same issue across files.** If the same pattern appears in multiple places, leave one inline comment on the first occurrence and mention it in the general summary instead of commenting on every instance.
|
|
52
|
+
7. Categorize each finding with a severity prefix in the comment body:
|
|
53
|
+
- `[must-fix]` — Bugs, security issues, data loss risks. These must be addressed.
|
|
54
|
+
- `[suggestion]` — Improvements that would meaningfully improve the code.
|
|
55
|
+
- `[nit]` — Style or preference. Fine to ignore.
|
|
56
|
+
- `[question]` — Something unclear that needs clarification from the author.
|
|
57
|
+
8. For each finding, leave a comment using:
|
|
58
|
+
```
|
|
59
|
+
diffity agent comment --file <path> --line <n> [--end-line <n>] [--side new] --body "<comment>"
|
|
60
|
+
```
|
|
61
|
+
- Use `--side new` (default) for comments on added/modified code
|
|
62
|
+
- Use `--side old` for comments on removed code
|
|
63
|
+
- Use `--end-line` when the issue spans multiple lines
|
|
64
|
+
- Be specific and actionable in your comments
|
|
65
|
+
9. After leaving all inline comments, write a general comment that summarizes your overall assessment of the diff. This should cover:
|
|
66
|
+
- Overall quality verdict (e.g. "Looks good with minor issues" or "Needs significant changes before merging")
|
|
67
|
+
- Cross-cutting concerns that don't belong on any single line (architecture, naming consistency across files, missing tests, etc.)
|
|
68
|
+
- A count of findings by severity (e.g. "2 must-fix, 3 suggestions, 1 nit")
|
|
69
|
+
```
|
|
70
|
+
diffity agent general-comment --body "<overall review summary>"
|
|
71
|
+
```
|
|
72
|
+
If there are no inline findings, still leave a general comment with your assessment (e.g. "Clean diff — no issues found").
|
|
73
|
+
10. Run `diffity agent list` to confirm all comments were created.
|
|
74
|
+
11. Tell the user to check the browser — comments will appear within 2 seconds via polling.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diffity-start
|
|
3
|
+
description: >-
|
|
4
|
+
Start the diffity diff viewer server for the current working tree or staged
|
|
5
|
+
changes
|
|
6
|
+
user-invocable: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Diffity Start Skill
|
|
10
|
+
|
|
11
|
+
You are starting the diffity diff viewer so the user can see their changes in the browser.
|
|
12
|
+
|
|
13
|
+
## Instructions
|
|
14
|
+
|
|
15
|
+
1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
|
|
16
|
+
2. Start the server using the Bash tool with `run_in_background: true`:
|
|
17
|
+
- Command: `diffity`
|
|
18
|
+
- Do NOT use `&` or `--quiet` — let the Bash tool handle backgrounding
|
|
19
|
+
- The browser will open automatically and the session is auto-created on startup
|
|
20
|
+
4. Wait 2 seconds, then verify the session exists by checking that `.diffity/current-session` file is present.
|
|
21
|
+
5. Tell the user diffity is running and show them what they can do next. Keep it short — don't show session IDs, hashes, or other internals. Example:
|
|
22
|
+
|
|
23
|
+
> Diffity is running — check your browser.
|
|
24
|
+
>
|
|
25
|
+
> Here's what you can do:
|
|
26
|
+
> - **/diffity-review** — get a code review on your changes
|
|
27
|
+
> - **/diffity-resolve** — fix issues from review comments
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true
|
|
15
|
+
},
|
|
16
|
+
"references": [
|
|
17
|
+
{ "path": "./packages/parser" },
|
|
18
|
+
{ "path": "./packages/git" },
|
|
19
|
+
{ "path": "./packages/cli" },
|
|
20
|
+
{ "path": "./packages/ui" }
|
|
21
|
+
]
|
|
22
|
+
}
|