Skip to content
Back to skills

Refactor Rewrite

BSecurity

Refactor vs rewrite decision analysis engine. Evaluates brownfield codebases

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 5, 2026
developmenttypescriptpythonrustgojavashellbashreactvueangular

Works with

  • claude code
  • cursor
  • vscode
  • terminal
  • cli
  • api

Security analysis

B88/100
  • criticalSends environment variables or credentials to an external URL

Pro scans all 2 files and shows the line behind each finding

Scanned November 5, 2026

npx -y skills add nightgauge/nightgauge --skill refactor-rewrite --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Refactor Rewrite?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Refactor Rewrite
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/nightgauge-refactor-rewrite/badge)](https://www.skillsdirectory.com/skills/nightgauge-refactor-rewrite)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: refactor-rewrite
description: Refactor vs rewrite decision analysis engine. Evaluates brownfield codebases
  across 8 dimensions to produce data-driven recommendations with confidence
  levels, risk/benefit matrices, and hybrid approach suggestions. Use when deciding
  whether to refactor or rewrite a legacy component before committing to a modernization path.
license: Apache-2.0
metadata:
  author: nightgauge
  version: "1.2.0"
  source: https://github.com/nightgauge/nightgauge
allowed-tools: Read Write Edit Glob Grep Bash Task
orchestration:
  mode: phased
  phase: refactor-analysis
  ceiling: fanout
  units:
    - id: code-quality
      role: dimension-worker
      promptRef: SKILL.md#phase-2-code-quality-analysis
    - id: test-coverage
      role: dimension-worker
      promptRef: SKILL.md#phase-3-test-coverage-analysis
    - id: dependency-coupling
      role: dimension-worker
      promptRef: SKILL.md#phase-4-dependency-coupling-analysis
    - id: business-logic
      role: dimension-worker
      promptRef: SKILL.md#phase-5-business-logic--tech-stack-analysis
    - id: team-effort
      role: dimension-worker
      promptRef: SKILL.md#phase-6-team--effort-analysis
  judge:
    mode: merge
    quorum: 1
    promptRef: SKILL.md#phase-7-decision-engine
disable-model-invocation: true
---

# Nightgauge Refactor vs Rewrite Analysis

## Description

Decision analysis engine that evaluates brownfield codebases across 8 dimensions
to produce a data-driven recommendation: **Refactor**, **Rewrite**, or
**Hybrid**. Uses deterministic tool commands (bash, grep, git) for metric
collection; AI interprets raw data into structured findings with 0-100 scoring,
confidence levels, risk/benefit matrices, and hybrid approach suggestions (e.g.,
strangler fig pattern).

**Use Cases:**

- Deciding whether to refactor or rewrite an aging codebase
- Evaluating individual modules/components for mixed-strategy modernization
- Providing stakeholders with data-driven modernization recommendations
- Producing structured input for `/nightgauge:modernize-plan`
- Comparing refactor vs rewrite trade-offs across monorepo packages

**When to Use:**

- When inheriting a brownfield codebase and deciding the modernization approach
- Before committing to a major rewrite effort
- When teams disagree on refactor vs rewrite — to ground the discussion in data
- After running `/nightgauge:health-check` and
  `/nightgauge:security-audit` for enriched analysis
- When evaluating whether a strangler fig or branch-by-abstraction approach fits

**Relationship to Other Skills:**

| Skill            | Focus                               | Relationship              |
| ---------------- | ----------------------------------- | ------------------------- |
| Health Check     | Codebase quality (6 dims)           | Optional input (upstream) |
| Security Audit   | Security posture (7 dims)           | Optional input (upstream) |
| Test Scaffold    | Coverage gaps + test generation     | Optional input (upstream) |
| Refactor/Rewrite | Refactor vs rewrite decision        | **This skill**            |
| Dep Modernize    | Dependency update engine            | Peer context (optional)   |
| Modernize Plan   | Phased roadmap from all assessments | Consumer (downstream)     |

## Invocation

| Tool        | Command                                    |
| ----------- | ------------------------------------------ |
| Claude Code | `/nightgauge:refactor-rewrite [options]`   |
| Copilot     | Invoke via Agent Skills extension          |
| Cursor      | Run via Agent Skills or direct SKILL.md    |
| Headless    | `claude -p "/nightgauge:refactor-rewrite"` |

## Arguments

| Argument              | Description                           | Default |
| --------------------- | ------------------------------------- | ------- |
| `--path DIR`          | Root directory to assess              | `.`     |
| `--package PKG`       | Monorepo package filter               | all     |
| `--module MODULE`     | Specific module/component to analyze  | all     |
| `--dimensions DIMS`   | Comma-separated dimensions            | all     |
| `--format FORMAT`     | Output: `summary`, `json`, `both`     | `both`  |
| `--output FILE`       | JSON output path                      | auto    |
| `--skip-coverage-run` | Skip running coverage tools           | `false` |
| `--team-size N`       | Team size for effort estimates        | `1`     |
| `--timeline WEEKS`    | Available timeline for effort context | `12`    |

### Examples

```bash
# Full refactor-vs-rewrite analysis of current directory
/nightgauge:refactor-rewrite

# Assess specific directory
/nightgauge:refactor-rewrite --path /path/to/project

# Analyze a specific module only
/nightgauge:refactor-rewrite --module src/legacy-auth/

# Only analyze code quality and dependency coupling
/nightgauge:refactor-rewrite --dimensions code-quality,dependency-coupling

# JSON output only
/nightgauge:refactor-rewrite --format json

# Estimate effort for a team of 4 over 16 weeks
/nightgauge:refactor-rewrite --team-size 4 --timeline 16

# Assess specific monorepo package
/nightgauge:refactor-rewrite --package packages/api-server
```

---

## Prerequisites

- Bash shell
- `jq` installed (for JSON processing)
- Ecosystem tools are optional — the skill gracefully degrades when audit tools
  or coverage runners are not installed
- Git history is optional but strongly recommended for team expertise and change
  frequency analysis

**CRITICAL**: This skill runs headless. Do NOT use AskUserQuestion. Make
autonomous decisions or fail with clear error.

---

## Analysis Dimensions

### 1. Code Quality (Weight: 0.15)

Evaluates code hygiene, maintainability indicators, and complexity proxies.

**Deterministic Signals:**

- Linting configuration presence (ESLint, Ruff, clippy, etc.)
- Formatter configuration presence (Prettier, Black, etc.)
- TODO/FIXME/HACK/XXX comment counts
- Average file size (lines of code)
- Largest files (complexity hotspots)
- Cyclomatic complexity proxies (branching construct counts per file)

**AI Interpretation:**

Assess overall maintainability. High complexity + no linting + many TODOs
suggests rewrite. Low complexity + good tooling suggests refactor.

**Scoring:**

| Score Range | Condition                                         |
| ----------- | ------------------------------------------------- |
| 90-100      | Linter + formatter + <5 TODOs + avg file <200 LOC |
| 70-89       | Linter present + <20 TODOs + moderate file sizes  |
| 50-69       | Some tooling + moderate debt markers              |
| 30-49       | No linter + many TODOs + large files              |
| 0-29        | No tooling + >100 TODOs + very large files        |

**Decision Signal:**

- Score > 60: Favors **refactor** (code is maintainable enough to improve
  incrementally)
- Score < 30: Favors **rewrite** (technical debt too deep for incremental
  improvement)

### 2. Test Coverage (Weight: 0.15)

Evaluates the safety net available for refactoring.

**Deterministic Signals:**

- Test framework detection (vitest, jest, pytest, go test, cargo test, junit)
- Test-to-source file ratio
- Coverage configuration presence
- Coverage percentage (if available or runnable)

**AI Interpretation:**

Assess whether existing tests provide adequate safety for refactoring. Low
coverage makes refactoring risky — changes break unknown behavior.

**Scoring:**

| Score Range | Condition                                 |
| ----------- | ----------------------------------------- |
| 90-100      | Framework + coverage config + ratio > 0.8 |
| 70-89       | Framework + some tests + ratio > 0.5      |
| 50-69       | Framework present + ratio > 0.2           |
| 30-49       | Few test files, no coverage config        |
| 0-29        | No test framework or no test files        |

**Decision Signal:**

- Score > 50: Favors **refactor** (adequate safety net exists)
- Score < 20: Favors **rewrite** (unsafe to refactor without tests — too risky
  to change existing code incrementally)

### 3. Dependency Coupling (Weight: 0.15)

Evaluates how entangled modules are and whether isolation is feasible.

**Deterministic Signals:**

- Import/require graph construction (per-file import analysis)
- Circular dependency detection
- Fan-in (files importing a module) and fan-out (modules imported by a file)
- Module boundary violations (cross-layer imports)

**AI Interpretation:**

Assess entanglement level. High circular dependencies and deep coupling make
incremental refactoring extremely difficult — changes cascade unpredictably.

**Scoring:**

| Score Range | Condition                                              |
| ----------- | ------------------------------------------------------ |
| 90-100      | No circular deps, clear module boundaries, low fan-out |
| 70-89       | <5% circular deps, mostly clean boundaries             |
| 50-69       | Some circular deps, moderate coupling                  |
| 30-49       | >15% circular deps, blurred module boundaries          |
| 0-29        | >30% circular deps, no discernible module structure    |

**Decision Signal:**

- Score > 60: Favors **refactor** (modules can be improved in isolation)
- Score 40-60: Favors **hybrid** (some modules refactorable, others need
  rewrite)
- Score < 30: Favors **rewrite** (entanglement prevents incremental change)

### 4. Business Logic Extraction (Weight: 0.10)

Evaluates whether core business rules can be identified and preserved.

**Deterministic Signals:**

- Service/domain layer detection (files matching service, domain, model, entity
  patterns)
- Separation of concerns indicators (distinct layers vs monolithic files)
- Business logic density (ratio of domain files to infrastructure files)
- Configuration externalization (env vars, config files vs hardcoded values)

**AI Interpretation:**

Assess whether business logic is identifiable and extractable. If rules are
scattered across infrastructure code, refactoring risks losing business logic.

**Scoring:**

| Score Range | Condition                                           |
| ----------- | --------------------------------------------------- |
| 90-100      | Clear domain layer, well-separated concerns         |
| 70-89       | Identifiable business logic, some mixing with infra |
| 50-69       | Business logic partially extractable                |
| 30-49       | Logic deeply embedded in infrastructure             |
| 0-29        | No discernible business logic layer                 |

**Decision Signal:**

- Score > 60: Favors **refactor** (business rules can be preserved during
  incremental changes)
- Score < 30: Favors **rewrite** (business logic must be reverse-engineered
  anyway)

### 5. Tech Stack Viability (Weight: 0.15)

Evaluates whether the current technology stack is viable long-term.

**Deterministic Signals:**

- Framework/runtime version detection (package.json engines, go.mod, etc.)
- Major version gap from latest (current vs latest available)
- End-of-life (EOL) status checks (Node.js LTS schedule, Python EOL, etc.)
- Community health indicators (last release date, open issues)

**AI Interpretation:**

Assess whether the stack is still viable. EOL runtimes with no security patches
make refactoring pointless — the entire platform needs replacement.

**Scoring:**

| Score Range | Condition                                         |
| ----------- | ------------------------------------------------- |
| 90-100      | Current LTS, latest major, active community       |
| 70-89       | 1 major behind, still supported, active releases  |
| 50-69       | 2 majors behind, approaching EOL                  |
| 30-49       | EOL approaching within 12 months, sparse releases |
| 0-29        | EOL reached, no security patches, dead community  |

**Decision Signal:**

- Score > 60: Favors **refactor** (stack is viable, worth investing in)
- Score < 30: Favors **rewrite** (stack is dying, refactoring on a dead platform
  is wasted effort)

### 6. Team Expertise (Weight: 0.05)

Evaluates team familiarity with the codebase and technology.

**Deterministic Signals:**

- Git author count (unique contributors in last 90/180/365 days)
- Recent commit frequency (commits per week in last 90 days)
- Language familiarity proxy (files per language, contributor distribution)
- Bus factor estimate (% of code touched by top contributor)

**AI Interpretation:**

Assess whether the team has sufficient familiarity to refactor safely. New teams
with no institutional knowledge may be better served by a clean rewrite.

**Scoring:**

| Score Range | Condition                                              |
| ----------- | ------------------------------------------------------ |
| 90-100      | >3 active contributors, high frequency, low bus factor |
| 70-89       | 2-3 active contributors, moderate frequency            |
| 50-69       | 1-2 contributors, some gaps in knowledge               |
| 30-49       | Single contributor or no recent activity               |
| 0-29        | No contributors in 6+ months, complete team turnover   |

**Decision Signal:**

- Score > 50: Favors **refactor** (team knows the code)
- Score < 30: Neutral — new teams can refactor or rewrite equally

### 7. Risk Assessment (Weight: 0.15)

Synthesizes signals from all other dimensions to evaluate risk.

**Deterministic Signals:**

This dimension combines metrics from dimensions 1-6:

- Critical path coverage (are the riskiest modules tested?)
- Deployment complexity (CI/CD presence, containerization)
- Data migration risk (database schema complexity, migration tooling)
- Rollback capability (feature flags, blue-green deployment indicators)

**AI Interpretation:**

Assess what could go wrong with each approach. Refactoring risks breaking
existing behavior; rewriting risks losing business logic and timeline overruns.

**Scoring:**

| Score Range | Condition                                           |
| ----------- | --------------------------------------------------- |
| 90-100      | Low risk: good tests, CI/CD, rollback capability    |
| 70-89       | Moderate risk: some gaps but manageable             |
| 50-69       | Notable risk: missing safety nets in critical areas |
| 30-49       | High risk: significant gaps in test/deploy/rollback |
| 0-29        | Critical risk: no safety nets, high blast radius    |

**Decision Signal:**

- Score > 60: Either approach is viable — choose based on other factors
- Score < 30: Favors **hybrid** (mitigate risk with gradual migration)

### 8. Effort Estimation (Weight: 0.10)

Estimates relative effort for refactor vs rewrite approaches.

**Deterministic Signals:**

- Total lines of code (LOC)
- Total file count
- Dependency count (direct + transitive)
- Codebase age (first commit date)
- Module count (distinct packages/directories with their own purpose)

**AI Interpretation:**

Estimate relative effort for refactoring (incremental improvement) vs rewriting
(clean-room implementation). Factor in team size and timeline from arguments.

**Scoring:**

| Score Range | Condition                                                |
| ----------- | -------------------------------------------------------- |
| 90-100      | Small codebase (<5K LOC), few deps, refactor trivial     |
| 70-89       | Medium codebase (5-20K LOC), manageable scope            |
| 50-69       | Large codebase (20-100K LOC), significant effort either  |
| 30-49       | Very large (100K-500K LOC), rewrite timeline prohibitive |
| 0-29        | Massive (>500K LOC), rewrite practically infeasible      |

**Decision Signal:**

- Score > 70: Either approach is feasible within typical timelines
- Score 30-70: Favors **refactor** or **hybrid** (rewrite too expensive)
- Score < 30: Favors **refactor** (rewrite is practically infeasible at this
  scale)

---

## Orchestration

This skill declares an `orchestration:` frontmatter block (`mode: phased`) that
fans the five analysis dimensions out as parallel worker units (Phases 2-6:
code quality, test coverage, dependency coupling, business logic & tech stack,
team & effort), then a merge judge runs the decision engine (Phase 7) over their
scores to produce the confidence-rated refactor-vs-rewrite recommendation. For a
monorepo the same fan-out runs per package (Phase 9). The block is consumed by
the capability-routed `WorkflowEngine` (epic #3899); see
[docs/WORKFLOW_ORCHESTRATION.md](https://github.com/nightgauge/nightgauge/blob/main/docs/WORKFLOW_ORCHESTRATION.md). Each
unit's `promptRef` points at the SAME dimension phase the prose **Workflow**
below walks, so providers without an orchestration capability run the dimensions
sequentially in one agent — the prose stays the portability floor.

## Workflow

### Phase 0: Environment Detection

### Phase 0: Environment Preflight

**PURPOSE**: Verify the nightgauge binary is available and the environment
is healthy before executing any pipeline stage. Skills halt immediately with
actionable instructions when this check fails.

Three-layer check:

1. **Binary discovery** — shell-level, runs before invoking the binary
2. **Doctor check** — binary-level, full environment health via `doctor --json`
3. **Project environment** — the checks the project declares under
   `preflight.environment` (#101); opt-in, silent when none are declared

The binary discovery cascade below is **provider-neutral** (issue 4029): it works
identically under every adapter (Claude, Codex, Gemini, …) and references no
VSCode-extension-specific path. The host that spawns the skill (the VSCode
extension's `skillRunner`, or the Go `nightgauge` binary in auto/CLI mode)
resolves the binary authoritatively and exports `NIGHTGAUGE_BIN`, which
this cascade honors first. The remaining steps (PATH, the development-only repo
`bin/` and worktree canonical-repo `bin/`, `~/go/bin`) are fallbacks for direct
or terminal invocation.

This intentionally **diverges** from
`claude-plugins/nightgauge/hooks/lib/guard.sh` (issue 3262 → issue 4029): guard.sh is
Claude-Code-only and retains a trailing `~/.vscode/extensions/...` glob to serve
the standalone-terminal-Claude case (where no host exports `NIGHTGAUGE_BIN`).
Skills must stay portable Markdown and cannot reference that VSCode-only path —
so the shared resolution order (`NIGHTGAUGE_BIN` → PATH → gated repo → gated
canonical → `~/go/bin`) stays in sync, and only guard.sh carries the extra Claude-only
fallback. The `nightgauge preflight skill-portability` gate enforces that
no skill reintroduces a `.vscode/extensions` path, and that every cascade keeps
the `NIGHTGAUGE_DEV_REPO_BIN` gate (#2758): a foreign repository's
`bin/nightgauge` must never run.

After the cascade resolves `$BINARY`, the preflight prepends
`dirname($BINARY)` to `PATH` so subsequent bare `nightgauge ...`
invocations later in the same skill body resolve through PATH rather than
failing with "command not found" (issue 3262).

```bash
# Layer 1: provider-neutral binary discovery (NIGHTGAUGE_BIN → PATH → gated repo
# bin → gated canonical-repo bin → ~/go/bin). No VSCode-extension path — see issue 4029.
# Repo bins need NIGHTGAUGE_DEV_REPO_BIN=1 and a verified core checkout (#2758).
BINARY="${NIGHTGAUGE_BIN:-}"
[ -n "$BINARY" ] && [ ! -x "$BINARY" ] && BINARY=""
[ -z "$BINARY" ] && BINARY=$(command -v nightgauge 2>/dev/null || echo "")
if [ -z "$BINARY" ] && [ "${NIGHTGAUGE_DEV_REPO_BIN:-}" = "1" ]; then
  for NG_ROOT in "$(git rev-parse --show-toplevel 2>/dev/null)" \
    "$(cd "$(git rev-parse --git-common-dir 2>/dev/null)/.." 2>/dev/null && pwd)"; do
    [ -x "$NG_ROOT/bin/nightgauge" ] || continue
    grep -qx 'module github.com/nightgauge/nightgauge' "$NG_ROOT/go.mod" 2>/dev/null || continue
    NG_ORIGIN="$(git -C "$NG_ROOT" config --get remote.origin.url 2>/dev/null)"
    case "${NG_ORIGIN%.git}" in
      git@github.com:nightgauge/nightgauge | https://github.com/nightgauge/nightgauge | \
        ssh://git@github.com/nightgauge/nightgauge) BINARY="$NG_ROOT/bin/nightgauge"; break ;;
    esac
  done
fi
[ -z "$BINARY" ] && [ -x "$HOME/go/bin/nightgauge" ] && BINARY="$HOME/go/bin/nightgauge"
if [ -z "$BINARY" ]; then
  echo "ERROR: nightgauge binary not found via NIGHTGAUGE_BIN, PATH or ~/go/bin" >&2
  echo "" >&2
  echo "Install via: go install github.com/nightgauge/nightgauge/cmd/nightgauge@latest" >&2
  echo "Or download from: https://github.com/nightgauge/nightgauge/releases" >&2
  echo "Run \`nightgauge doctor\` after installing to verify your environment." >&2
  exit 1
fi
# Make the resolved binary callable via bare `nightgauge ...` later in this skill body (issue 3262).
export PATH="$(dirname "$BINARY"):$PATH"

# Per-repo GitHub token: export GH_TOKEN so every `gh` call in this skill
# authenticates as the *current repo's* configured user instead of the machine-
# global `gh auth` active account. `forge auth token` resolves per-repo (config
# github_user/token → GITHUB_TOKEN → `gh auth token --user <github_user>`), so
# concurrent sessions/workspaces owned by different GitHub users each use their
# own token. GH_TOKEN outranks GITHUB_TOKEN and the gh keyring. Only resolve when
# unset (an upstream env value — e.g. the VSCode extension's terminal env —
# wins); never fail preflight over it. Mirrors hooks/lib/guard.sh (sync, issue 3262).
if [ -z "${GH_TOKEN:-}" ]; then
  IB_REPO_TOKEN="$("$BINARY" forge auth token 2>/dev/null || true)"
  if [ -n "$IB_REPO_TOKEN" ]; then
    export GH_TOKEN="$IB_REPO_TOKEN"
    export GITHUB_TOKEN="${GITHUB_TOKEN:-$IB_REPO_TOKEN}"
  fi
  unset IB_REPO_TOKEN
fi

# Layer 2: full environment health check via doctor --json
DOCTOR_RESULT=$("$BINARY" doctor --json 2>/dev/null)
DOCTOR_EXIT=$?
if [ "$DOCTOR_EXIT" -eq 2 ]; then
  echo "ERROR: Environment check failed — nightgauge doctor reports broken environment." >&2
  FAILED_CHECKS=$(printf '%s\n' "$DOCTOR_RESULT" | jq -r '.failed_checks[]? // empty' 2>/dev/null)
  if [ -n "$FAILED_CHECKS" ]; then
    echo "Failing check(s): $(echo "$FAILED_CHECKS" | tr '\n' ',' | sed 's/,$//')" >&2
  fi
  printf '%s\n' "$DOCTOR_RESULT" | jq -r '.errors[]' >&2 2>/dev/null || true
  INSTALL_MSG=$(printf '%s\n' "$DOCTOR_RESULT" | jq -r '.install_instructions // empty' 2>/dev/null)
  [ -n "$INSTALL_MSG" ] && echo "$INSTALL_MSG" >&2
  exit 1
fi
if [ "$DOCTOR_EXIT" -eq 1 ]; then
  echo "WARNING: Environment has non-critical issues:" >&2
  printf '%s\n' "$DOCTOR_RESULT" | jq -r '.warnings[]' >&2 2>/dev/null || true
  # Continue — warnings do not block skill execution
fi

# Layer 3: project-declared environment checks (preflight.environment, #101).
# Opt-in: with none declared this prints nothing and continues. The verb
# writes one `[environment-halt]` line per failed check, with its remedy.
if ! "$BINARY" preflight environment >/dev/null; then
  echo "[environment-halt] Project environment check failed — fix the environment, not the code." >&2
  exit 1
fi
```

**Exit codes from `nightgauge doctor`**:

| Code | Meaning                         | Skill behavior                          |
| ---- | ------------------------------- | --------------------------------------- |
| 0    | Healthy                         | Continue                                |
| 1    | Degraded (warnings only)        | Continue with warning printed to stderr |
| 2    | Broken (required checks failed) | Halt immediately with error details     |

**Layer 3** halts the stage when any check declared under
`preflight.environment` in the workspace's `.nightgauge/config.yaml` fails
(exit 1) or the declaration is invalid (exit 2). The failure is in the
operator's environment, not the code: do not edit code to work around it, do
not run the remedy yourself, and end the stage. The pipeline records it as
`environment_halt` and holds the issue for an operator.

Do not theorize about the cause beyond what is printed here — the failing
check name (`failed_checks`) and its `.errors[]` message are the complete
diagnosis (#277).


---

#### Step 0.1: Parse Arguments

Extract options from invocation:

```bash
ASSESS_PATH="."
PACKAGE_FILTER=""
MODULE_FILTER=""
DIMENSIONS="all"
OUTPUT_FORMAT="both"
SKIP_COVERAGE_RUN=false
OUTPUT_FILE=""
TEAM_SIZE=1
TIMELINE_WEEKS=12

# Parse arguments from invocation
# --path DIR: set ASSESS_PATH
# --package PKG: set PACKAGE_FILTER
# --module MODULE: set MODULE_FILTER
# --dimensions DIMS: set DIMENSIONS (comma-separated)
# --format FORMAT: set OUTPUT_FORMAT
# --skip-coverage-run: set SKIP_COVERAGE_RUN=true
# --output FILE: set OUTPUT_FILE
# --team-size N: set TEAM_SIZE
# --timeline WEEKS: set TIMELINE_WEEKS
```

#### Step 0.2: Detect Ecosystems

Scan for ecosystem indicators in the assessment path:

```bash
cd "$ASSESS_PATH"
ECOSYSTEMS=()

# Node.js
[ -f package.json ] && ECOSYSTEMS+=("nodejs")

# Python
([ -f pyproject.toml ] || [ -f setup.py ] || \
 [ -f requirements.txt ]) && ECOSYSTEMS+=("python")

# Go
[ -f go.mod ] && ECOSYSTEMS+=("go")

# Rust
[ -f Cargo.toml ] && ECOSYSTEMS+=("rust")

# Java/JVM
([ -f pom.xml ] || [ -f build.gradle ] || \
 [ -f build.gradle.kts ]) && ECOSYSTEMS+=("java")

if [ ${#ECOSYSTEMS[@]} -eq 0 ]; then
  echo "WARNING: No recognized ecosystem detected."
  echo "  Proceeding with generic file analysis only."
fi

echo "Ecosystems detected: ${ECOSYSTEMS[*]}"
```

#### Step 0.3: Detect Monorepo Structure

```bash
IS_MONOREPO=false
PACKAGES=()

# Node.js workspaces
if [ -f package.json ]; then
  WORKSPACES=$(jq -r '.workspaces // empty' package.json 2>/dev/null)
  if [ -n "$WORKSPACES" ]; then
    IS_MONOREPO=true
    for ws in $(jq -r '.workspaces[]? // empty' package.json 2>/dev/null); do
      for dir in $ws; do
        [ -d "$dir" ] && PACKAGES+=("$dir")
      done
    done
  fi
fi

# Cargo workspace
if [ -f Cargo.toml ]; then
  grep -q '\[workspace\]' Cargo.toml 2>/dev/null && IS_MONOREPO=true
fi

# Go workspace
[ -f go.work ] && IS_MONOREPO=true

echo "Monorepo: $IS_MONOREPO"
if [ "$IS_MONOREPO" = true ]; then
  echo "Packages: ${PACKAGES[*]}"
fi
```

#### Step 0.4: Apply Module Filter

If `--module` is specified, scope all analysis to that directory:

```bash
if [ -n "$MODULE_FILTER" ]; then
  if [ ! -d "$MODULE_FILTER" ]; then
    echo "ERROR: Module directory not found: $MODULE_FILTER"
    exit 1
  fi
  ASSESS_PATH="$MODULE_FILTER"
  echo "Module filter: $MODULE_FILTER"
fi
```

---

### Phase 1: Load Existing Assessments

Optionally read upstream assessment reports to avoid redundant analysis and
enrich scoring with pre-computed data. They are in this checkout's `reports/`
directory inside the git directory (`nightgauge layout path checkout reports`,
ADR-024 § 7).

```bash
HEALTH_REPORT="$(nightgauge layout path checkout reports/health-report.json)"
SECURITY_REPORT="$(nightgauge layout path checkout reports/security-audit.json)"
SCAFFOLD_REPORT="$(nightgauge layout path checkout reports/test-scaffold-report.json)"

HEALTH_AVAILABLE=false
SECURITY_AVAILABLE=false
SCAFFOLD_AVAILABLE=false

if [ -f "$HEALTH_REPORT" ]; then
  HEALTH_AVAILABLE=true
  HC_OVERALL=$(jq -r '.summary.overall_health_score // "N/A"' \
    "$HEALTH_REPORT" 2>/dev/null)
  HC_TEST_SCORE=$(jq -r '.dimensions.test_coverage.score // "N/A"' \
    "$HEALTH_REPORT" 2>/dev/null)
  HC_QUALITY_SCORE=$(jq -r '.dimensions.code_quality.score // "N/A"' \
    "$HEALTH_REPORT" 2>/dev/null)
  HC_DEBT_SCORE=$(jq -r '.dimensions.tech_debt.score // "N/A"' \
    "$HEALTH_REPORT" 2>/dev/null)
  echo "Health check available: overall=$HC_OVERALL"
fi

if [ -f "$SECURITY_REPORT" ]; then
  SECURITY_AVAILABLE=true
  SA_OVERALL=$(jq -r '.summary.overall_security_score // "N/A"' \
    "$SECURITY_REPORT" 2>/dev/null)
  echo "Security audit available: overall=$SA_OVERALL"
fi

if [ -f "$SCAFFOLD_REPORT" ]; then
  SCAFFOLD_AVAILABLE=true
  TS_GAPS=$(jq -r '.summary.critical_gaps_found // "N/A"' \
    "$SCAFFOLD_REPORT" 2>/dev/null)
  TS_COVERAGE=$(jq -r '.summary.coverage_before // "N/A"' \
    "$SCAFFOLD_REPORT" 2>/dev/null)
  echo "Test scaffold available: gaps=$TS_GAPS coverage=$TS_COVERAGE"
fi

echo "Upstream reports: health=$HEALTH_AVAILABLE" \
     "security=$SECURITY_AVAILABLE" \
     "scaffold=$SCAFFOLD_AVAILABLE"
```

When upstream reports are available, reuse their scores for overlapping
dimensions (code quality, test coverage, dependency health) rather than
re-running the same analysis. This avoids redundant work and ensures
consistency.

---

### Phase 2: Code Quality Analysis

Only runs if `DIMENSIONS` includes `code-quality` or is `all`.

If `HEALTH_AVAILABLE=true` and health-check code_quality score exists, reuse it
and skip deterministic collection for this dimension.

#### Step 2.1: Check Linter and Formatter Configuration

```bash
LINTER_PRESENT=false
FORMATTER_PRESENT=false

# Linters
([ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.json ] || \
 [ -f .eslintrc.yml ] || [ -f eslint.config.js ] || \
 [ -f eslint.config.mjs ]) && LINTER_PRESENT=true
([ -f .flake8 ] || [ -f .pylintrc ]) && LINTER_PRESENT=true
[ -f .golangci.yml ] && LINTER_PRESENT=true
[ -f clippy.toml ] && LINTER_PRESENT=true
[ -f ruff.toml ] && LINTER_PRESENT=true
[ -f pyproject.toml ] && grep -q '\[tool.ruff\]' pyproject.toml \
  2>/dev/null && LINTER_PRESENT=true

# Formatters
([ -f .prettierrc ] || [ -f .prettierrc.js ] || \
 [ -f .prettierrc.json ] || [ -f .prettierrc.yml ] || \
 [ -f prettier.config.js ]) && FORMATTER_PRESENT=true
[ -f .editorconfig ] && FORMATTER_PRESENT=true
[ -f pyproject.toml ] && grep -qE '\[tool\.(black|ruff\.format)\]' \
  pyproject.toml 2>/dev/null && FORMATTER_PRESENT=true

echo "Linter: $LINTER_PRESENT"
echo "Formatter: $FORMATTER_PRESENT"
```

#### Step 2.2: Count Debt Markers

```bash
DEBT_MARKERS=$(grep -r --include="*.ts" --include="*.tsx" \
  --include="*.js" --include="*.jsx" --include="*.py" \
  --include="*.go" --include="*.rs" --include="*.java" \
  --include="*.kt" \
  -cE 'TODO|FIXME|HACK|XXX' "$ASSESS_PATH" \
  --exclude-dir=node_modules --exclude-dir=.git \
  --exclude-dir=vendor --exclude-dir=dist \
  --exclude-dir=build 2>/dev/null | \
  awk -F: '{sum+=$NF} END {print sum+0}')

echo "Debt markers (TODO/FIXME/HACK/XXX): $DEBT_MARKERS"
```

#### Step 2.3: Analyze File Sizes and Complexity

Collect source files using parallel Glob calls (faster than `find`):

```
Glob("**/*.ts")    Glob("**/*.tsx")   Glob("**/*.js")    Glob("**/*.jsx")
Glob("**/*.py")    Glob("**/*.go")    Glob("**/*.rs")    Glob("**/*.java")
Glob("**/*.kt")

Exclude: node_modules/, .git/, vendor/, dist/, build/
```

Filter out test files (`*.test.*`, `*.spec.*`). Store as `SOURCE_FILES`.

```bash
# File sizes (top 10 + total)
echo "$SOURCE_FILES" | xargs wc -l 2>/dev/null | sort -rn | head -11

echo "File sizes (top 10 + total):"
echo "$FILE_SIZES"

# Cyclomatic complexity proxy: count branching constructs per file
# Sample first 20 source files from SOURCE_FILES
for f in $(echo "$SOURCE_FILES" | head -20); do
  BRANCHES=$(grep -cE '\b(if|else|switch|case|for|while|try|catch|&&|\|\|)\b' \
    "$f" 2>/dev/null || echo 0)
  LINES=$(wc -l < "$f" | tr -d ' ')
  echo "  $f: branches=$BRANCHES lines=$LINES"
done
```

#### Step 2.4: Compute Score

AI interprets the collected metrics to compute a 0-100 score based on the
scoring rubric above. Write results to `/tmp/rr_dim_quality.json`.

---

### Phase 3: Test Coverage Analysis

Only runs if `DIMENSIONS` includes `test-coverage` or is `all`.

If `HEALTH_AVAILABLE=true` and health-check test_coverage score exists, reuse
it. If `SCAFFOLD_AVAILABLE=true`, use the scaffold report's coverage data.

#### Step 3.1: Detect Test Framework

```bash
TEST_FRAMEWORK="none"

# Node.js
if [ -f package.json ]; then
  jq -r '.devDependencies // {} | keys[]' package.json 2>/dev/null | \
    grep -qE '^(vitest|jest|mocha|ava|tap)$' && \
    TEST_FRAMEWORK=$(jq -r \
      '.devDependencies // {} | keys[]' package.json | \
      grep -E '^(vitest|jest|mocha|ava|tap)$' | head -1)
fi

# Python
([ -f pytest.ini ] || [ -f setup.cfg ] || [ -f pyproject.toml ]) && \
  grep -ql 'pytest' pytest.ini setup.cfg pyproject.toml 2>/dev/null && \
  TEST_FRAMEWORK="pytest"

# Go (built-in)
[[ " ${ECOSYSTEMS[*]} " =~ " go " ]] && TEST_FRAMEWORK="go-test"

# Rust (built-in)
[[ " ${ECOSYSTEMS[*]} " =~ " rust " ]] && TEST_FRAMEWORK="cargo-test"

# Java
[ -f pom.xml ] && grep -q 'junit' pom.xml 2>/dev/null && \
  TEST_FRAMEWORK="junit"

echo "Test framework: $TEST_FRAMEWORK"
```

#### Step 3.2: Count Test Files vs Source Files

Use parallel Glob calls (faster than `find`):

```
# Count test files — run in parallel
Glob("**/*.test.*")   Glob("**/*.spec.*")
Glob("**/*_test.*")   Glob("**/test_*")

Exclude: node_modules/, .git/, vendor/, dist/, build/
```

Collect into `TEST_FILES`. Count: `TEST_FILE_COUNT`.

Reuse `SOURCE_FILES` from Phase 2 Step 2.3 if available, otherwise collect
with the same parallel Glob pattern. Count: `SOURCE_FILE_COUNT`.

```
TEST_RATIO = TEST_FILE_COUNT / SOURCE_FILE_COUNT  (0 if no source files)
```

Report: `Test files: N`, `Source files: N`, `Test-to-source ratio: N`

#### Step 3.3: Check Coverage

```bash
COVERAGE_CONFIG=false

# Node.js
([ -f .nycrc ] || [ -f .nycrc.json ] || [ -f .c8rc.json ]) && \
  COVERAGE_CONFIG=true
[ -f vitest.config.ts ] && grep -q 'coverage' vitest.config.ts \
  2>/dev/null && COVERAGE_CONFIG=true

# Python
[ -f .coveragerc ] && COVERAGE_CONFIG=true
[ -f pyproject.toml ] && grep -q 'coverage' pyproject.toml 2>/dev/null && \
  COVERAGE_CONFIG=true

echo "Coverage config: $COVERAGE_CONFIG"
```

If `--skip-coverage-run` is NOT set and a coverage tool is available, run
coverage and capture the percentage.

#### Step 3.4: Compute Score

AI interprets metrics using the scoring rubric. Write results to
`/tmp/rr_dim_test.json`.

---

### Phase 4: Dependency Coupling Analysis

Only runs if `DIMENSIONS` includes `dependency-coupling` or is `all`.

#### Step 4.1: Build Import Graph

```bash
echo "=== Dependency Coupling: Import Graph ==="

# Node.js/TypeScript: extract import/require statements
if [[ " ${ECOSYSTEMS[*]} " =~ " nodejs " ]]; then
  grep -rn --include="*.ts" --include="*.tsx" --include="*.js" \
    --include="*.jsx" \
    -E "^(import |const .* = require\(|from ['\"])" \
    "$ASSESS_PATH" \
    --exclude-dir=node_modules --exclude-dir=dist \
    --exclude-dir=build --exclude-dir=.git \
    2>/dev/null > /tmp/rr_imports.txt || true
  IMPORT_COUNT=$(wc -l < /tmp/rr_imports.txt | tr -d ' ')
  echo "Import statements found: $IMPORT_COUNT"
fi

# Python: extract import statements
if [[ " ${ECOSYSTEMS[*]} " =~ " python " ]]; then
  grep -rn --include="*.py" \
    -E "^(import |from .+ import )" \
    "$ASSESS_PATH" \
    --exclude-dir=.git --exclude-dir=vendor \
    --exclude-dir=dist --exclude-dir=build \
    2>/dev/null > /tmp/rr_imports_py.txt || true
  PY_IMPORT_COUNT=$(wc -l < /tmp/rr_imports_py.txt | tr -d ' ')
  echo "Python import statements: $PY_IMPORT_COUNT"
fi
```

#### Step 4.2: Detect Circular Dependencies

```bash
echo "=== Dependency Coupling: Circular Dependencies ==="

# For Node.js: detect mutual imports between files
# Sample approach: find files that import each other
CIRCULAR_COUNT=0

# Build a simplified adjacency list and check for mutual imports
if [ -f /tmp/rr_imports.txt ]; then
  # Extract file:import pairs, check for A->B and B->A patterns
  while IFS=: read -r file line_num content; do
    IMPORTING=$(echo "$content" | \
      grep -oE "from ['\"](\./|\.\./)([^'\"]+)" | \
      sed "s/from ['\"]//;s/['\"]//g" | head -1)
    if [ -n "$IMPORTING" ]; then
      echo "$file -> $IMPORTING"
    fi
  done < /tmp/rr_imports.txt > /tmp/rr_edges.txt 2>/dev/null || true

  # Count files involved
  EDGE_COUNT=$(wc -l < /tmp/rr_edges.txt 2>/dev/null | tr -d ' ')
  echo "Import edges analyzed: $EDGE_COUNT"
fi

echo "Circular dependency estimate: $CIRCULAR_COUNT"
```

#### Step 4.3: Compute Fan-In and Fan-Out

```bash
echo "=== Dependency Coupling: Fan-In/Fan-Out ==="

# For each source file, count how many files import it (fan-in)
# and how many files it imports (fan-out)
# Reuse SOURCE_FILES from Phase 2, sample first 30
for f in $(echo "$SOURCE_FILES" | head -30); do
  BASENAME=$(basename "$f" | sed 's/\.[^.]*$//')
  FAN_IN=$(grep -rl --include="*.ts" --include="*.tsx" \
    --include="*.js" --include="*.jsx" --include="*.py" \
    "$BASENAME" "$ASSESS_PATH" \
    --exclude-dir=node_modules --exclude-dir=dist \
    --exclude-dir=build --exclude-dir=.git \
    2>/dev/null | wc -l | tr -d ' ')
  FAN_OUT=$(grep -cE "^(import |from |const .* = require)" "$f" \
    2>/dev/null || echo 0)
  echo "  $f: fan_in=$FAN_IN fan_out=$FAN_OUT"
done
```

#### Step 4.4: Compute Score

AI interprets coupling metrics to compute a 0-100 score. Write results to
`/tmp/rr_dim_coupling.json`.

---

### Phase 5: Business Logic & Tech Stack Analysis

Only runs if `DIMENSIONS` includes `business-logic-extraction` or
`tech-stack-viability` or is `all`.

#### Step 5.1: Detect Domain/Service Layer

```bash
echo "=== Business Logic: Domain Layer Detection ==="

# Count files matching service/domain/model/entity patterns
# Reuse SOURCE_FILES from Phase 2 Step 2.3
DOMAIN_FILES=$(echo "$SOURCE_FILES" | \
  grep -iE '(service|domain|model|entity|repository|usecase|handler)' | \
  wc -l | tr -d ' ')

TOTAL_SOURCE=$(echo "$SOURCE_FILES" | wc -l | tr -d ' ')

echo "Domain/service files: $DOMAIN_FILES of $TOTAL_SOURCE total"

# Check for hardcoded values (magic numbers, hardcoded URLs)
HARDCODED=$(grep -rn --include="*.ts" --include="*.tsx" \
  --include="*.js" --include="*.jsx" --include="*.py" \
  --include="*.go" --include="*.java" \
  -E '(http://|https://)[^"'"'"']+\.(com|io|org|net)' \
  "$ASSESS_PATH" \
  --exclude-dir=node_modules --exclude-dir=.git \
  --exclude-dir=dist --exclude-dir=build \
  --exclude-dir=vendor \
  2>/dev/null | \
  grep -vE '(test|spec|mock|example|README|CHANGELOG|\.md:)' | \
  wc -l | tr -d ' ')
echo "Hardcoded URL patterns: $HARDCODED"
```

#### Step 5.2: Framework Version Checks

```bash
echo "=== Tech Stack: Framework Version Checks ==="

# Node.js
if [[ " ${ECOSYSTEMS[*]} " =~ " nodejs " ]]; then
  NODE_VERSION=$(node --version 2>/dev/null || echo "not installed")
  echo "Node.js runtime: $NODE_VERSION"

  # Check package.json engines field
  ENGINES=$(jq -r '.engines // {}' package.json 2>/dev/null)
  echo "Engines constraint: $ENGINES"

  # Check for major frameworks and their versions
  for pkg in react next express fastify vue angular nestjs; do
    VER=$(jq -r ".dependencies[\"$pkg\"] // .devDependencies[\"$pkg\"] // empty" \
      package.json 2>/dev/null)
    [ -n "$VER" ] && echo "  $pkg: $VER"
  done
fi

# Python
if [[ " ${ECOSYSTEMS[*]} " =~ " python " ]]; then
  PYTHON_VERSION=$(python3 --version 2>/dev/null || echo "not installed")
  echo "Python runtime: $PYTHON_VERSION"

  # Check for requires-python in pyproject.toml
  if [ -f pyproject.toml ]; then
    REQUIRES_PY=$(grep 'requires-python' pyproject.toml 2>/dev/null || true)
    echo "Python constraint: $REQUIRES_PY"
  fi
fi

# Go
if [[ " ${ECOSYSTEMS[*]} " =~ " go " ]]; then
  GO_VERSION=$(grep '^go ' go.mod 2>/dev/null || echo "unknown")
  echo "Go version: $GO_VERSION"
fi

# Rust
if [[ " ${ECOSYSTEMS[*]} " =~ " rust " ]]; then
  RUST_EDITION=$(grep 'edition' Cargo.toml 2>/dev/null | head -1 || true)
  echo "Rust edition: $RUST_EDITION"
fi
```

#### Step 5.3: Compute Scores

AI interprets business logic extraction metrics and tech stack viability data to
compute two separate 0-100 scores. Write results to `/tmp/rr_dim_business.json`
and `/tmp/rr_dim_techstack.json`.

---

### Phase 6: Team & Effort Analysis

Only runs if `DIMENSIONS` includes `team-expertise` or `effort-estimation` or is
`all`.

#### Step 6.1: Git Log Analysis

```bash
echo "=== Team Expertise: Git Analysis ==="

# Active contributors in last 90 days
AUTHORS_90=$(git log --since="90 days ago" --format='%aN' 2>/dev/null | \
  sort -u | wc -l | tr -d ' ')
echo "Active contributors (90 days): $AUTHORS_90"

# Active contributors in last 365 days
AUTHORS_365=$(git log --since="365 days ago" --format='%aN' 2>/dev/null | \
  sort -u | wc -l | tr -d ' ')
echo "Active contributors (365 days): $AUTHORS_365"

# Recent commit frequency (commits per week, last 90 days)
COMMITS_90=$(git log --since="90 days ago" --oneline 2>/dev/null | \
  wc -l | tr -d ' ')
if [ "$COMMITS_90" -gt 0 ]; then
  COMMITS_PER_WEEK=$(echo "scale=1; $COMMITS_90 / 13" | bc)
else
  COMMITS_PER_WEEK="0"
fi
echo "Commits/week (90 days): $COMMITS_PER_WEEK"

# Bus factor: top contributor percentage
TOP_AUTHOR_COMMITS=$(git log --since="365 days ago" --format='%aN' \
  2>/dev/null | sort | uniq -c | sort -rn | head -1 | \
  awk '{print $1}')
TOTAL_COMMITS_YEAR=$(git log --since="365 days ago" --oneline \
  2>/dev/null | wc -l | tr -d ' ')
if [ "$TOTAL_COMMITS_YEAR" -gt 0 ] && [ -n "$TOP_AUTHOR_COMMITS" ]; then
  BUS_FACTOR=$(echo "scale=0; $TOP_AUTHOR_COMMITS * 100 / $TOTAL_COMMITS_YEAR" | bc)
else
  BUS_FACTOR=100
fi
echo "Bus factor (top contributor %): $BUS_FACTOR"

# Codebase age (first commit)
FIRST_COMMIT=$(git log --reverse --format='%ci' 2>/dev/null | \
  head -1 || echo "unknown")
echo "First commit: $FIRST_COMMIT"
```

#### Step 6.2: LOC and Scope Metrics

```bash
echo "=== Effort Estimation: Scope Metrics ==="

# Total LOC (excluding tests, node_modules, etc.)
# Reuse SOURCE_FILES from Phase 2 Step 2.3
TOTAL_LOC=$(echo "$SOURCE_FILES" | \
  xargs wc -l 2>/dev/null | tail -1 | awk '{print $1}')
echo "Total source LOC: $TOTAL_LOC"

# File count
TOTAL_FILES=$SOURCE_FILE_COUNT
echo "Total source files: $TOTAL_FILES"

# Dependency count
if [ -f package.json ]; then
  DEP_COUNT=$(jq -r \
    '(.dependencies // {} | length) + (.devDependencies // {} | length)' \
    package.json 2>/dev/null || echo 0)
  echo "npm dependencies: $DEP_COUNT"
fi

echo "Team size (provided): $TEAM_SIZE"
echo "Timeline (provided): $TIMELINE_WEEKS weeks"
```

#### Step 6.3: Change Frequency Heatmap

```bash
echo "=== Team Expertise: Change Frequency ==="

# Top 10 most frequently changed files in last 6 months
git log --since="6 months ago" --name-only --pretty=format: 2>/dev/null | \
  grep -v '^$' | sort | uniq -c | sort -rn | head -10
```

#### Step 6.4: Compute Scores

AI interprets team metrics and effort data to compute two separate 0-100 scores.
Write results to `/tmp/rr_dim_team.json` and `/tmp/rr_dim_effort.json`.

---

### Phase 7: Decision Engine

AI synthesizes all 8 dimension scores into a recommendation.

#### Step 7.1: Compute Weighted Score

```text
weighted_score = (
  code_quality         * 0.15 +
  test_coverage        * 0.15 +
  dependency_coupling  * 0.15 +
  business_logic       * 0.10 +
  tech_stack_viability * 0.15 +
  team_expertise       * 0.05 +
  risk_assessment      * 0.15 +
  effort_estimation    * 0.10
)
```

If a dimension was skipped, redistribute its weight proportionally among
assessed dimensions.

#### Step 7.2: Apply Decision Rules

**Signals suggesting rewrite:**

- Framework/runtime at end-of-life (no security patches)
- Test coverage < 20% (unsafe to refactor)
- Circular dependency count > 30% of modules
- Performance requirements incompatible with current architecture
- Tech stack viability score < 30

**Signals suggesting refactor:**

- Core business logic identifiable and sound
- Test coverage > 50% (adequate safety net)
- Team actively familiar with codebase (>3 active contributors in 90 days)
- Incremental delivery requirement (can't pause feature work)
- Tech stack viability score > 60

**Signals suggesting hybrid (strangler fig / branch by abstraction):**

- Mixed signals (some components refactorable, others not)
- Large codebase where full rewrite is too risky
- Dependency coupling score moderate (40-70)
- Some components have good test coverage, others don't

#### Step 7.3: Determine Confidence Level

| Confidence | Condition                                   |
| ---------- | ------------------------------------------- |
| Strong     | 6+ dimensions agree on direction            |
| Moderate   | 4-5 dimensions agree, minor dissent         |
| Weak       | 3 or fewer agree, significant mixed signals |

#### Step 7.4: Generate Per-Component Analysis

If the codebase has distinct modules/packages, analyze each independently:

For each component/package:

1. Apply the 8-dimension analysis scoped to that directory
2. Produce a per-component recommendation
3. Identify which components to refactor and which to rewrite

Use the `Task` tool with `model: "haiku"` to spawn parallel subagents for
independent per-component assessments.

#### Step 7.5: Build Risk/Benefit Matrix

For each of the three approaches (refactor, rewrite, hybrid):

- **Benefits**: List expected positive outcomes
- **Risks**: List potential negative outcomes
- **Effort estimate**: Person-weeks based on team size and LOC
- **Timeline estimate**: Weeks based on available timeline

#### Step 7.6: Hybrid Strategy Selection

If recommendation is "hybrid", select the most appropriate strategy:

| Strategy              | Best When                                      |
| --------------------- | ---------------------------------------------- |
| Strangler Fig         | New features can be built alongside old system |
| Branch by Abstraction | Internal modules can be swapped behind APIs    |
| Parallel Run          | Critical path needs verified correctness       |

---

### Phase 8: Report Generation

#### Step 8.1: Ensure Output Directory

```bash
mkdir -p .nightgauge
```

#### Step 8.2: Write JSON Report

Write structured report to `.nightgauge/refactor-rewrite-analysis.json` (or
custom `--output` path):

```json
{
  "schema_version": "1.0",
  "assessment_date": "ISO-8601",
  "codebase": {
    "name": "project-name",
    "root_path": "/path",
    "ecosystems": ["nodejs"],
    "is_monorepo": false,
    "packages": [],
    "total_files": 0,
    "total_loc": 0
  },
  "inputs_available": {
    "health_report": true,
    "security_audit": false,
    "test_scaffold": true
  },
  "dimensions": {
    "code_quality": {
      "score": 65,
      "status": "good",
      "weight": 0.15,
      "findings": [],
      "metrics": {}
    },
    "test_coverage": {
      "score": 45,
      "status": "fair",
      "weight": 0.15,
      "findings": [],
      "metrics": {}
    },
    "dependency_coupling": {
      "score": 55,
      "status": "fair",
      "weight": 0.15,
      "findings": [],
      "metrics": {}
    },
    "business_logic_extraction": {
      "score": 70,
      "status": "good",
      "weight": 0.1,
      "findings": [],
      "metrics": {}
    },
    "tech_stack_viability": {
      "score": 80,
      "status": "good",
      "weight": 0.15,
      "findings": [],
      "metrics": {}
    },
    "team_expertise": {
      "score": 60,
      "status": "fair",
      "weight": 0.05,
      "findings": [],
      "metrics": {}
    },
    "risk_assessment": {
      "score": 50,
      "status": "fair",
      "weight": 0.15,
      "findings": [],
      "metrics": {}
    },
    "effort_estimation": {
      "score": 55,
      "status": "fair",
      "weight": 0.1,
      "findings": [],
      "metrics": {}
    }
  },
  "recommendation": {
    "decision": "refactor|rewrite|hybrid",
    "confidence": "strong|moderate|weak",
    "rationale": "string",
    "hybrid_strategy": "strangler-fig|branch-by-abstraction|parallel-run|null",
    "hybrid_details": "string or null"
  },
  "risk_benefit_matrix": {
    "refactor": {
      "benefits": ["..."],
      "risks": ["..."],
      "effort_estimate": "string",
      "timeline_estimate": "string"
    },
    "rewrite": {
      "benefits": ["..."],
      "risks": ["..."],
      "effort_estimate": "string",
      "timeline_estimate": "string"
    },
    "hybrid": {
      "strategy": "string",
      "benefits": ["..."],
      "risks": ["..."],
      "effort_estimate": "string",
      "timeline_estimate": "string"
    }
  },
  "per_component_analysis": [
    {
      "component": "string",
      "path": "string",
      "recommendation": "refactor|rewrite|hybrid",
      "confidence": "strong|moderate|weak",
      "key_factors": ["..."],
      "scores": {}
    }
  ],
  "organizational_constraints": {
    "team_size": 1,
    "timeline_weeks": 12,
    "notes": ["..."]
  },
  "top_recommendations": [
    {
      "priority": 1,
      "action": "string",
      "effort": "XS|S|M|L|XL",
      "impact": "string"
    }
  ],
  "references": [
    "Martin Fowler - Refactoring",
    "Michael Feathers - Working Effectively with Legacy Code",
    "Sam Newman - Strangler Fig Pattern"
  ],
  "created_at": "ISO-8601"
}
```

#### Step 8.3: Write Markdown Summary

Output a human-readable report:

```text
REFACTOR vs REWRITE ANALYSIS
================================================================

Project: project-name
Ecosystems: nodejs
Assessment Date: 2026-02-21
Team Size: 1 | Timeline: 12 weeks
Monorepo: No

RECOMMENDATION: HYBRID (Strangler Fig) [MODERATE confidence]
================================================================

Rationale: The codebase shows good business logic separation and a
viable tech stack, but low test coverage and moderate coupling make
a full refactor risky. A strangler fig approach allows incremental
migration while preserving working functionality.

DIMENSION SCORES
----------------------------------------------------------------
  Code Quality:            ████████████░░░░ 65  [GOOD]
  Test Coverage:           ████████░░░░░░░░ 45  [FAIR]
  Dependency Coupling:     ██████████░░░░░░ 55  [FAIR]
  Business Logic:          ██████████████░░ 70  [GOOD]
  Tech Stack Viability:    ████████████████ 80  [GOOD]
  Team Expertise:          ████████████░░░░ 60  [FAIR]
  Risk Assessment:         ██████████░░░░░░ 50  [FAIR]
  Effort Estimation:       ██████████░░░░░░ 55  [FAIR]

RISK/BENEFIT MATRIX
----------------------------------------------------------------

  REFACTOR
    Benefits:
      + Preserves existing business logic
      + Incremental delivery — no feature freeze
      + Lower upfront cost
    Risks:
      - Low test coverage (45%) makes changes risky
      - Moderate coupling may cause cascading changes
    Effort: ~8 person-weeks | Timeline: ~10 weeks

  REWRITE
    Benefits:
      + Clean architecture from scratch
      + Eliminate all technical debt
      + Modern patterns and tooling
    Risks:
      - Business logic may be lost in translation
      - Feature freeze during rewrite
      - Timeline overrun risk (historical: 2-3x estimates)
    Effort: ~16 person-weeks | Timeline: ~20 weeks

  HYBRID (Strangler Fig) [RECOMMENDED]
    Benefits:
      + Gradual migration reduces risk
      + New features built on new architecture
      + Old system remains functional during transition
    Risks:
      - Maintaining two systems temporarily
      - Integration complexity at boundaries
    Effort: ~12 person-weeks | Timeline: ~14 weeks

PER-COMPONENT ANALYSIS
----------------------------------------------------------------

  src/auth/         -> REWRITE   [Strong]  (EOL framework, no tests)
  src/api/          -> REFACTOR  [Strong]  (good tests, clean code)
  src/services/     -> HYBRID    [Moderate] (mixed quality)
  src/utils/        -> REFACTOR  [Strong]  (standalone, well-tested)

TOP RECOMMENDATIONS (sorted by impact)
----------------------------------------------------------------
  1. [M] Add characterization tests for src/auth/ before migration
  2. [S] Establish API boundary for strangler fig integration point
  3. [L] Rewrite auth module on modern framework
  4. [M] Refactor src/services/ to reduce circular dependencies
  5. [S] Update tech stack to current LTS versions

UPSTREAM REPORT INTEGRATION
----------------------------------------------------------------
  Health Check:    available (score: 72)
  Security Audit:  not available
  Test Scaffold:   available (8 critical gaps)

----------------------------------------------------------------
Report saved: .nightgauge/refactor-rewrite-analysis.json
```

If `--format json`, write only JSON. If `--format summary`, output only
markdown. If `--format both`, write JSON and output the markdown summary.

#### Step 8.4: Verify JSON Report

```bash
python3 -m json.tool .nightgauge/refactor-rewrite-analysis.json \
  > /dev/null && \
  echo "Report written: .nightgauge/refactor-rewrite-analysis.json"
```

---

### Phase 9: Monorepo Aggregation (Conditional)

Only runs if `IS_MONOREPO=true` and `--package` was NOT specified.

#### Step 9.1: Per-Package Assessment

For each package in `PACKAGES`:

1. Run Phases 2-7 scoped to the package directory
2. Compute per-package scores and recommendations

Use the `Task` tool with `model: "haiku"` to spawn parallel subagents for
independent package assessments. Each subagent receives: package path, ecosystem
detected, dimensions to assess, and team/timeline parameters.

#### Step 9.2: Aggregate Results

Combine per-package results:

- Overall recommendation (weighted by package LOC)
- Per-package recommendation table
- Aggregate risk/benefit matrix
- Combined top recommendations

#### Step 9.3: Per-Package Breakdown in Report

Add per-package section to both JSON and markdown reports:

```json
{
  "packages": [
    {
      "name": "packages/api-server",
      "recommendation": "refactor",
      "confidence": "strong",
      "overall_score": 72,
      "key_factors": ["good test coverage", "viable stack"],
      "dimensions": {}
    },
    {
      "name": "packages/legacy-ui",
      "recommendation": "rewrite",
      "confidence": "moderate",
      "overall_score": 35,
      "key_factors": ["EOL framework", "no tests"],
      "dimensions": {}
    }
  ]
}
```

---

### Self-Assessment Epilogue

### Self-Assessment Epilogue

**PURPOSE**: Evaluate whether this skill's instructions matched reality during
this execution. This phase is **non-blocking** — skip entirely if any main phase
failed. A perfectly working skill produces **no output** from this phase.

> See [docs/SKILL_SELF_ASSESSMENT.md](https://github.com/nightgauge/nightgauge/blob/main/docs/SKILL_SELF_ASSESSMENT.md) for
> the full strategy, synthesis algorithm, and integration architecture.

#### When to Skip

- Any prior phase exited with an error before completing
- The skill was cancelled or timed out
- Running in `--dry-run` mode

#### Step 1: Evaluate Execution Friction

Review the execution that just completed. Answer these questions honestly:

1. **Command failures**: Did any command, script, or binary call in the skill
   instructions fail? (e.g., script not found, wrong flags, missing binary)
2. **Workarounds**: Did you have to deviate from the skill instructions to
   accomplish the goal? (e.g., used `gh` directly because a referenced script
   didn't exist, skipped a step that referenced a nonexistent file)
3. **Stale references**: Did any file path, function name, API endpoint, or tool
   referenced in the instructions not exist in the current codebase?
4. **Unclear instructions**: Were any instructions ambiguous enough that you had
   to guess at the intended behavior?
5. **Missing instructions**: Was there a significant step you had to figure out
   on your own that should have been documented in this skill?

**If ALL answers are "no" — write nothing and complete the skill normally.** The
goal is silence when everything works.

#### Step 2: Write Assessment Record

Only if friction was detected in Step 1. Write a single JSON file,
`assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json` in the clone's pipeline state
directory, through the binary (JSON on stdin; never a direct file write):

```bash
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
nightgauge layout write pipeline "assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json" <<'JSON'
{ ... the record below ... }
JSON
```

The assessment record MUST follow this schema:

```json
{
  "schema_version": "1",
  "skill": "{STAGE_NAME}",
  "skill_file": "skills/nightgauge-{STAGE_NAME}/SKILL.md",
  "issue_number": 42,
  "timestamp": "2026-03-10T14:30:00Z",
  "friction": [
    {
      "type": "command_failure",
      "severity": "high",
      "description": "hooks/lib/add-to-project.sh not found — script was deleted",
      "skill_line_hint": "claude-plugins/nightgauge/hooks/lib/add-to-project.sh <issue-number>",
      "actual_resolution": "Used gh api graphql to add issue to project board directly",
      "suggested_fix": "Replace with: nightgauge project add <issue-number>"
    }
  ]
}
```

**Friction types**: `command_failure`, `workaround`, `stale_reference`,
`unclear_instruction`, `missing_instruction`

**Severity levels**:

- `high` — instruction is **broken**. Required manual workaround to complete.
- `medium` — instruction is **misleading**. Agent adapted without user help.
- `low` — instruction is **suboptimal**. No functional impact.

**Rules**:

- **One record per execution** — multiple friction items go in the `friction`
  array, not separate files.
- **Be specific** — quote the exact instruction text that was wrong. Not "some
  commands didn't work" but "Step 5.2 calls `hooks/lib/add-to-project.sh` but
  this script was deleted in commit 65915701."
- **Suggest the fix** — every finding MUST include `suggested_fix` with the
  concrete SKILL.md change needed. Not "update the docs" but "replace
  `hooks/lib/add-to-project.sh <N>` with `nightgauge project add <N>`."
- **Don't invent friction** — only report issues you actually encountered during
  this execution. Do not speculate about potential problems.

#### Step 3: Validate and Complete

```bash
# Validate JSON if written
ISSUE_NUMBER="${NIGHTGAUGE_ISSUE_NUMBER:-$(git branch --show-current | sed -n 's#^[^/]*/\([0-9]*\)-.*#\1#p')}"
: "${ISSUE_NUMBER:?set NIGHTGAUGE_ISSUE_NUMBER or check out the issue branch}"
ASSESSMENT_FILE="$(nightgauge layout path pipeline "assessments/{STAGE_NAME}-${ISSUE_NUMBER}.json")"
if [ -f "$ASSESSMENT_FILE" ]; then
  python3 -m json.tool "$ASSESSMENT_FILE" > /dev/null 2>&1 || \
    echo "WARNING: Assessment record is not valid JSON" >&2
fi
```


---

## Output Format

### JSON Schema

See Phase 8 Step 8.2 for the complete JSON report structure.

### Report Files

| File                                         | Format   | When Written            |
| -------------------------------------------- | -------- | ----------------------- |
| `.nightgauge/refactor-rewrite-analysis.json` | JSON     | `--format json/both`    |
| Console output                               | Markdown | `--format summary/both` |

---

## Error Handling

| Condition                    | Action                                        |
| ---------------------------- | --------------------------------------------- |
| No ecosystem detected        | Proceed with generic file analysis only       |
| Git not available            | Skip team expertise, note in findings         |
| Coverage tool not installed  | Skip coverage run, use ratio-based scoring    |
| `--dimensions` invalid value | Error with valid dimension list               |
| `--module` path not found    | Error with path not found message             |
| `--package` path not found   | Error with path not found message             |
| Assessment path not found    | Error with path not found message             |
| `jq` not installed           | Error with install instructions               |
| Permission denied on files   | Skip inaccessible files, note count in report |
| Large codebase timeout       | Use `head`/`--max-count` limits, sample files |
| Monorepo package not found   | Warning, skip package, continue with others   |
| Upstream report invalid JSON | Skip that report, warn, continue without it   |

---

## Pipeline Position

```text
UTILITIES (not part of main pipeline)

/nightgauge:health-check ──────────────────────────┐
       |                                           |
  Writes: checkout reports/health-report.json      |
                                                   |
/nightgauge:security-audit ────────────────────────┤
       |                                           |
  Writes: checkout reports/security-audit.json     |
                                                   ├──► /nightgauge:refactor-rewrite
/nightgauge:test-scaffold ─────────────────────────┤         |
       |                                           |    Reads: all three reports (optional)
  Writes: checkout reports/test-scaffold-report.json    Writes: refactor-rewrite-analysis.json
                                                        |
                                                        ├──► /nightgauge:modernize-plan
                                                        |
                                                   /nightgauge:dep-modernize
```

This is a standalone utility skill. It does not affect pipeline state and can be
run at any time without interfering with active pipeline runs.

---

## Configuration

This skill reads configuration from `.nightgauge/config.yaml` if present:

| Config Key                           | Default | Description               |
| ------------------------------------ | ------- | ------------------------- |
| `refactor_rewrite.default_format`    | `both`  | Default `--format` value  |
| `refactor_rewrite.skip_coverage_run` | `false` | Default for coverage run  |
| `refactor_rewrite.output_path`       | auto    | Default JSON output path  |
| `refactor_rewrite.team_size`         | `1`     | Default team size         |
| `refactor_rewrite.timeline_weeks`    | `12`    | Default timeline in weeks |

---

**Author:** nightgauge **License:** Apache-2.0

Files in this skill

  • CHANGELOG.md1.8 KB
  • SKILL.md60.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.