codewalk 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/bin/codewalk.js +2 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +163 -0
- package/dist/commands/visualize.d.ts +5 -0
- package/dist/commands/visualize.d.ts.map +1 -0
- package/dist/commands/visualize.js +79 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21435 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/tui/app.d.ts +13 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +11 -0
- package/dist/tui/tree-view.d.ts +24 -0
- package/dist/tui/tree-view.d.ts.map +1 -0
- package/dist/tui/tree-view.js +274 -0
- package/dist/types/codewalker.d.ts +15 -0
- package/dist/types/codewalker.d.ts.map +1 -0
- package/dist/types/codewalker.js +1 -0
- package/dist/utils/git.d.ts +24 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +132 -0
- package/dist/utils/tracking.d.ts +23 -0
- package/dist/utils/tracking.d.ts.map +1 -0
- package/dist/utils/tracking.js +85 -0
- package/package.json +41 -0
package/bin/codewalk.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAmHA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDrE"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
const SKILL_TEMPLATE = `# Code Walker
|
|
5
|
+
|
|
6
|
+
You are Code Walker, an AI programming assistant built on top of Claude Code.
|
|
7
|
+
|
|
8
|
+
Your purpose is to give the user more visibility into the changes you are making.
|
|
9
|
+
|
|
10
|
+
The current functionality you follow is to make changes, asking for permission if needed as you go, and then you provide a brief summary of the changes made after you're done.
|
|
11
|
+
|
|
12
|
+
In addition to your normal summary, you should also keep track of what you changed in a structured file.
|
|
13
|
+
|
|
14
|
+
The purpose of the file is to walk the user through the code changes step-by-step so that they can understand the code changes you made, why you made them, and how they relate to other changes you made during that task. If the user follows up with further instructions or changes, you should update the file to track that. A full walkthrough can be found below.
|
|
15
|
+
|
|
16
|
+
User prompts are surrounded by \`<USER>\` tags, your code changes are surrounded by \`<ASSISTANT>\` tags, example tracking files are surrounded by \`<TRACK>\` tags, and notes are surrounded in \`<NOTE>\` tags.
|
|
17
|
+
|
|
18
|
+
## Tracking File Schema
|
|
19
|
+
|
|
20
|
+
\`\`\`typescript
|
|
21
|
+
type Changeset = {
|
|
22
|
+
// Schema version for forward compatibility
|
|
23
|
+
version: number;
|
|
24
|
+
|
|
25
|
+
// Git commit SHA this changeset describes
|
|
26
|
+
commit: string;
|
|
27
|
+
|
|
28
|
+
// Who made the changes (human name, "claude", etc.)
|
|
29
|
+
author: string;
|
|
30
|
+
|
|
31
|
+
// List of logical changes, each with its own reasoning
|
|
32
|
+
changes: Change[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type Change = {
|
|
36
|
+
// Human-readable explanation of why this change was made.
|
|
37
|
+
// Should explain the intent, not just describe what changed.
|
|
38
|
+
reasoning: string;
|
|
39
|
+
|
|
40
|
+
// Files affected by this logical change
|
|
41
|
+
files: FileChange[];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type FileChange = {
|
|
45
|
+
// Path to the file, relative to repo root
|
|
46
|
+
path: string;
|
|
47
|
+
|
|
48
|
+
// Which hunks from \`git show <commit>\` belong to this change.
|
|
49
|
+
// 1-indexed, in order of appearance in the diff.
|
|
50
|
+
// Example: [1, 3] means the first and third hunks in this file's diff.
|
|
51
|
+
hunks: number[];
|
|
52
|
+
};
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
## Git Commands Reference
|
|
56
|
+
|
|
57
|
+
- Get current commit hash: \`git rev-parse --short HEAD\`
|
|
58
|
+
- Get commit author: \`git log -1 --format="%an"\`
|
|
59
|
+
- View commit diff with hunks: \`git show <commit> --format=""\`
|
|
60
|
+
- List files changed: \`git show <commit> --name-only --format=""\`
|
|
61
|
+
|
|
62
|
+
Hunks are numbered 1, 2, 3... in order of appearance. Each \`@@\` line in the diff starts a new hunk.
|
|
63
|
+
|
|
64
|
+
## Handling Refinements
|
|
65
|
+
|
|
66
|
+
If the user requests changes to something you just did (e.g., "use different colors", "rename that function"):
|
|
67
|
+
|
|
68
|
+
1. If it's part of the same logical task, amend the commit: \`git add -A && git commit --amend --no-edit\`
|
|
69
|
+
2. Update the existing tracking file to reflect the final state
|
|
70
|
+
3. The reasoning should describe the final result, not the iteration history
|
|
71
|
+
|
|
72
|
+
If it's a distinct new task, create a new commit and new tracking file.
|
|
73
|
+
|
|
74
|
+
## Instructions
|
|
75
|
+
|
|
76
|
+
1. Before committing, check if a git repo exists with \`git status\`. Only run \`git init\` if there isn't one.
|
|
77
|
+
2. After completing a task, commit your changes and automatically create a tracking file at \`.codewalker/<commit-hash>.json\`
|
|
78
|
+
3. Group hunks by their logical purpose—one reasoning per logical change, even if it spans multiple hunks or multiple files. If a single user request results in changes across several files, those should typically be one change with one reasoning, not separate changes per file.
|
|
79
|
+
4. Write reasoning that explains *why*, not just *what* (the diff already shows what)
|
|
80
|
+
5. If a follow-up request refines previous work, amend the commit and update the tracking file
|
|
81
|
+
6. If a follow-up request is a new task, create a new commit and new tracking file
|
|
82
|
+
7. After writing the tracking file, validate it with: \`python3 -c "import json; json.load(open('.codewalker/<commit-hash>.json'))"\`
|
|
83
|
+
|
|
84
|
+
Do not wait for the user to ask for the tracking file—create it automatically as part of finishing each task.
|
|
85
|
+
|
|
86
|
+
**Important:** Always complete the tracking step after every task, even in long conversations with multiple tasks. Do not skip this step regardless of conversation length or number of previous tasks.
|
|
87
|
+
`;
|
|
88
|
+
const CLAUDE_MD_CONTENT = `# Code Walker
|
|
89
|
+
|
|
90
|
+
**CRITICAL REQUIREMENT**: You MUST create git commits for your changes AND create tracking files after EVERY task. No exceptions.
|
|
91
|
+
|
|
92
|
+
## Workflow (MANDATORY):
|
|
93
|
+
1. Make your code changes using Write/Edit tools
|
|
94
|
+
2. **COMMIT the changes**: \`git add -A && git commit -m "descriptive message"\`
|
|
95
|
+
3. Get the commit hash: \`git rev-parse --short HEAD\`
|
|
96
|
+
4. **IMMEDIATELY** create \`.codewalker/<commit-hash>.json\` with the tracking data
|
|
97
|
+
5. Commit the tracking file: \`git add .codewalker/<hash>.json && git commit -m "Add tracking file for <hash>"\`
|
|
98
|
+
6. Only THEN respond to the user
|
|
99
|
+
|
|
100
|
+
**DO NOT** skip commits. **DO NOT** respond to the user until both the code commit AND tracking file commit are done.
|
|
101
|
+
|
|
102
|
+
See \`.claude/skills/codewalker.md\` for the complete schema and examples.
|
|
103
|
+
`;
|
|
104
|
+
async function fileExists(filePath) {
|
|
105
|
+
try {
|
|
106
|
+
await fs.access(filePath);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export async function initCommand(options) {
|
|
114
|
+
const { cwd } = options;
|
|
115
|
+
console.log(pc.bold('Initializing CodeWalker...\n'));
|
|
116
|
+
// 1. Create .claude/skills directory
|
|
117
|
+
const skillsDir = path.join(cwd, '.claude', 'skills');
|
|
118
|
+
await fs.mkdir(skillsDir, { recursive: true });
|
|
119
|
+
// 2. Create skill file (idempotent)
|
|
120
|
+
const skillPath = path.join(skillsDir, 'codewalker.md');
|
|
121
|
+
const skillExists = await fileExists(skillPath);
|
|
122
|
+
if (!skillExists) {
|
|
123
|
+
await fs.writeFile(skillPath, SKILL_TEMPLATE);
|
|
124
|
+
console.log(pc.green('✓') + ' Created .claude/skills/codewalker.md');
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(pc.yellow('○') + ' .claude/skills/codewalker.md already exists, skipping');
|
|
128
|
+
}
|
|
129
|
+
// 3. Update CLAUDE.md (idempotent)
|
|
130
|
+
const claudePath = path.join(cwd, 'CLAUDE.md');
|
|
131
|
+
let claudeContent = '';
|
|
132
|
+
try {
|
|
133
|
+
claudeContent = await fs.readFile(claudePath, 'utf-8');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// File doesn't exist, will create
|
|
137
|
+
}
|
|
138
|
+
if (!claudeContent.includes('.claude/skills/codewalker.md')) {
|
|
139
|
+
const newContent = claudeContent
|
|
140
|
+
? claudeContent + '\n\n' + CLAUDE_MD_CONTENT
|
|
141
|
+
: CLAUDE_MD_CONTENT;
|
|
142
|
+
await fs.writeFile(claudePath, newContent);
|
|
143
|
+
console.log(pc.green('✓') + ' Updated CLAUDE.md with CodeWalker instructions');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log(pc.yellow('○') + ' CLAUDE.md already references CodeWalker, skipping');
|
|
147
|
+
}
|
|
148
|
+
// 4. Create .codewalker directory
|
|
149
|
+
const codewalkerDir = path.join(cwd, '.codewalker');
|
|
150
|
+
const codewalkerExists = await fileExists(codewalkerDir);
|
|
151
|
+
await fs.mkdir(codewalkerDir, { recursive: true });
|
|
152
|
+
if (!codewalkerExists) {
|
|
153
|
+
console.log(pc.green('✓') + ' Created .codewalker/ directory');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.log(pc.yellow('○') + ' .codewalker/ directory already exists');
|
|
157
|
+
}
|
|
158
|
+
console.log(pc.bold('\nCodeWalker initialized successfully!'));
|
|
159
|
+
console.log('\nNext steps:');
|
|
160
|
+
console.log(' 1. Start Claude Code in this directory');
|
|
161
|
+
console.log(' 2. Make changes - Claude will automatically track them');
|
|
162
|
+
console.log(' 3. Run ' + pc.cyan('codewalker visualize') + ' to browse changes');
|
|
163
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualize.d.ts","sourceRoot":"","sources":["../../src/commands/visualize.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2F/E"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { createCliRenderer } from '@opentui/core';
|
|
3
|
+
import { getCurrentBranch, getCommitList, isGitRepo } from '../utils/git.js';
|
|
4
|
+
import { loadTrackingFiles, getTrackedCommits, aggregateByReasoning } from '../utils/tracking.js';
|
|
5
|
+
import { createAppState } from '../tui/app.js';
|
|
6
|
+
import { TreeView } from '../tui/tree-view.js';
|
|
7
|
+
export async function visualizeCommand(options) {
|
|
8
|
+
const { cwd } = options;
|
|
9
|
+
// Check if in git repo
|
|
10
|
+
if (!isGitRepo(cwd)) {
|
|
11
|
+
console.error(pc.red('Error: Not a git repository'));
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
// Load data
|
|
15
|
+
console.log(pc.dim('Loading tracking data...'));
|
|
16
|
+
const branch = getCurrentBranch(cwd);
|
|
17
|
+
const commits = getCommitList(cwd);
|
|
18
|
+
const allTrackedCommits = await loadTrackingFiles(cwd, commits);
|
|
19
|
+
const trackedCommits = getTrackedCommits(allTrackedCommits);
|
|
20
|
+
if (trackedCommits.length === 0) {
|
|
21
|
+
console.log(pc.yellow('No tracked commits found in .codewalker/'));
|
|
22
|
+
console.log(pc.dim('Run some tasks with Claude Code to generate tracking data.'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.log(pc.dim('Aggregating changes by reasoning...'));
|
|
26
|
+
// Aggregate changes by reasoning (like the PR "By Reasoning" view)
|
|
27
|
+
const reasoningGroups = aggregateByReasoning(cwd, trackedCommits);
|
|
28
|
+
if (reasoningGroups.length === 0) {
|
|
29
|
+
console.log(pc.yellow('No changes with diffs found.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Create OpenTUI renderer
|
|
33
|
+
console.log(pc.dim('Starting visualizer...'));
|
|
34
|
+
const renderer = await createCliRenderer({
|
|
35
|
+
exitOnCtrlC: true,
|
|
36
|
+
useAlternateScreen: true,
|
|
37
|
+
useMouse: true,
|
|
38
|
+
backgroundColor: '#0f0f1a',
|
|
39
|
+
});
|
|
40
|
+
// Create app state
|
|
41
|
+
const state = createAppState(branch, reasoningGroups);
|
|
42
|
+
// Create tree view
|
|
43
|
+
const treeView = new TreeView(renderer, state);
|
|
44
|
+
// Handle keyboard input
|
|
45
|
+
renderer.keyInput.on('keypress', (event) => {
|
|
46
|
+
const key = event.name;
|
|
47
|
+
switch (key) {
|
|
48
|
+
case 'q':
|
|
49
|
+
treeView.destroy();
|
|
50
|
+
renderer.destroy();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
break;
|
|
53
|
+
case 'j':
|
|
54
|
+
case 'ArrowDown':
|
|
55
|
+
treeView.moveSelection(1);
|
|
56
|
+
break;
|
|
57
|
+
case 'k':
|
|
58
|
+
case 'ArrowUp':
|
|
59
|
+
treeView.moveSelection(-1);
|
|
60
|
+
break;
|
|
61
|
+
case 'Enter':
|
|
62
|
+
case ' ':
|
|
63
|
+
treeView.toggleExpand();
|
|
64
|
+
break;
|
|
65
|
+
case 'g':
|
|
66
|
+
// Go to top
|
|
67
|
+
state.selectedIndex = 0;
|
|
68
|
+
treeView.refresh();
|
|
69
|
+
break;
|
|
70
|
+
case 'G':
|
|
71
|
+
// Go to bottom
|
|
72
|
+
state.selectedIndex = Math.max(0, treeView.getItemCount() - 1);
|
|
73
|
+
treeView.refresh();
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Start rendering
|
|
78
|
+
renderer.start();
|
|
79
|
+
}
|