mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-30 12:49:33 +01:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fad32eeaab | ||
|
|
c1458d8ab8 |
No files matched your search
@@ -1,5 +1,15 @@
|
|||||||
# aicodeman
|
# aicodeman
|
||||||
|
|
||||||
|
## 0.9.10
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Self-update now restarts automatically on headless Macs supervised by a system LaunchDaemon.
|
||||||
|
|
||||||
|
New `launchd-daemon` supervisor kind: when Codeman runs under a bootstrapped, KeepAlive system-level LaunchDaemon (`/Library/LaunchDaemons/com.codeman.web.plist` — the right setup for headless Macs, where LaunchAgents never start because there is no GUI login), the updater no longer ends with "Update staged — restart Codeman to apply". It restarts rootlessly: the update script kills the server PID (passed via `--server-pid`) and launchd respawns it on the freshly built `dist/`. Detection is conservative — the daemon must be bootstrapped in the system domain AND have `KeepAlive` enabled.
|
||||||
|
|
||||||
|
Also fixed: a lingering "restart Codeman to apply" status. After a manual restart of a staged update, boot reconciliation now flips `completed-needs-manual-restart` to `completed` once the running version matches the staged target, so the Updates tab stops showing the stale instruction.
|
||||||
|
|
||||||
## 0.9.9
|
## 0.9.9
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ When user says "COM":
|
|||||||
|
|
||||||
CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed.
|
CI runs `npm run check:lockfile` on every push/PR, so lockfile drift fails the build even if the `version-packages` script is bypassed.
|
||||||
|
|
||||||
**Version**: 0.9.9 (must match `package.json`)
|
**Version**: 0.9.10 (must match `package.json`)
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
|
|||||||
|
|
||||||
**Circuit breaker**: Prevents respawn thrashing. States: `CLOSED` → `HALF_OPEN` → `OPEN`. Reset: `/api/sessions/:id/ralph-circuit-breaker/reset`.
|
**Circuit breaker**: Prevents respawn thrashing. States: `CLOSED` → `HALF_OPEN` → `OPEN`. Reset: `/api/sessions/:id/ralph-circuit-breaker/reset`.
|
||||||
|
|
||||||
**Self-update** (App Settings → Updates): in-app updater for **git-clone installs** supervised by systemd/launchd. The update restarts the very process running it, so the real work runs in a DETACHED `scripts/self-update.sh` (`git checkout <release tag> && npm install && npm run build && restart`) that outlives the restart; it writes progress to `dataPath('update-status.json')`, which the browser polls across the connection drop. Channel = latest `codeman@X.Y.Z` release tag; dirty trees are auto-stashed. `src/web/self-update.ts` splits PURE helpers (semver/tag parsing, reconcile decision — unit-tested) from IO wrappers (`getInstallInfo`/`checkForUpdate`/`startUpdate`/`reconcileUpdateOnBoot`). Routes: `GET /api/system/update/check`, `POST /api/system/update`, `GET /api/system/update/status`. Types: `src/types/update.ts`. npm installs report as non-updatable.
|
**Self-update** (App Settings → Updates): in-app updater for **git-clone installs** supervised by systemd/launchd. Supervisors: `systemd` (user unit), `launchd` (GUI LaunchAgent, gui-domain kickstart), `launchd-daemon` (KeepAlive system LaunchDaemon on headless Macs — restarts rootlessly by killing the server PID and letting launchd respawn it; detected only when the daemon is bootstrapped AND KeepAlive), else `none` → "restart manually" message; on next boot a manual-restart status auto-completes when the running version matches the target. The update restarts the very process running it, so the real work runs in a DETACHED `scripts/self-update.sh` (`git checkout <release tag> && npm install && npm run build && restart`) that outlives the restart; it writes progress to `dataPath('update-status.json')`, which the browser polls across the connection drop. Channel = latest `codeman@X.Y.Z` release tag; dirty trees are auto-stashed. `src/web/self-update.ts` splits PURE helpers (semver/tag parsing, reconcile decision — unit-tested) from IO wrappers (`getInstallInfo`/`checkForUpdate`/`startUpdate`/`reconcileUpdateOnBoot`). Routes: `GET /api/system/update/check`, `POST /api/system/update`, `GET /api/system/update/status`. Types: `src/types/update.ts`. npm installs report as non-updatable.
|
||||||
|
|
||||||
**Port interfaces**: Routes declare dependencies via port interfaces (`src/web/ports/`). Routes use intersection types (e.g., `SessionPort & EventPort`).
|
**Port interfaces**: Routes declare dependencies via port interfaces (`src/web/ports/`). Routes use intersection types (e.g., `SessionPort & EventPort`).
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "aicodeman",
|
"name": "aicodeman",
|
||||||
"version": "0.9.9",
|
"version": "0.9.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "aicodeman",
|
"name": "aicodeman",
|
||||||
"version": "0.9.9",
|
"version": "0.9.10",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aicodeman",
|
"name": "aicodeman",
|
||||||
"version": "0.9.9",
|
"version": "0.9.10",
|
||||||
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export PUPPETEER_SKIP_DOWNLOAD="${PUPPETEER_SKIP_DOWNLOAD:-1}"
|
|||||||
REPO=""
|
REPO=""
|
||||||
TAG=""
|
TAG=""
|
||||||
SUPERVISOR="none"
|
SUPERVISOR="none"
|
||||||
|
SERVER_PID=""
|
||||||
STATUS_FILE=""
|
STATUS_FILE=""
|
||||||
UPDATE_ID=""
|
UPDATE_ID=""
|
||||||
FROM_VERSION=""
|
FROM_VERSION=""
|
||||||
@@ -50,6 +51,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--node) NODE="$2"; shift 2 ;;
|
--node) NODE="$2"; shift 2 ;;
|
||||||
--log) LOG="$2"; shift 2 ;;
|
--log) LOG="$2"; shift 2 ;;
|
||||||
--prev-sha) PREV_SHA="$2"; shift 2 ;;
|
--prev-sha) PREV_SHA="$2"; shift 2 ;;
|
||||||
|
--server-pid) SERVER_PID="$2"; shift 2 ;;
|
||||||
--stash) DO_STASH=1; shift ;;
|
--stash) DO_STASH=1; shift ;;
|
||||||
*) shift ;;
|
*) shift ;;
|
||||||
esac
|
esac
|
||||||
@@ -198,6 +200,19 @@ case "$SUPERVISOR" in
|
|||||||
|| fail "Build succeeded but launchd restart failed" "launchctl"
|
|| fail "Build succeeded but launchd restart failed" "launchctl"
|
||||||
}
|
}
|
||||||
;;
|
;;
|
||||||
|
launchd-daemon)
|
||||||
|
# System-level KeepAlive LaunchDaemon (headless Mac): kickstarting the system
|
||||||
|
# domain needs root, but we don't need it — kill the server and launchd
|
||||||
|
# respawns it on the new dist/ within ThrottleInterval seconds.
|
||||||
|
if [[ -n "$SERVER_PID" ]] && kill "$SERVER_PID" 2>/dev/null; then
|
||||||
|
: # respawn is launchd's job from here
|
||||||
|
else
|
||||||
|
MANUAL_CMD="sudo launchctl kickstart -k system/com.codeman.web"
|
||||||
|
write_status "completed-needs-manual-restart" "Update staged — restart Codeman to apply v$TO_VERSION."
|
||||||
|
echo "[self-update] launchd-daemon: could not signal server pid '$SERVER_PID' — manual restart required"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
MANUAL_CMD="pkill -f 'codeman.*web'; codeman web &"
|
MANUAL_CMD="pkill -f 'codeman.*web'; codeman web &"
|
||||||
write_status "completed-needs-manual-restart" "Update staged — restart Codeman to apply v$TO_VERSION."
|
write_status "completed-needs-manual-restart" "Update staged — restart Codeman to apply v$TO_VERSION."
|
||||||
|
|||||||
+67
-431
@@ -1,363 +1,84 @@
|
|||||||
# CLAUDE.md - Project Configuration
|
# CLAUDE.md
|
||||||
|
|
||||||
## Setup
|
<!--
|
||||||
Copy these files to your new project:
|
Generated by Codeman on [DATE]. This file is loaded into context at the
|
||||||
- `CLAUDE.md` → project root
|
start of every Claude Code session in this project.
|
||||||
- `.claude/settings.json` → `.claude/settings.json`
|
|
||||||
|
|
||||||
Then update the Project Overview section below.
|
Keep it short (target: under 200 lines). For each line ask: "would removing
|
||||||
|
this cause Claude to make mistakes?" If not, cut it. Don't document what
|
||||||
|
Claude can infer from the code itself (file layout, standard conventions,
|
||||||
|
APIs) — bloat causes Claude to ignore the rules that matter.
|
||||||
|
|
||||||
---
|
HTML comments like this one are stripped before loading, so fill-in notes
|
||||||
|
cost no context. If this file grows too big, split into path-scoped rules
|
||||||
|
in .claude/rules/*.md or import other files with @path/to/file syntax.
|
||||||
|
-->
|
||||||
|
|
||||||
|
This file guides Claude Code when working in this repository.
|
||||||
|
|
||||||
|
## Project
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
<!-- Update this section with project-specific details -->
|
|
||||||
- **Project Name**: [PROJECT_NAME]
|
- **Project Name**: [PROJECT_NAME]
|
||||||
- **Description**: [PROJECT_DESCRIPTION]
|
- **Description**: [PROJECT_DESCRIPTION]
|
||||||
- **Tech Stack**: [TECHNOLOGIES_USED]
|
|
||||||
- **Last Updated**: [DATE]
|
|
||||||
|
|
||||||
---
|
## Commands
|
||||||
|
|
||||||
|
<!-- List the exact commands Claude can't guess — fill in as the project
|
||||||
|
takes shape, then delete this comment:
|
||||||
|
|
||||||
|
| Task | Command |
|
||||||
|
|------|---------|
|
||||||
|
| Dev server | `npm run dev` |
|
||||||
|
| Test (single file) | `npm test -- test/<file>.test.ts` |
|
||||||
|
| Lint | `npm run lint` |
|
||||||
|
| Build | `npm run build` |
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
<!-- Only rules that differ from language/framework defaults, one line each:
|
||||||
|
- Use 2-space indentation
|
||||||
|
- ES modules only — never require()
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
- Full permissions are granted: read, write, edit, and execute without asking.
|
||||||
|
- Commit after every meaningful change; never batch unrelated work.
|
||||||
|
- Use conventional commits (`feat:` `fix:` `docs:` `refactor:` `test:` `chore:`); the message says what changed and why.
|
||||||
|
- Run the tests and linter before declaring any task done.
|
||||||
|
- Keep README and docs in sync with code changes.
|
||||||
|
|
||||||
## Codeman Environment
|
## Codeman Environment
|
||||||
|
|
||||||
This session is managed by **Codeman** and runs within a tmux session.
|
This session is managed by Codeman and runs inside tmux (`CODEMAN_MUX=1` confirms it).
|
||||||
|
|
||||||
**Important**: Check for `CODEMAN_MUX=1` environment variable to confirm.
|
- NEVER kill your own session: no `tmux kill-session`, `pkill tmux`, or `pkill claude`.
|
||||||
- Do NOT attempt to kill your own tmux session
|
- The session persists across disconnects — your work is safe.
|
||||||
- The session persists across disconnects - your work is safe
|
- Hooks may auto-format or validate after writes; unexpected tool behavior usually means a hook ran. Keep working.
|
||||||
- Token usage, costs, and background tasks are tracked externally
|
|
||||||
|
|
||||||
---
|
## Ralph Loop (Codeman Autonomous Mode)
|
||||||
|
|
||||||
## Work Principles
|
Start: `/ralph-loop:ralph-loop` · Cancel: `/ralph-loop:cancel-ralph` · Help: `/ralph-loop:help`
|
||||||
|
|
||||||
### Autonomy
|
You are in a Ralph loop when the prompt contains a completion phrase (e.g. `<promise>COMPLETE</promise>`). While looping:
|
||||||
Full permissions granted. Act decisively without asking - read, write, edit, execute freely.
|
|
||||||
|
|
||||||
### Git Discipline
|
- Work incrementally: one sub-task at a time — implement, verify, commit, move on.
|
||||||
- **Commit after every meaningful change** - never batch unrelated work
|
- Fix failing tests and lint errors before starting the next task.
|
||||||
- Use conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`
|
- Check `git log --oneline -10` / `git diff HEAD~1` before retrying an approach that already failed.
|
||||||
- Commit message = what changed + why (not how)
|
- Keep testing to ~20% of total effort; prioritize implementation. Don't refactor working code or add unrequested
|
||||||
|
features as busy work.
|
||||||
|
- If the prompt sets a minimum duration (e.g. "work for 4 hours"), record the start time (`date +%s`) and, when the
|
||||||
|
primary tasks finish early, keep generating useful related work (edge cases, coverage, docs, cleanup, hardening)
|
||||||
|
until the minimum time is reached.
|
||||||
|
|
||||||
### Documentation
|
Output the completion phrase ONLY when every requirement is verifiably done: all tests pass, lint is clean, the build
|
||||||
- Update README.md when adding features or changing setup
|
succeeds, and changes are committed. Never output it early — and never withhold it for busy work once everything is
|
||||||
- Update this file's session log after work sessions
|
genuinely done.
|
||||||
- Keep docs in sync with code changes
|
|
||||||
|
|
||||||
### Thinking
|
### RALPH_STATUS block (required)
|
||||||
Extended thinking is enabled. Use deep reasoning for complex architectural decisions, difficult bugs, and multi-file changes.
|
|
||||||
|
|
||||||
### Task Tracking (TodoWrite)
|
End EVERY response during a Ralph loop with exactly this block — Codeman parses it to track the loop:
|
||||||
**ALWAYS use TodoWrite** to track tasks. This is non-negotiable for anything beyond trivial single-step work.
|
|
||||||
|
|
||||||
**When to use TodoWrite:**
|
|
||||||
- Multi-step tasks (3+ steps)
|
|
||||||
- Bug fixes requiring investigation
|
|
||||||
- Feature implementations
|
|
||||||
- Any work where progress tracking helps
|
|
||||||
- When the user provides multiple requests
|
|
||||||
|
|
||||||
**How to use it:**
|
|
||||||
1. **Before starting**: Break down the work into discrete todos
|
|
||||||
2. **During work**: Mark each todo `in_progress` before starting, `completed` when done
|
|
||||||
3. **One at a time**: Only ONE todo should be `in_progress` at any moment
|
|
||||||
4. **Immediately**: Mark todos complete the moment they're done - don't batch
|
|
||||||
|
|
||||||
**Why this matters:**
|
|
||||||
- Gives the user visibility into your progress
|
|
||||||
- Prevents forgetting tasks mid-work
|
|
||||||
- Creates accountability checkpoints
|
|
||||||
- Makes complex work manageable
|
|
||||||
|
|
||||||
**Example workflow:**
|
|
||||||
```
|
|
||||||
User: "Add user authentication with JWT"
|
|
||||||
|
|
||||||
→ TodoWrite:
|
|
||||||
- [ ] Research existing auth patterns in codebase
|
|
||||||
- [ ] Implement JWT token generation
|
|
||||||
- [ ] Add login endpoint
|
|
||||||
- [ ] Add token validation middleware
|
|
||||||
- [ ] Add protected route example
|
|
||||||
- [ ] Write tests
|
|
||||||
|
|
||||||
→ Mark "Research existing auth patterns" as in_progress
|
|
||||||
→ Do the research
|
|
||||||
→ Mark as completed, mark next as in_progress
|
|
||||||
→ Continue until all done
|
|
||||||
```
|
|
||||||
|
|
||||||
**Anti-patterns to avoid:**
|
|
||||||
- Starting work without creating todos first
|
|
||||||
- Having multiple todos `in_progress` simultaneously
|
|
||||||
- Batching completions at the end
|
|
||||||
- Skipping TodoWrite for "simple" multi-step tasks
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## When to Use Agents
|
|
||||||
|
|
||||||
**Explore agent**: Codebase investigation, finding files, understanding architecture
|
|
||||||
```
|
|
||||||
"Use explore agent to find all authentication-related code"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parallel agents**: Independent tasks that don't conflict
|
|
||||||
```
|
|
||||||
"Research auth, database, and API modules in parallel using separate agents"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Background execution**: Long-running operations (tests, builds)
|
|
||||||
```
|
|
||||||
"Run the test suite in the background while I continue"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Sequential chaining**: When second task depends on first
|
|
||||||
```
|
|
||||||
"Use code-reviewer to find issues, then use fixer to resolve them"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Planning Mode (Automatic)
|
|
||||||
|
|
||||||
**Automatically enter planning mode** when ANY of these conditions apply:
|
|
||||||
- Multi-file changes (3+ files affected)
|
|
||||||
- Architectural decisions
|
|
||||||
- Unclear or evolving requirements
|
|
||||||
- Risk mitigation on core systems
|
|
||||||
- New feature implementation
|
|
||||||
- Refactoring existing functionality
|
|
||||||
|
|
||||||
**Do NOT ask** whether to enter planning mode - just enter it when conditions are met.
|
|
||||||
|
|
||||||
Planning mode flow: read-only exploration → create plan → get approval → execute.
|
|
||||||
|
|
||||||
**Skip planning mode** only for:
|
|
||||||
- Single-file bug fixes
|
|
||||||
- Typo corrections
|
|
||||||
- Simple config changes
|
|
||||||
- Tasks with explicit step-by-step instructions from user
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ralph Wiggum Loop (Autonomous Work Mode)
|
|
||||||
|
|
||||||
Ralph loops enable persistent, autonomous work on large tasks. When active, you continue iterating until completion criteria are met or the loop is cancelled.
|
|
||||||
|
|
||||||
### Starting a Ralph Loop
|
|
||||||
- Start: `/ralph-loop:ralph-loop`
|
|
||||||
- Cancel: `/ralph-loop:cancel-ralph`
|
|
||||||
- Help: `/ralph-loop:help`
|
|
||||||
|
|
||||||
### Time-Aware Loops
|
|
||||||
|
|
||||||
When the user specifies a **minimum duration** (e.g., "optimize for 8 hours", "work on this for 2 hours"), the loop becomes time-aware:
|
|
||||||
|
|
||||||
**At loop start:**
|
|
||||||
```bash
|
|
||||||
# Record start time
|
|
||||||
date +%s > /tmp/ralph_start_time
|
|
||||||
echo "Loop started at $(date)"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Check elapsed time periodically:**
|
|
||||||
```bash
|
|
||||||
START=$(cat /tmp/ralph_start_time)
|
|
||||||
NOW=$(date +%s)
|
|
||||||
ELAPSED_HOURS=$(echo "scale=2; ($NOW - $START) / 3600" | bc)
|
|
||||||
echo "Elapsed: $ELAPSED_HOURS hours"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Time-aware behavior:**
|
|
||||||
1. Complete all primary tasks from the user's prompt
|
|
||||||
2. After primary tasks done, check elapsed time
|
|
||||||
3. If minimum duration NOT reached:
|
|
||||||
- **Do NOT output completion phrase**
|
|
||||||
- Self-generate additional related tasks
|
|
||||||
- Continue working until minimum time elapsed
|
|
||||||
4. Only output completion phrase when:
|
|
||||||
- ALL primary tasks complete AND
|
|
||||||
- Minimum duration reached (or exceeded)
|
|
||||||
|
|
||||||
**Self-generating additional tasks when time remains:**
|
|
||||||
- Code optimization (performance, readability, DRY)
|
|
||||||
- Test coverage improvements
|
|
||||||
- Edge case handling
|
|
||||||
- Error message improvements
|
|
||||||
- Documentation gaps
|
|
||||||
- Security hardening
|
|
||||||
- Accessibility improvements
|
|
||||||
- Code cleanup and dead code removal
|
|
||||||
- Dependency updates
|
|
||||||
- Type safety improvements
|
|
||||||
|
|
||||||
**Example time-aware prompt:**
|
|
||||||
```
|
|
||||||
"Optimize the API endpoints for the next 4 hours. Focus on performance first,
|
|
||||||
then code quality. Minimum runtime: 4 hours."
|
|
||||||
Completion phrase: <promise>TIME_COMPLETE</promise>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Time-aware loop behavior:**
|
|
||||||
```
|
|
||||||
[Start loop, record timestamp]
|
|
||||||
[Complete primary optimization tasks - 2 hours elapsed]
|
|
||||||
[Check time: 2/4 hours - NOT done yet]
|
|
||||||
[Self-generate: "Add caching to database queries"]
|
|
||||||
[Self-generate: "Optimize N+1 queries"]
|
|
||||||
[Self-generate: "Add request batching"]
|
|
||||||
[Continue working... 4.5 hours elapsed]
|
|
||||||
[Check time: 4.5/4 hours - minimum reached]
|
|
||||||
[All tasks complete, tests pass]
|
|
||||||
<promise>TIME_COMPLETE</promise>
|
|
||||||
```
|
|
||||||
|
|
||||||
### How You Know You're in a Ralph Loop
|
|
||||||
|
|
||||||
The user started the loop with a prompt containing:
|
|
||||||
- Clear task requirements
|
|
||||||
- A **completion phrase** (e.g., `<promise>COMPLETE</promise>`)
|
|
||||||
- **Optional: minimum duration** (e.g., "for the next 4 hours")
|
|
||||||
- Iteration limits (handled by the system)
|
|
||||||
|
|
||||||
Your job: Keep working until ALL requirements are verifiably done AND minimum time reached (if specified), then output the exact completion phrase.
|
|
||||||
|
|
||||||
### Core Behaviors During Ralph Loop
|
|
||||||
|
|
||||||
**1. Work Incrementally**
|
|
||||||
- Complete one sub-task at a time
|
|
||||||
- Verify it works before moving to the next
|
|
||||||
- Don't try to do everything in one pass
|
|
||||||
|
|
||||||
**2. Commit Frequently**
|
|
||||||
- Commit after each meaningful completion
|
|
||||||
- Creates recovery points if something breaks
|
|
||||||
- Shows progress in git history
|
|
||||||
```
|
|
||||||
git add . && git commit -m "feat(auth): add token refresh endpoint"
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Self-Correct Relentlessly**
|
|
||||||
```
|
|
||||||
Loop:
|
|
||||||
1. Implement/fix
|
|
||||||
2. Run tests
|
|
||||||
3. If tests fail → read error, fix, go to 1
|
|
||||||
4. Run linter
|
|
||||||
5. If lint errors → fix, go to 1
|
|
||||||
6. Commit
|
|
||||||
7. Continue to next task
|
|
||||||
```
|
|
||||||
|
|
||||||
**4. Track Progress**
|
|
||||||
Update the session log in this file as you complete tasks:
|
|
||||||
```markdown
|
|
||||||
| Date | Tasks Completed | Files Changed | Notes |
|
|
||||||
|------|-----------------|---------------|-------|
|
|
||||||
| YYYY-MM-DD | Add auth endpoint | auth.ts, routes.ts | Tests passing |
|
|
||||||
```
|
|
||||||
|
|
||||||
**5. Use Git History When Stuck**
|
|
||||||
If something isn't working:
|
|
||||||
```bash
|
|
||||||
git log --oneline -10
|
|
||||||
git diff HEAD~1
|
|
||||||
```
|
|
||||||
See what you already tried. Don't repeat failed approaches.
|
|
||||||
|
|
||||||
**6. Completion Phrase = Contract**
|
|
||||||
Only output the completion phrase (e.g., `<promise>COMPLETE</promise>`) when:
|
|
||||||
- ALL requirements from the original prompt are done
|
|
||||||
- ALL tests pass
|
|
||||||
- ALL linting passes
|
|
||||||
- Changes are committed
|
|
||||||
|
|
||||||
**Never output the completion phrase early.** The loop only ends when you say it's done.
|
|
||||||
|
|
||||||
### What Makes Good Completion Criteria
|
|
||||||
|
|
||||||
The user should provide criteria that are:
|
|
||||||
- **Verifiable**: Tests pass, lint clean, build succeeds
|
|
||||||
- **Measurable**: "5 endpoints", "all files in src/", "zero errors"
|
|
||||||
- **Binary**: Done or not done, no ambiguity
|
|
||||||
|
|
||||||
If the original prompt has vague criteria, ask clarifying questions before starting heavy work.
|
|
||||||
|
|
||||||
### Self-Correction Pattern (Include in Your Work)
|
|
||||||
|
|
||||||
```
|
|
||||||
FOR EACH TASK:
|
|
||||||
1. Implement the change
|
|
||||||
2. Run tests (npm test, pytest, go test, cargo test, etc.)
|
|
||||||
- If fail → read error, fix, retry
|
|
||||||
3. Run linter (npm run lint, ruff, golangci-lint, etc.)
|
|
||||||
- If fail → fix, go to step 2
|
|
||||||
4. Verify manually if needed
|
|
||||||
5. Commit with descriptive message
|
|
||||||
6. Update session log
|
|
||||||
7. Move to next task
|
|
||||||
|
|
||||||
WHEN ALL TASKS DONE:
|
|
||||||
1. Run full test suite
|
|
||||||
2. Run full lint
|
|
||||||
3. Verify build succeeds
|
|
||||||
4. Review all changes: git diff main
|
|
||||||
5. Only then output completion phrase
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example: How to Think During Ralph Loop
|
|
||||||
|
|
||||||
**Original prompt**: "Add CRUD endpoints for todos with validation"
|
|
||||||
|
|
||||||
**Your approach**:
|
|
||||||
```
|
|
||||||
Task breakdown:
|
|
||||||
- [ ] GET /todos (list)
|
|
||||||
- [ ] POST /todos (create with validation)
|
|
||||||
- [ ] GET /todos/:id (single)
|
|
||||||
- [ ] PUT /todos/:id (update with validation)
|
|
||||||
- [ ] DELETE /todos/:id
|
|
||||||
- [ ] Tests for all endpoints
|
|
||||||
|
|
||||||
Starting with GET /todos...
|
|
||||||
[implement]
|
|
||||||
[test - passes]
|
|
||||||
[commit: "feat(todos): add GET /todos endpoint"]
|
|
||||||
[update session log]
|
|
||||||
|
|
||||||
Moving to POST /todos...
|
|
||||||
[implement]
|
|
||||||
[test - fails: validation not working]
|
|
||||||
[fix validation]
|
|
||||||
[test - passes]
|
|
||||||
[commit: "feat(todos): add POST /todos with validation"]
|
|
||||||
[update session log]
|
|
||||||
|
|
||||||
...continue until all done...
|
|
||||||
|
|
||||||
Final verification:
|
|
||||||
[npm test - all pass]
|
|
||||||
[npm run lint - clean]
|
|
||||||
[npm run build - succeeds]
|
|
||||||
|
|
||||||
<promise>COMPLETE</promise>
|
|
||||||
```
|
|
||||||
|
|
||||||
### When to NOT Output Completion Phrase
|
|
||||||
|
|
||||||
- Tests are failing (even one)
|
|
||||||
- Lint errors exist
|
|
||||||
- Build is broken
|
|
||||||
- You skipped a requirement
|
|
||||||
- You're unsure if something works
|
|
||||||
- **Minimum duration not reached** (for time-aware loops)
|
|
||||||
|
|
||||||
Instead: Fix the issue, verify, then complete. For time-aware loops: generate more tasks and keep improving until minimum time elapsed.
|
|
||||||
|
|
||||||
### RALPH_STATUS Block (Required During Ralph Loop)
|
|
||||||
|
|
||||||
At the **END of every response** during a Ralph Loop, output this structured status block:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
---RALPH_STATUS---
|
---RALPH_STATUS---
|
||||||
@@ -367,95 +88,10 @@ FILES_MODIFIED: <number>
|
|||||||
TESTS_STATUS: PASSING | FAILING | NOT_RUN
|
TESTS_STATUS: PASSING | FAILING | NOT_RUN
|
||||||
WORK_TYPE: IMPLEMENTATION | TESTING | DOCUMENTATION | REFACTORING
|
WORK_TYPE: IMPLEMENTATION | TESTING | DOCUMENTATION | REFACTORING
|
||||||
EXIT_SIGNAL: false | true
|
EXIT_SIGNAL: false | true
|
||||||
RECOMMENDATION: <one line summary of what to do next>
|
RECOMMENDATION: <one line: what to do next>
|
||||||
---END_RALPH_STATUS---
|
---END_RALPH_STATUS---
|
||||||
```
|
```
|
||||||
|
|
||||||
**Rules:**
|
- `EXIT_SIGNAL: true` only when ALL tasks are verifiably done — then also output the completion phrase.
|
||||||
- Output this block at the end of **every** response, no exceptions
|
- `STATUS: BLOCKED` when you need human input; describe the blocker in RECOMMENDATION.
|
||||||
- Set `EXIT_SIGNAL` to `true` ONLY when ALL tasks are verifiably done
|
- Never set `EXIT_SIGNAL: true` while tests are failing.
|
||||||
- Set `STATUS` to `BLOCKED` when you need human intervention
|
|
||||||
- Do NOT continue with busy work when `EXIT_SIGNAL` should be `true`
|
|
||||||
- Do NOT forget the status block — it is required for loop tracking
|
|
||||||
|
|
||||||
### Testing Limits
|
|
||||||
|
|
||||||
- **LIMIT testing to ~20% of total effort** per loop
|
|
||||||
- PRIORITIZE: Implementation > Documentation > Tests
|
|
||||||
- Only write tests for NEW functionality
|
|
||||||
- Do NOT refactor existing tests unless broken
|
|
||||||
- Do NOT run tests repeatedly without implementing new features
|
|
||||||
|
|
||||||
### Exit Scenarios (When to Set EXIT_SIGNAL)
|
|
||||||
|
|
||||||
| Scenario | STATUS | EXIT_SIGNAL | Action |
|
|
||||||
|----------|--------|-------------|--------|
|
|
||||||
| All tasks completed, tests pass | COMPLETE | true | Output completion phrase |
|
|
||||||
| No work remaining, specs done | COMPLETE | true | Output completion phrase |
|
|
||||||
| Making normal progress | IN_PROGRESS | false | Continue to next task |
|
|
||||||
| Test-only loop (no implementation) | IN_PROGRESS | false | Warn and shift to implementation |
|
|
||||||
| Stuck on same error repeatedly | BLOCKED | false | Describe blocker, request help |
|
|
||||||
| Needs human decision/intervention | BLOCKED | false | Describe what's needed |
|
|
||||||
|
|
||||||
**Anti-patterns to avoid:**
|
|
||||||
- Setting `EXIT_SIGNAL: true` when tests are failing
|
|
||||||
- Continuing to work when all tasks are genuinely done (busy work)
|
|
||||||
- Running the same failing test repeatedly without changing approach
|
|
||||||
- Adding features not in the original specifications
|
|
||||||
- Refactoring working code instead of completing assigned tasks
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Standards
|
|
||||||
|
|
||||||
### Before Writing
|
|
||||||
- Read existing code in the area you're modifying
|
|
||||||
- Follow existing patterns and conventions
|
|
||||||
- Check for similar implementations to reference
|
|
||||||
|
|
||||||
### During Implementation
|
|
||||||
- Keep changes focused and minimal
|
|
||||||
- Don't over-engineer
|
|
||||||
- Write tests for new functionality
|
|
||||||
|
|
||||||
### After Implementation
|
|
||||||
- Run tests
|
|
||||||
- Update docs if needed
|
|
||||||
- Commit with descriptive message
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hooks Awareness
|
|
||||||
|
|
||||||
This project may have hooks that auto-format code after writes or validate operations. If a tool call behaves unexpectedly, hooks are likely the cause. Continue working - they're intentional.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Log
|
|
||||||
|
|
||||||
| Date | Tasks Completed | Files Changed | Notes |
|
|
||||||
|------|-----------------|---------------|-------|
|
|
||||||
| [DATE] | Project created | CLAUDE.md | Initial setup |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current Task Queue
|
|
||||||
|
|
||||||
### Active Ralph Loop
|
|
||||||
**Status**: Not Active
|
|
||||||
**Completion Phrase**: -
|
|
||||||
|
|
||||||
### Pending Tasks
|
|
||||||
- [ ] <!-- Add tasks here -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Plans
|
|
||||||
|
|
||||||
<!-- Document plans before major implementations -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notes & Decisions
|
|
||||||
|
|
||||||
<!-- Track important decisions and context -->
|
|
||||||
@@ -15,18 +15,17 @@ import { fileURLToPath } from 'node:url';
|
|||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const BUNDLED_TEMPLATE_PATH = join(__dirname, 'case-template.md');
|
const BUNDLED_TEMPLATE_PATH = join(__dirname, 'case-template.md');
|
||||||
|
|
||||||
const MINIMAL_FALLBACK = `# CLAUDE.md - Project Configuration
|
const MINIMAL_FALLBACK = `# CLAUDE.md
|
||||||
|
|
||||||
|
<!-- Generated by Codeman on [DATE]. Add the commands, code style rules, and
|
||||||
|
workflow notes Claude can't infer from the code. Keep it short. -->
|
||||||
|
|
||||||
|
This file guides Claude Code when working in this repository.
|
||||||
|
|
||||||
|
## Project
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
- **Project Name**: [PROJECT_NAME]
|
- **Project Name**: [PROJECT_NAME]
|
||||||
- **Description**: [PROJECT_DESCRIPTION]
|
- **Description**: [PROJECT_DESCRIPTION]
|
||||||
- **Last Updated**: [DATE]
|
|
||||||
|
|
||||||
## Session Log
|
|
||||||
|
|
||||||
| Date | Tasks Completed | Files Changed | Notes |
|
|
||||||
|------|-----------------|---------------|-------|
|
|
||||||
| [DATE] | Project created | CLAUDE.md | Initial setup |
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-2
@@ -13,8 +13,12 @@
|
|||||||
* @module types/update
|
* @module types/update
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Which init system supervises the running server (decides how we restart it). */
|
/**
|
||||||
export type SupervisorKind = 'systemd' | 'launchd' | 'none';
|
* Which init system supervises the running server (decides how we restart it).
|
||||||
|
* `launchd-daemon` = a KeepAlive system-level LaunchDaemon (headless Macs, no GUI
|
||||||
|
* login): restart works by killing the server and letting launchd respawn it.
|
||||||
|
*/
|
||||||
|
export type SupervisorKind = 'systemd' | 'launchd' | 'launchd-daemon' | 'none';
|
||||||
|
|
||||||
/** How Codeman was installed — only `git` installs can self-update in place. */
|
/** How Codeman was installed — only `git` installs can self-update in place. */
|
||||||
export type InstallKind = 'git' | 'npm' | 'unknown';
|
export type InstallKind = 'git' | 'npm' | 'unknown';
|
||||||
|
|||||||
+28
-1
@@ -161,7 +161,9 @@ export function parseGitHubRepo(remoteUrl: string): { owner: string; repo: strin
|
|||||||
* persist — or null to leave it untouched.
|
* persist — or null to leave it untouched.
|
||||||
*
|
*
|
||||||
* Rules (see plan "Hardening"):
|
* Rules (see plan "Hardening"):
|
||||||
* - Terminal phases → untouched.
|
* - Terminal phases → untouched, EXCEPT `completed-needs-manual-restart`: once we
|
||||||
|
* boot into the staged target version the manual restart evidently happened, so
|
||||||
|
* it flips to `completed` (otherwise the stale instruction lingers in the UI).
|
||||||
* - Only the `restarting` marker (written right before the updater triggers our
|
* - Only the `restarting` marker (written right before the updater triggers our
|
||||||
* restart) flips to completed/failed by comparing running version vs. target.
|
* restart) flips to completed/failed by comparing running version vs. target.
|
||||||
* - Other in-flight phases are owned by the still-running updater scope — leave
|
* - Other in-flight phases are owned by the still-running updater scope — leave
|
||||||
@@ -174,6 +176,17 @@ export function reconcileStatusDecision(
|
|||||||
now: number
|
now: number
|
||||||
): UpdateStatus | null {
|
): UpdateStatus | null {
|
||||||
if (!status) return null;
|
if (!status) return null;
|
||||||
|
|
||||||
|
// A staged update that asked for a manual restart: if we're now running the
|
||||||
|
// target version, the user (or supervisor) did restart — mark it completed so
|
||||||
|
// the UI stops showing the stale "restart Codeman to apply" instruction.
|
||||||
|
if (status.phase === 'completed-needs-manual-restart') {
|
||||||
|
if (status.toVersion && runningVersion === status.toVersion) {
|
||||||
|
return { ...status, phase: 'completed', message: `Updated to v${runningVersion}`, updatedAt: now };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IN_FLIGHT_PHASES.has(status.phase)) return null;
|
if (!IN_FLIGHT_PHASES.has(status.phase)) return null;
|
||||||
|
|
||||||
if (status.phase === 'restarting') {
|
if (status.phase === 'restarting') {
|
||||||
@@ -275,6 +288,16 @@ function detectInstallKind(dir: string): InstallKind {
|
|||||||
export function detectSupervisor(): SupervisorKind {
|
export function detectSupervisor(): SupervisorKind {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
if (existsSync(join(homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`))) return 'launchd';
|
if (existsSync(join(homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`))) return 'launchd';
|
||||||
|
// Headless Macs (no GUI login → no gui domain) run Codeman as a system-level
|
||||||
|
// LaunchDaemon instead. Restarting one needs no root IF it has KeepAlive: the
|
||||||
|
// updater just kills the server and launchd respawns it on the new build. Only
|
||||||
|
// claim this supervisor when the daemon is actually bootstrapped and KeepAlive.
|
||||||
|
const daemonPlist = join('/Library/LaunchDaemons', `${LAUNCHD_LABEL}.plist`);
|
||||||
|
if (existsSync(daemonPlist)) {
|
||||||
|
const loaded = tryExec('launchctl', ['print', `system/${LAUNCHD_LABEL}`]) !== null;
|
||||||
|
const keepAlive = tryExec('plutil', ['-extract', 'KeepAlive', 'raw', '-o', '-', daemonPlist]);
|
||||||
|
if (loaded && keepAlive === 'true') return 'launchd-daemon';
|
||||||
|
}
|
||||||
return 'none';
|
return 'none';
|
||||||
}
|
}
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
@@ -535,6 +558,10 @@ export async function startUpdate(): Promise<StartUpdateResult> {
|
|||||||
process.execPath,
|
process.execPath,
|
||||||
'--log',
|
'--log',
|
||||||
logFile,
|
logFile,
|
||||||
|
// For the launchd-daemon restart path: the updater kills this PID and the
|
||||||
|
// KeepAlive daemon respawns the server on the freshly built dist/.
|
||||||
|
'--server-pid',
|
||||||
|
String(process.pid),
|
||||||
];
|
];
|
||||||
if (prevSha) args.push('--prev-sha', prevSha);
|
if (prevSha) args.push('--prev-sha', prevSha);
|
||||||
if (info.dirty) args.push('--stash');
|
if (info.dirty) args.push('--stash');
|
||||||
|
|||||||
@@ -153,4 +153,17 @@ describe('reconcileStatusDecision (boot handoff state machine)', () => {
|
|||||||
expect(out?.phase).toBe('failed');
|
expect(out?.phase).toBe('failed');
|
||||||
expect(out?.error).toContain('building');
|
expect(out?.error).toContain('building');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('needs-manual-restart + now running the target version → completed', () => {
|
||||||
|
const out = reconcileStatusDecision(base({ phase: 'completed-needs-manual-restart' }), '0.9.4', NOW);
|
||||||
|
expect(out?.phase).toBe('completed');
|
||||||
|
expect(out?.message).toContain('0.9.4');
|
||||||
|
expect(out?.updatedAt).toBe(NOW);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('needs-manual-restart + still on the old version → untouched (restart pending)', () => {
|
||||||
|
expect(reconcileStatusDecision(base({ phase: 'completed-needs-manual-restart' }), '0.9.3', NOW)).toBeNull();
|
||||||
|
const noTarget = base({ phase: 'completed-needs-manual-restart', toVersion: undefined });
|
||||||
|
expect(reconcileStatusDecision(noTarget, '0.9.4', NOW)).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
+33
-24
@@ -51,7 +51,7 @@ describe('generateClaudeMd', () => {
|
|||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
const result = generateClaudeMd('my-project');
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
expect(result).toContain(`**Last Updated**: ${today}`);
|
expect(result).toContain(`Generated by Codeman on ${today}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include Codeman environment section', () => {
|
it('should include Codeman environment section', () => {
|
||||||
@@ -61,55 +61,61 @@ describe('generateClaudeMd', () => {
|
|||||||
expect(result).toContain('CODEMAN_MUX=1');
|
expect(result).toContain('CODEMAN_MUX=1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include work principles', () => {
|
it('should include workflow rules', () => {
|
||||||
const result = generateClaudeMd('my-project');
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
expect(result).toContain('## Work Principles');
|
expect(result).toContain('## Workflow');
|
||||||
expect(result).toContain('### Autonomy');
|
expect(result).toContain('conventional commits');
|
||||||
expect(result).toContain('### Git Discipline');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include TodoWrite guidance', () => {
|
it('should include Ralph Loop section with slash commands', () => {
|
||||||
const result = generateClaudeMd('my-project');
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
expect(result).toContain('### Task Tracking (TodoWrite)');
|
expect(result).toContain('## Ralph Loop');
|
||||||
expect(result).toContain('**ALWAYS use TodoWrite**');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should include Ralph Wiggum Loop section', () => {
|
|
||||||
const result = generateClaudeMd('my-project');
|
|
||||||
|
|
||||||
expect(result).toContain('## Ralph Wiggum Loop');
|
|
||||||
expect(result).toContain('/ralph-loop:ralph-loop');
|
expect(result).toContain('/ralph-loop:ralph-loop');
|
||||||
expect(result).toContain('/ralph-loop:cancel-ralph');
|
expect(result).toContain('/ralph-loop:cancel-ralph');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include planning mode section', () => {
|
it('should include the RALPH_STATUS contract parsed by ralph-status-parser', () => {
|
||||||
const result = generateClaudeMd('my-project');
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
expect(result).toContain('## Planning Mode');
|
expect(result).toContain('---RALPH_STATUS---');
|
||||||
expect(result).toContain('Multi-file changes');
|
expect(result).toContain('---END_RALPH_STATUS---');
|
||||||
|
expect(result).toContain('STATUS: IN_PROGRESS | COMPLETE | BLOCKED');
|
||||||
|
expect(result).toContain('EXIT_SIGNAL: false | true');
|
||||||
|
expect(result).toContain('completion phrase');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include session log table', () => {
|
it('should stay under the 200-line CLAUDE.md guidance', () => {
|
||||||
const result = generateClaudeMd('my-project');
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
expect(result).toContain('## Session Log');
|
expect(result.split('\n').length).toBeLessThan(200);
|
||||||
expect(result).toContain('| Date | Tasks Completed | Files Changed | Notes |');
|
});
|
||||||
|
|
||||||
|
it('should not include legacy bloat sections', () => {
|
||||||
|
const result = generateClaudeMd('my-project');
|
||||||
|
|
||||||
|
expect(result).not.toContain('## Session Log');
|
||||||
|
expect(result).not.toContain('TodoWrite');
|
||||||
|
expect(result).not.toContain('## Planning Mode');
|
||||||
|
expect(result).not.toContain('[TECHNOLOGIES_USED]');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('custom template', () => {
|
describe('custom template', () => {
|
||||||
it('should use custom template when provided and exists', () => {
|
it('should use custom template when provided and exists', () => {
|
||||||
const templatePath = join(testDir, 'custom-template.md');
|
const templatePath = join(testDir, 'custom-template.md');
|
||||||
writeFileSync(templatePath, `
|
writeFileSync(
|
||||||
|
templatePath,
|
||||||
|
`
|
||||||
# [PROJECT_NAME]
|
# [PROJECT_NAME]
|
||||||
|
|
||||||
Description: [PROJECT_DESCRIPTION]
|
Description: [PROJECT_DESCRIPTION]
|
||||||
Date: [DATE]
|
Date: [DATE]
|
||||||
|
|
||||||
Custom content here.
|
Custom content here.
|
||||||
`);
|
`
|
||||||
|
);
|
||||||
|
|
||||||
const result = generateClaudeMd('my-project', 'Test desc', templatePath);
|
const result = generateClaudeMd('my-project', 'Test desc', templatePath);
|
||||||
|
|
||||||
@@ -120,11 +126,14 @@ Custom content here.
|
|||||||
|
|
||||||
it('should replace all placeholder occurrences', () => {
|
it('should replace all placeholder occurrences', () => {
|
||||||
const templatePath = join(testDir, 'multi-placeholder.md');
|
const templatePath = join(testDir, 'multi-placeholder.md');
|
||||||
writeFileSync(templatePath, `
|
writeFileSync(
|
||||||
|
templatePath,
|
||||||
|
`
|
||||||
[PROJECT_NAME] is a project.
|
[PROJECT_NAME] is a project.
|
||||||
The name is [PROJECT_NAME].
|
The name is [PROJECT_NAME].
|
||||||
About [PROJECT_NAME]: [PROJECT_DESCRIPTION]
|
About [PROJECT_NAME]: [PROJECT_DESCRIPTION]
|
||||||
`);
|
`
|
||||||
|
);
|
||||||
|
|
||||||
const result = generateClaudeMd('awesome-app', 'Cool stuff', templatePath);
|
const result = generateClaudeMd('awesome-app', 'Cool stuff', templatePath);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user