codehere 0.3.1 → 0.3.3
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/README.md +33 -25
- package/dist/index.js +10 -0
- package/dist/presentation/cli/commands/init-command.js +245 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,48 +11,56 @@ codehere is **a PM for your AI coding agents.** Delegate a task. codehere picks
|
|
|
11
11
|
|
|
12
12
|
One manager, many specialists, every action recorded.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
---
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
npm install -g codehere
|
|
18
|
-
```
|
|
16
|
+
## Recipe — setup to first audit in 30 minutes
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
> One folder convention, one prompt formula, four ready-to-use prompts.
|
|
19
|
+
> Full version: **[docs/RECIPE.md](../docs/RECIPE.md)**.
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
### Setup (once)
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
| | | |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| 1 | **Install** | `npm install -g codehere` (Node 20+) |
|
|
26
|
+
| 2 | **Detect environment** | `codehere setup` — checks API key, finds your repo |
|
|
27
|
+
| 3 | **Install the safety hook** | `codehere hook install` — every Claude Code tool call now audited |
|
|
28
|
+
| 4 | **Install at least one specialist** | Claude Code: `npm i -g @anthropic-ai/claude-code` (Codex / Aider / OpenCode also supported) |
|
|
29
|
+
| 5 | **Scaffold this repo** | `cd /your/repo && codehere init` — creates `.codehere/CONTEXT.md`, `HANDOVER.md`, `PROMPTS.md` |
|
|
30
|
+
| 6 | **Fill in CONTEXT.md** | 5 minutes. The agent will follow what's written there. |
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
You're done. `codehere start` opens the web UI at `:3773`; you delegate from there.
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|---|---|
|
|
30
|
-
| Claude Code | `npm install -g @anthropic-ai/claude-code` |
|
|
31
|
-
| Codex CLI | `npm install -g @openai/codex` |
|
|
32
|
-
| Aider | `brew install aider` |
|
|
33
|
-
| OpenCode | `curl -fsSL https://opencode.ai/install \| bash` |
|
|
34
|
+
### The prompt formula
|
|
34
35
|
|
|
35
|
-
Then:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
codehere start # opens http://127.0.0.1:3773 in your browser
|
|
39
36
|
```
|
|
37
|
+
Context + End State + Constraints
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Context** — *"Read .codehere/CONTEXT.md"* + any specific files
|
|
41
|
+
- **End State** — the user-visible change, the test that passes, the file that exists
|
|
42
|
+
- **Constraints** — files not to touch, dependencies not to add, patterns not to use
|
|
43
|
+
|
|
44
|
+
### Folder convention (what `codehere init` writes)
|
|
45
|
+
|
|
46
|
+
| File | Purpose | Committed? |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `.codehere/CONTEXT.md` | What this repo is. Conventions. Style. Constraints. | yes |
|
|
49
|
+
| `.codehere/HANDOVER.md` | Cross-session continuity (until F3 memory ships in v0.5). | **no** |
|
|
50
|
+
| `.codehere/PROMPTS.md` | Your library of recurring task prompts. | yes |
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
---
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
- **A task intake** with three starter prompts you can click
|
|
45
|
-
- **A cwd field** pre-filled with where you ran `codehere start`
|
|
46
|
-
- **An agent dropdown** showing which of the four are installed (and a one-click Install button for the others)
|
|
54
|
+
## What you see when you delegate a task
|
|
47
55
|
|
|
48
|
-
Pick an agent, type a task, hit **Assign**. codehere:
|
|
56
|
+
`codehere start` opens `http://127.0.0.1:3773`. Pick an agent, type a task, hit **Assign**. codehere:
|
|
49
57
|
|
|
50
58
|
1. Spawns the agent with the right headless flags
|
|
51
59
|
2. Streams the steps back as readable progress · *"Read src/auth.ts · Edit src/auth.ts · Run npm test"*
|
|
52
60
|
3. For Claude Code tasks: routes every tool call through the codehere safety hook — you see *"🛡 5 safe · 0 blocked"* grow live. For Codex / Aider / OpenCode tasks: emits the agent's native event stream (no per-tool blocking yet)
|
|
53
61
|
4. On completion, hands you a **Final report**: files changed with `+/-` lines, trust score per file, test pass/fail, token usage
|
|
54
62
|
|
|
55
|
-
Claude Code tool calls are logged to `~/.codehere/audit/hook-events.jsonl`; spawned-task lifecycle (any adapter) is logged to `~/.codehere/sessions.jsonl`.
|
|
63
|
+
Claude Code tool calls are logged to `~/.codehere/audit/hook-events.jsonl`; spawned-task lifecycle (any adapter) is logged to `~/.codehere/sessions.jsonl`. Open-source MIT. Node 20+.
|
|
56
64
|
|
|
57
65
|
### Just the safety hook (no agents required)
|
|
58
66
|
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { config } from 'dotenv';
|
|
|
15
15
|
import { join, dirname } from 'path';
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
import { executeSetupCommand } from './presentation/cli/commands/setup-command.js';
|
|
18
|
+
import { executeInitCommand } from './presentation/cli/commands/init-command.js';
|
|
18
19
|
import { executeConfigCommand } from './presentation/cli/commands/config-command.js';
|
|
19
20
|
import { executeHelpCommand } from './presentation/cli/commands/help-command.js';
|
|
20
21
|
import { executeStatusCommand } from './presentation/cli/commands/status-command.js';
|
|
@@ -93,6 +94,15 @@ program
|
|
|
93
94
|
.action(async (options) => {
|
|
94
95
|
await executeSetupCommand(options.force || false);
|
|
95
96
|
});
|
|
97
|
+
// ── init ─────────────────────────────────────────────────────────────────────
|
|
98
|
+
program
|
|
99
|
+
.command('init')
|
|
100
|
+
.description('Scaffold .codehere/ in this repo · CONTEXT, HANDOVER, PROMPTS')
|
|
101
|
+
.option('-f, --force', 'Overwrite existing files in .codehere/')
|
|
102
|
+
.option('--no-gitignore', 'Skip updating .gitignore')
|
|
103
|
+
.action((options) => {
|
|
104
|
+
executeInitCommand({ force: options.force, noGitignore: !options.gitignore });
|
|
105
|
+
});
|
|
96
106
|
// ── config ───────────────────────────────────────────────────────────────────
|
|
97
107
|
program
|
|
98
108
|
.command('config')
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Command — scaffold a `.codehere/` directory at the repo root.
|
|
3
|
+
*
|
|
4
|
+
* The user-side analog to `git init`: gives the project a small, conventional
|
|
5
|
+
* shape codehere can read from when an agent runs here. Three files:
|
|
6
|
+
*
|
|
7
|
+
* .codehere/CONTEXT.md — what this repo is, conventions, constraints
|
|
8
|
+
* .codehere/HANDOVER.md — manual cross-session continuity (until F3 ships)
|
|
9
|
+
* .codehere/PROMPTS.md — your library of recurring task prompts
|
|
10
|
+
*
|
|
11
|
+
* Conventions:
|
|
12
|
+
* - CONTEXT.md and PROMPTS.md are committed (project config, shared)
|
|
13
|
+
* - HANDOVER.md is git-ignored by default (per-machine working state)
|
|
14
|
+
*
|
|
15
|
+
* Targets the repo root (via findCodebaseRoot), not cwd, so `cd src && codehere init`
|
|
16
|
+
* still scaffolds at the repo root, not inside src/.
|
|
17
|
+
*
|
|
18
|
+
* Idempotent: re-running detects existing files and skips them unless --force.
|
|
19
|
+
* Refuses to follow symlinks (lstat check) so a hostile/misplaced symlink at
|
|
20
|
+
* `.codehere/CONTEXT.md` or `.gitignore` cannot redirect writes outside the repo.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync, lstatSync, statSync } from 'fs';
|
|
23
|
+
import { join } from 'path';
|
|
24
|
+
import { colors, success, info, warning, error, newline } from '../../../ui.js';
|
|
25
|
+
import { findCodebaseRoot } from '../../../domain/services/codebase-detector.js';
|
|
26
|
+
const TEMPLATES = [
|
|
27
|
+
{
|
|
28
|
+
filename: 'CONTEXT.md',
|
|
29
|
+
body: `# Project Context
|
|
30
|
+
|
|
31
|
+
> codehere reads this when an agent runs in this directory.
|
|
32
|
+
> Keep it short. Keep it true. The agent will follow what's written here.
|
|
33
|
+
|
|
34
|
+
## What this is
|
|
35
|
+
|
|
36
|
+
[One-paragraph description of what this repo does and who uses it.]
|
|
37
|
+
|
|
38
|
+
## Conventions
|
|
39
|
+
|
|
40
|
+
[How this codebase is organized — entry points, file naming, framework version.
|
|
41
|
+
Anything an agent would otherwise have to discover by grep.]
|
|
42
|
+
|
|
43
|
+
## Style
|
|
44
|
+
|
|
45
|
+
[Tone for generated code. Test framework. Lint rules. Imports style.
|
|
46
|
+
Prefer X. Avoid Y.]
|
|
47
|
+
|
|
48
|
+
## Constraints
|
|
49
|
+
|
|
50
|
+
[Don't do X. Always do Y. Files that are off-limits.
|
|
51
|
+
Things you've been burned by.]
|
|
52
|
+
|
|
53
|
+
## Useful commands
|
|
54
|
+
|
|
55
|
+
\`\`\`
|
|
56
|
+
[build, test, lint, dev server — the agent will run these]
|
|
57
|
+
\`\`\`
|
|
58
|
+
`,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
filename: 'HANDOVER.md',
|
|
62
|
+
body: `# Handover
|
|
63
|
+
|
|
64
|
+
> Update at the end of each working session.
|
|
65
|
+
> codehere has no project memory across sessions yet (F3 ships in v0.5).
|
|
66
|
+
> This file is the manual handover so the next session — or the next agent —
|
|
67
|
+
> can pick up without you re-explaining.
|
|
68
|
+
|
|
69
|
+
## Last session
|
|
70
|
+
|
|
71
|
+
- **Date:**
|
|
72
|
+
- **What got done:**
|
|
73
|
+
- **What's in progress:**
|
|
74
|
+
- **What to pick up next:**
|
|
75
|
+
- **Open questions:**
|
|
76
|
+
|
|
77
|
+
## Decisions worth remembering
|
|
78
|
+
|
|
79
|
+
[Decisions made this session that future sessions should respect.]
|
|
80
|
+
|
|
81
|
+
## Files touched recently
|
|
82
|
+
|
|
83
|
+
[List the files you've been working on. The agent can prioritize them.]
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
filename: 'PROMPTS.md',
|
|
88
|
+
body: `# Prompts
|
|
89
|
+
|
|
90
|
+
> Your library of recurring task prompts.
|
|
91
|
+
> Copy + adapt + delegate via \`codehere task\` or the web UI.
|
|
92
|
+
>
|
|
93
|
+
> The codehere prompt formula: **Context + End State + Constraints**.
|
|
94
|
+
> Stack-specific details (test framework, language, file conventions) belong
|
|
95
|
+
> in CONTEXT.md, not duplicated in every prompt here.
|
|
96
|
+
|
|
97
|
+
## Implement a change
|
|
98
|
+
|
|
99
|
+
> "Read .codehere/CONTEXT.md. [Describe the change in user-visible terms — what
|
|
100
|
+
> behavior is added, removed, or changed.] Match existing conventions. Don't
|
|
101
|
+
> touch unrelated files. Don't add new dependencies."
|
|
102
|
+
|
|
103
|
+
## Add tests
|
|
104
|
+
|
|
105
|
+
> "Read .codehere/CONTEXT.md. Add tests for [FILE / behavior]. Cover happy
|
|
106
|
+
> path, edge cases, and error paths. Match the existing test style and
|
|
107
|
+
> framework. Don't lower the strictness of any existing test."
|
|
108
|
+
|
|
109
|
+
## Refactor safely
|
|
110
|
+
|
|
111
|
+
> "Read .codehere/CONTEXT.md. Refactor [FILE / module] to [GOAL]. Keep the
|
|
112
|
+
> public API stable. All existing tests must still pass. Don't introduce
|
|
113
|
+
> new dependencies. Show the diff before applying."
|
|
114
|
+
|
|
115
|
+
## Review changes
|
|
116
|
+
|
|
117
|
+
> "Read .codehere/CONTEXT.md. Review the changes in [PR / branch / file] for:
|
|
118
|
+
> security issues, missing tests, breaking changes, style violations, and
|
|
119
|
+
> alignment with the constraints in CONTEXT.md. Output a short markdown
|
|
120
|
+
> report; don't change any code."
|
|
121
|
+
`,
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
const GITIGNORE_MARKER = '# Added by codehere init';
|
|
125
|
+
const GITIGNORE_BLOCK = `\n${GITIGNORE_MARKER}\n.codehere/HANDOVER.md\n`;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the directory to scaffold. Prefers git/package root over cwd so
|
|
128
|
+
* `cd repo/src && codehere init` still lands at repo/.
|
|
129
|
+
*/
|
|
130
|
+
function resolveTargetRoot() {
|
|
131
|
+
const info = findCodebaseRoot();
|
|
132
|
+
if (info)
|
|
133
|
+
return { root: info.rootPath, type: info.type };
|
|
134
|
+
return { root: process.cwd(), type: 'directory' };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Reject a path that exists AND is a symlink. Refusing on symlinks (rather
|
|
138
|
+
* than following) prevents an init run from clobbering files outside the
|
|
139
|
+
* repo if a hostile or misplaced symlink already exists at the target.
|
|
140
|
+
*/
|
|
141
|
+
function isSymlink(path) {
|
|
142
|
+
try {
|
|
143
|
+
return lstatSync(path).isSymbolicLink();
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function ensureGitignore(repoRoot) {
|
|
150
|
+
const gitDir = join(repoRoot, '.git');
|
|
151
|
+
if (!existsSync(gitDir))
|
|
152
|
+
return 'no-git';
|
|
153
|
+
const gitignorePath = join(repoRoot, '.gitignore');
|
|
154
|
+
if (isSymlink(gitignorePath))
|
|
155
|
+
return 'refused-symlink';
|
|
156
|
+
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf8') : '';
|
|
157
|
+
if (existing.includes('.codehere/HANDOVER.md'))
|
|
158
|
+
return 'already-present';
|
|
159
|
+
appendFileSync(gitignorePath, GITIGNORE_BLOCK);
|
|
160
|
+
return 'added';
|
|
161
|
+
}
|
|
162
|
+
export function executeInitCommand(options = {}) {
|
|
163
|
+
try {
|
|
164
|
+
const { root, type } = resolveTargetRoot();
|
|
165
|
+
const codehereDir = join(root, '.codehere');
|
|
166
|
+
newline();
|
|
167
|
+
console.log(colors.bold('codehere init'));
|
|
168
|
+
console.log(colors.dim(` scaffolding .codehere/ at ${type} root: ${root}`));
|
|
169
|
+
newline();
|
|
170
|
+
// Refuse if .codehere itself is a symlink — would redirect every subsequent write.
|
|
171
|
+
if (existsSync(codehereDir) && isSymlink(codehereDir)) {
|
|
172
|
+
console.log(` ${error('refused')} .codehere is a symlink — refusing to follow. Remove it first.`);
|
|
173
|
+
newline();
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!existsSync(codehereDir)) {
|
|
178
|
+
mkdirSync(codehereDir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const codehereStat = statSync(codehereDir);
|
|
182
|
+
if (!codehereStat.isDirectory()) {
|
|
183
|
+
console.log(` ${error('refused')} .codehere exists but is not a directory.`);
|
|
184
|
+
newline();
|
|
185
|
+
process.exitCode = 1;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const created = [];
|
|
190
|
+
const skipped = [];
|
|
191
|
+
const refused = [];
|
|
192
|
+
for (const tmpl of TEMPLATES) {
|
|
193
|
+
const target = join(codehereDir, tmpl.filename);
|
|
194
|
+
if (isSymlink(target)) {
|
|
195
|
+
refused.push(tmpl.filename);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (existsSync(target) && !options.force) {
|
|
199
|
+
skipped.push(tmpl.filename);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
writeFileSync(target, tmpl.body);
|
|
203
|
+
created.push(tmpl.filename);
|
|
204
|
+
}
|
|
205
|
+
for (const f of created) {
|
|
206
|
+
console.log(` ${success(`created`)} .codehere/${f}`);
|
|
207
|
+
}
|
|
208
|
+
for (const f of skipped) {
|
|
209
|
+
console.log(` ${info(`exists `)} .codehere/${f} ${colors.dim('(use --force to overwrite)')}`);
|
|
210
|
+
}
|
|
211
|
+
for (const f of refused) {
|
|
212
|
+
console.log(` ${error(`refused`)} .codehere/${f} ${colors.dim('(symlink — remove it first)')}`);
|
|
213
|
+
}
|
|
214
|
+
if (!options.noGitignore) {
|
|
215
|
+
const gitignoreResult = ensureGitignore(root);
|
|
216
|
+
newline();
|
|
217
|
+
switch (gitignoreResult) {
|
|
218
|
+
case 'added':
|
|
219
|
+
console.log(` ${success('updated')} .gitignore ${colors.dim('(.codehere/HANDOVER.md ignored — per-machine working state)')}`);
|
|
220
|
+
break;
|
|
221
|
+
case 'already-present':
|
|
222
|
+
console.log(` ${info('exists ')} .gitignore ${colors.dim('(HANDOVER.md already ignored)')}`);
|
|
223
|
+
break;
|
|
224
|
+
case 'no-git':
|
|
225
|
+
console.log(` ${warning('skipped')} .gitignore ${colors.dim('(no .git directory at root)')}`);
|
|
226
|
+
break;
|
|
227
|
+
case 'refused-symlink':
|
|
228
|
+
console.log(` ${error('refused')} .gitignore is a symlink — left untouched.`);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
newline();
|
|
233
|
+
console.log(colors.bold('Next:'));
|
|
234
|
+
console.log(` 1. Open ${colors.cyan('.codehere/CONTEXT.md')} and fill in what this repo is`);
|
|
235
|
+
console.log(` 2. Run ${colors.cyan('codehere start')} and delegate your first task`);
|
|
236
|
+
console.log(` 3. Update ${colors.cyan('.codehere/HANDOVER.md')} at the end of the session`);
|
|
237
|
+
newline();
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
241
|
+
console.error(`\n ${error('failed')} codehere init — ${msg}\n`);
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=init-command.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehere",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "A PM for your AI coding agents. Delegate, orchestrate, and audit Claude Code, Codex, Aider, and OpenCode from one local web UI — every AI action traced, every file scored.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|