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 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
- ## Install
14
+ ---
15
15
 
16
- ```bash
17
- npm install -g codehere
18
- ```
16
+ ## Recipe — setup to first audit in 30 minutes
19
17
 
20
- Requires Node.js 20+. Open-source MIT.
18
+ > One folder convention, one prompt formula, four ready-to-use prompts.
19
+ > Full version: **[docs/RECIPE.md](../docs/RECIPE.md)**.
21
20
 
22
- ## Quick start
21
+ ### Setup (once)
23
22
 
24
- ### Delegate a task to an AI agent
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
- Make sure at least one of the four supported agents is installed:
32
+ You're done. `codehere start` opens the web UI at `:3773`; you delegate from there.
27
33
 
28
- | Agent | Install |
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
- You'll see:
52
+ ---
42
53
 
43
- - **Today's activity** · "47 AI actions · 3 blocked · avg trust 91 · 4 agents"
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.1",
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",