GITHUB-ISSUES-2026-03-21-v0100-cli-rewrite.md — Build instructions for Harley
GITHUB-ISSUES-2026-03-21-v0100-cli-rewrite.md — Build instructions for Harley
Section titled “GITHUB-ISSUES-2026-03-21-v0100-cli-rewrite.md — Build instructions for Harley”Context
Section titled “Context”Joker session: planning — v0.10.0 issue sweep — 2026-03-21 This file covers: CLI rewrite per decision-0002, hardcoded path fix (#251), sc update test pass (#269), sc config test pass (#273).
Run this AFTER the first issue file (v0100-sweep) has been merged. The repo root is now projects/scaffolder/ per decision-0003.
Rules for Harley
Section titled “Rules for Harley”- Create a feature branch for this work
- Commit after each logical chunk
- Run
npm run buildafter code changes — must be clean - Report any failures to operator immediately
- Do NOT merge — operator merges
Phase 0 — Setup
Section titled “Phase 0 — Setup”Operation 1 — Create feature branch
Section titled “Operation 1 — Create feature branch”git checkout dev && git pull origin devgit checkout -b feature/cli-rewritePhase 1 — Remove dead CLI commands (decision-0002)
Section titled “Phase 1 — Remove dead CLI commands (decision-0002)”Remove 11 commands. Keep: create, start (rewritten), config, update.
Operation 2 — Delete source files for removed commands
Section titled “Operation 2 — Delete source files for removed commands”rm src/scHelp.tsrm src/scQueue.tsrm src/scRuleset.tsrm src/openVSCode.tsrm src/session.tsOperation 3 — Rewrite src/sc.ts
Section titled “Operation 3 — Rewrite src/sc.ts”Remove all imports for deleted modules:
- Remove import of
startSession, pauseSession, resumeSession, endSession, getActiveSession, formatDuration, getElapsedMsfrom./session - Remove import of
showHelpfrom./scHelp - Remove import of
listQueue, addToQueue, markQueueDone, showNextQueue, getOpenQueueItemsfrom./scQueue - Remove import of
handleRulesetfrom./scRuleset
Remove these functions entirely:
handleStart(being rewritten — see Phase 2)handleStatushandleDonehandleOpenhandleBuild(being replaced by newhandleStart)formatQueueForHandoff
Remove these cases from the switch statement:
start(being rewritten)pauseresumedonestatusopenbuildqueueruleset
Remove pause and resume from the usage/help text.
Update the usage text to show only:
Usage: sc <command> [options]
Commands: init Set up global configuration config [set|get|list] Manage configuration create "project-name" Create a new project start [project] Start Harley on a project update [project] Update a project to latest version migrate-path [old] [new] Update path referencesOperation 4 — Build and verify
Section titled “Operation 4 — Build and verify”npm run buildMust be clean. Fix any import errors from removed modules.
Operation 5 — Commit
Section titled “Operation 5 — Commit”git add -Agit commit -m "feat: remove 11 CLI commands per decision-0002
Removed: build, end, done, exit, status, open, ruleset, help, history, log, queueDeleted: scHelp.ts, scQueue.ts, scRuleset.ts, openVSCode.ts, session.tsRemaining: create, start (to be rewritten), config, update, init, migrate-path"Phase 2 — Rewrite sc start (decision-0002)
Section titled “Phase 2 — Rewrite sc start (decision-0002)”sc start <project> should: resolve the project path, verify it exists, cd there,
and launch Harley (claude). Session-start skill handles the rest automatically.
Operation 6 — Write new handleStart in src/sc.ts
Section titled “Operation 6 — Write new handleStart in src/sc.ts”Replace the old handleStart with:
async function handleStart(args: string[]): Promise<void> { const { execSync } = await import("child_process"); const project = args[0];
if (!project) { console.error("Usage: sc start [project]"); process.exit(1); }
const config = getConfig(); const basePath = config.defaultDrive;
if (!basePath) { console.error("No default drive configured. Run sc init or sc config set defaultDrive [path]"); process.exit(1); }
const projectPath = path.join(basePath, project);
if (!fs.existsSync(projectPath)) { console.error(`Project not found: ${projectPath}`); console.error(`Looked in: ${basePath}`); process.exit(1); }
console.log(`Starting Harley on "${project}" at ${projectPath}`);
try { execSync("harley", { cwd: projectPath, stdio: "inherit" }); } catch { // Harley may exit non-zero; that's fine }}Add start back to the switch statement:
case "start": await handleStart(args.slice(1)); break;Operation 7 — Build and verify
Section titled “Operation 7 — Build and verify”npm run buildOperation 8 — Test sc start
Section titled “Operation 8 — Test sc start”sc start scaffolderShould launch Harley in the scaffolder project directory. Exit Harley with /exit after confirming it started in the right directory.
Operation 9 — Commit
Section titled “Operation 9 — Commit”git add -Agit commit -m "feat: rewrite sc start to launch Harley on a project
sc start <project> resolves path from config, verifies it exists, launches Harley.Replaces sc build. Session-start skill handles state reading and goal confirmation."Phase 3 — Fix hardcoded D: drive (#251)
Section titled “Phase 3 — Fix hardcoded D: drive (#251)”Multiple places in the codebase fall back to /mnt/d/Claude/projects when
config.defaultDrive is empty. Remove all hardcoded fallbacks. If defaultDrive
is not set, error out and tell the user to run sc init or sc config set.
Operation 10 — Search for hardcoded paths
Section titled “Operation 10 — Search for hardcoded paths”Search all .ts files in src/ for:
/mnt/d/Claude/mnt/d/D:\\ClaudeD:\\
List every occurrence.
Operation 11 — Replace hardcoded fallbacks
Section titled “Operation 11 — Replace hardcoded fallbacks”Every place that does:
const basePath = config.defaultDrive || "/mnt/d/Claude/projects";Replace with:
const basePath = config.defaultDrive;if (!basePath) { console.error("No default drive configured. Run sc init or sc config set defaultDrive [path]"); process.exit(1);}Apply to ALL files that have this pattern (sc.ts, updateProject.ts, any others found).
Also check handleInit in sc.ts — the default drive prompt should not pre-fill
with any hardcoded path. Empty default is correct.
Also check config.ts — defaultScaleTier defaults to “hobby”. Change to “basic”
per decision-0001 (tier rename).
Operation 12 — Build and verify
Section titled “Operation 12 — Build and verify”npm run buildOperation 13 — Test with no defaultDrive
Section titled “Operation 13 — Test with no defaultDrive”Temporarily clear defaultDrive:
sc config set defaultDrive ""sc start scaffolderShould error with “No default drive configured” — NOT fall back to a hardcoded path.
Restore:
sc config set defaultDrive "/mnt/d/scaffolder-engine/projects"Operation 14 — Commit
Section titled “Operation 14 — Commit”git add -Agit commit -m "fix: replace hardcoded D: drive with dynamic path from config
All commands now require defaultDrive to be set in config.No more fallback to /mnt/d/Claude/projects.Errors clearly direct user to sc init or sc config set.
Closes #251"Phase 4 — sc config test pass (#273)
Section titled “Phase 4 — sc config test pass (#273)”Operation 15 — Test sc config list
Section titled “Operation 15 — Test sc config list”sc config listShould show all config values cleanly.
Operation 16 — Test sc config get
Section titled “Operation 16 — Test sc config get”sc config get chatAiNamesc config get defaultDrivesc config get nonExistentKeyFirst two should return values. Third should error clearly.
Operation 17 — Test sc config set
Section titled “Operation 17 — Test sc config set”sc config set chatAiName "TestName"sc config get chatAiNamesc config set chatAiName "Joker"Should set, verify, and restore.
Operation 18 — Test edge cases
Section titled “Operation 18 — Test edge cases”sc config setsc config getsc configsc config set defaultDrive ""sc config set defaultDrive "/mnt/d/scaffolder-engine/projects"First three should show usage errors. Empty set should work (clears value). Last one restores.
Operation 19 — Report results
Section titled “Operation 19 — Report results”List every test and pass/fail. If any fail, fix the issue before continuing.
Operation 20 — Close #273 if all pass
Section titled “Operation 20 — Close #273 if all pass”gh issue close 273 --comment "Full test pass complete. All sc config subcommands working: list, get, set. Edge cases handled."Phase 5 — sc update test pass (#269)
Section titled “Phase 5 — sc update test pass (#269)”Operation 21 — Create a test project
Section titled “Operation 21 — Create a test project”sc create "update-test-project"Operation 22 — Test sc update on the test project
Section titled “Operation 22 — Test sc update on the test project”sc update "update-test-project"Should complete without errors. Verify templates were applied.
Operation 23 — Test sc update —rollback
Section titled “Operation 23 — Test sc update —rollback”sc update "update-test-project" --rollbackShould restore to previous state. Verify.
Operation 24 — Test sc update on nonexistent project
Section titled “Operation 24 — Test sc update on nonexistent project”sc update "does-not-exist"Should error clearly.
Operation 25 — Clean up test project
Section titled “Operation 25 — Clean up test project”Delete the test project directory.
Operation 26 — Report results
Section titled “Operation 26 — Report results”List every test and pass/fail. If any fail, fix the issue before continuing.
Operation 27 — Close #269 if all pass
Section titled “Operation 27 — Close #269 if all pass”gh issue close 269 --comment "Full test pass complete. sc update and sc update --rollback working. Error handling verified."Phase 6 — Add session-end hook (decision-0002)
Section titled “Phase 6 — Add session-end hook (decision-0002)”Claude Code hooks are configured in .claude/settings.local.json.
Operation 28 — Read current .claude/settings.local.json
Section titled “Operation 28 — Read current .claude/settings.local.json”Check what exists. May need to create or update.
Operation 29 — Add pre-exit hook
Section titled “Operation 29 — Add pre-exit hook”The hook should fire before Harley exits and run the session-end behavior:
- Commit any uncommitted work
- Write ai-sessions/CURRENT-CODE.md
- Write session log to ai-sessions/logs/
- Commit session files
Check Claude Code documentation for hook configuration format. If hooks are not yet supported in the current Claude Code version, document this and create a GitHub issue to track it for when hooks become available.
Confidence note: I am ~80% confident Claude Code supports pre-exit hooks via settings.local.json. Harley should verify by checking Claude Code docs or testing. If not supported, skip this operation and report back.
Operation 30 — Commit hook config
Section titled “Operation 30 — Commit hook config”git add -Agit commit -m "feat: add session-end pre-exit hook
Automatic session state write and log on Harley exit.Operator never needs to remember to end sessions."Phase 7 — Final build and PR
Section titled “Phase 7 — Final build and PR”Operation 31 — Clean build
Section titled “Operation 31 — Clean build”npm run buildMust be clean with zero warnings.
Operation 32 — Verify sc —version
Section titled “Operation 32 — Verify sc —version”sc --versionShould report current version.
Operation 33 — Verify sc create dry-run
Section titled “Operation 33 — Verify sc create dry-run”sc create "test-dry-run" --dry-runShould show correct output with no hardcoded paths.
Operation 34 — Push and open PR
Section titled “Operation 34 — Push and open PR”git push origin feature/cli-rewritegh pr create --base dev --title "feat: CLI rewrite — remove 11 commands, rewrite sc start, fix hardcoded paths, add session hook" --body "## What this does
Removes 11 unused CLI commands per decision-0002Rewrites sc start to launch Harley on a project (replaces sc build)Removes all hardcoded D: drive fallbacks — config.defaultDrive requiredAdds session-end pre-exit hook for automatic state writesFull test passes on sc config and sc update
Closes #251, closes #269, closes #273"After this file
Section titled “After this file”Joker session needed for:
- #257 — README rewrite (content not produced yet)
- #300 — End-of-milestone docs audit
Then: release flow (dev → staging → main → tag v0.10.0)