Skip to content

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”

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.

  • Create a feature branch for this work
  • Commit after each logical chunk
  • Run npm run build after code changes — must be clean
  • Report any failures to operator immediately
  • Do NOT merge — operator merges

git checkout dev && git pull origin dev
git checkout -b feature/cli-rewrite

Phase 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.ts
rm src/scQueue.ts
rm src/scRuleset.ts
rm src/openVSCode.ts
rm src/session.ts

Remove all imports for deleted modules:

  • Remove import of startSession, pauseSession, resumeSession, endSession, getActiveSession, formatDuration, getElapsedMs from ./session
  • Remove import of showHelp from ./scHelp
  • Remove import of listQueue, addToQueue, markQueueDone, showNextQueue, getOpenQueueItems from ./scQueue
  • Remove import of handleRuleset from ./scRuleset

Remove these functions entirely:

  • handleStart (being rewritten — see Phase 2)
  • handleStatus
  • handleDone
  • handleOpen
  • handleBuild (being replaced by new handleStart)
  • formatQueueForHandoff

Remove these cases from the switch statement:

  • start (being rewritten)
  • pause
  • resume
  • done
  • status
  • open
  • build
  • queue
  • ruleset

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 references
npm run build

Must be clean. Fix any import errors from removed modules.

git add -A
git commit -m "feat: remove 11 CLI commands per decision-0002
Removed: build, end, done, exit, status, open, ruleset, help, history, log, queue
Deleted: scHelp.ts, scQueue.ts, scRuleset.ts, openVSCode.ts, session.ts
Remaining: 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;
npm run build
sc start scaffolder

Should launch Harley in the scaffolder project directory. Exit Harley with /exit after confirming it started in the right directory.

git add -A
git 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."

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:\\Claude
  • D:\\

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).

npm run build

Operation 13 — Test with no defaultDrive

Section titled “Operation 13 — Test with no defaultDrive”

Temporarily clear defaultDrive:

sc config set defaultDrive ""
sc start scaffolder

Should error with “No default drive configured” — NOT fall back to a hardcoded path.

Restore:

sc config set defaultDrive "/mnt/d/scaffolder-engine/projects"
git add -A
git 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"

sc config list

Should show all config values cleanly.

sc config get chatAiName
sc config get defaultDrive
sc config get nonExistentKey

First two should return values. Third should error clearly.

sc config set chatAiName "TestName"
sc config get chatAiName
sc config set chatAiName "Joker"

Should set, verify, and restore.

sc config set
sc config get
sc config
sc 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.

List every test and pass/fail. If any fail, fix the issue before continuing.

gh issue close 273 --comment "Full test pass complete. All sc config subcommands working: list, get, set. Edge cases handled."

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" --rollback

Should 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.

Delete the test project directory.

List every test and pass/fail. If any fail, fix the issue before continuing.

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.

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.

git add -A
git 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."

npm run build

Must be clean with zero warnings.

sc --version

Should report current version.

sc create "test-dry-run" --dry-run

Should show correct output with no hardcoded paths.

git push origin feature/cli-rewrite
gh 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-0002
Rewrites sc start to launch Harley on a project (replaces sc build)
Removes all hardcoded D: drive fallbacks — config.defaultDrive required
Adds session-end pre-exit hook for automatic state writes
Full test passes on sc config and sc update
Closes #251, closes #269, closes #273"

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)