code-warden 3.1.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CONFIGURE.md +39 -39
  2. package/DECISIONS.md +107 -107
  3. package/README.md +199 -137
  4. package/SKILL.md +169 -169
  5. package/bin/code-warden.js +82 -0
  6. package/codewarden.json +14 -14
  7. package/examples/governed-session.md +132 -132
  8. package/install.js +399 -399
  9. package/install.ps1 +32 -32
  10. package/install.sh +33 -33
  11. package/package.json +45 -2
  12. package/references/anti-drift.md +55 -55
  13. package/references/architecture.md +26 -26
  14. package/references/cleanup.md +30 -30
  15. package/references/cognition.md +36 -36
  16. package/references/operations.md +45 -45
  17. package/references/planning-gates.md +83 -83
  18. package/references/research-and-fit.md +51 -51
  19. package/references/safety.md +31 -31
  20. package/templates/ci/github-actions.yml +83 -66
  21. package/tools/auto-detect.js +91 -91
  22. package/tools/auto-targets.js +104 -104
  23. package/tools/auto-windsurf-adapter.js +75 -75
  24. package/tools/get-context.js +50 -50
  25. package/tools/governance-report.js +302 -0
  26. package/tools/hooks/claude/install-hooks.js +112 -112
  27. package/tools/hooks/claude/uninstall-hooks.js +75 -75
  28. package/tools/hooks/claude/warden-lint-hook.js +106 -106
  29. package/tools/hooks/claude/warden-secrets-hook.js +73 -73
  30. package/tools/hooks/codex/install-hooks.js +100 -100
  31. package/tools/hooks/codex/uninstall-hooks.js +53 -53
  32. package/tools/hooks/codex/warden-apply-patch-hook.js +113 -113
  33. package/tools/hooks/codex/warden-bash-hook.js +51 -51
  34. package/tools/lib/config.js +49 -49
  35. package/tools/lib/file-collection.js +72 -72
  36. package/tools/lib/line-count.js +28 -28
  37. package/tools/lib/secret-patterns.js +57 -57
  38. package/tools/tests/fixtures/clean.js +9 -9
  39. package/tools/tests/run-tests.js +210 -210
  40. package/tools/verify-secrets.js +26 -26
  41. package/tools/warden-lint.js +27 -27
@@ -1,83 +1,83 @@
1
- # Planning Gates
2
-
3
- Two mandatory declaration blocks that fire before implementation begins.
4
- Neither is a checklist — they are structured outputs the AI produces and
5
- the user confirms before any code is written.
6
-
7
- ---
8
-
9
- ## Scope Gate
10
-
11
- Fires when: a new session begins, scope is ambiguous, or a request would
12
- touch files outside the current declared scope.
13
-
14
- **AI produces this block. User confirms before proceeding.**
15
-
16
- ```
17
- SCOPE GATE
18
-
19
- Goal: [One sentence — what this session will accomplish]
20
- Non-goals: [What is explicitly out of scope for this session]
21
- Files in: [Complete list of files the AI expects to read or modify]
22
- Files out: [Files that must not be touched, with reason]
23
- Verify after: [The exact commands that will confirm success]
24
- Rollback: [One-step revert — e.g. git checkout HEAD -- <files>]
25
- ```
26
-
27
- Rules:
28
- - Goal must be one sentence. If it cannot be, split into multiple sessions.
29
- - Files-in list is a contract. Any unlisted file requires a new Scope Gate
30
- or explicit user approval before it is touched.
31
- - Rollback must be a concrete command, not "undo manually."
32
- - If any field is unknown, write `[UNKNOWN — user must provide]` and halt.
33
-
34
- ---
35
-
36
- ## Plan Gate
37
-
38
- Fires when: the session will touch more than one file, or any single change
39
- exceeds 30 lines.
40
-
41
- **AI produces this block after Scope Gate is confirmed. User confirms before any edits.**
42
-
43
- ```
44
- PLAN GATE
45
-
46
- Patch order:
47
- 1. <file> — <one-line description of change>
48
- 2. <file> — <one-line description of change>
49
- ...
50
-
51
- Blast radius class: [CONTAINED | MODERATE | HIGH]
52
- CONTAINED — changes isolated to declared files, no interface changes
53
- MODERATE — shared interfaces or types modified, downstream callers may need updates
54
- HIGH — data-flow changes, schema changes, or deletions of public APIs
55
-
56
- Human checkpoint: [YES | NO]
57
- YES if: blast radius is MODERATE or HIGH, or more than 2 files are touched.
58
-
59
- Post-patch checks:
60
- After step 1: [command or verification]
61
- After step 2: [command or verification]
62
- After all: [final verification suite]
63
- ```
64
-
65
- Rules:
66
- - Patch order is sequential. Do not parallelise edits across files unless
67
- confirmed safe and explicitly approved.
68
- - Blast radius class must be declared before the first edit, not assessed after.
69
- - If Human Checkpoint is YES, pause after the last patch and output
70
- `[AWAITING CONFIRMATION]` before marking the task complete.
71
- - Post-patch checks must be concrete commands, not "verify it works."
72
-
73
- ---
74
-
75
- ## Gate Failure Response
76
-
77
- If either gate cannot be completed:
78
-
79
- 1. State which field is blocking and why.
80
- 2. Ask for the minimum information needed to fill it.
81
- 3. Do not produce partial gates. Do not proceed without both gates confirmed.
82
-
83
- A partial plan is not a plan. Implement nothing until both gates are signed off.
1
+ # Planning Gates
2
+
3
+ Two mandatory declaration blocks that fire before implementation begins.
4
+ Neither is a checklist — they are structured outputs the AI produces and
5
+ the user confirms before any code is written.
6
+
7
+ ---
8
+
9
+ ## Scope Gate
10
+
11
+ Fires when: a new session begins, scope is ambiguous, or a request would
12
+ touch files outside the current declared scope.
13
+
14
+ **AI produces this block. User confirms before proceeding.**
15
+
16
+ ```
17
+ SCOPE GATE
18
+
19
+ Goal: [One sentence — what this session will accomplish]
20
+ Non-goals: [What is explicitly out of scope for this session]
21
+ Files in: [Complete list of files the AI expects to read or modify]
22
+ Files out: [Files that must not be touched, with reason]
23
+ Verify after: [The exact commands that will confirm success]
24
+ Rollback: [One-step revert — e.g. git checkout HEAD -- <files>]
25
+ ```
26
+
27
+ Rules:
28
+ - Goal must be one sentence. If it cannot be, split into multiple sessions.
29
+ - Files-in list is a contract. Any unlisted file requires a new Scope Gate
30
+ or explicit user approval before it is touched.
31
+ - Rollback must be a concrete command, not "undo manually."
32
+ - If any field is unknown, write `[UNKNOWN — user must provide]` and halt.
33
+
34
+ ---
35
+
36
+ ## Plan Gate
37
+
38
+ Fires when: the session will touch more than one file, or any single change
39
+ exceeds 30 lines.
40
+
41
+ **AI produces this block after Scope Gate is confirmed. User confirms before any edits.**
42
+
43
+ ```
44
+ PLAN GATE
45
+
46
+ Patch order:
47
+ 1. <file> — <one-line description of change>
48
+ 2. <file> — <one-line description of change>
49
+ ...
50
+
51
+ Blast radius class: [CONTAINED | MODERATE | HIGH]
52
+ CONTAINED — changes isolated to declared files, no interface changes
53
+ MODERATE — shared interfaces or types modified, downstream callers may need updates
54
+ HIGH — data-flow changes, schema changes, or deletions of public APIs
55
+
56
+ Human checkpoint: [YES | NO]
57
+ YES if: blast radius is MODERATE or HIGH, or more than 2 files are touched.
58
+
59
+ Post-patch checks:
60
+ After step 1: [command or verification]
61
+ After step 2: [command or verification]
62
+ After all: [final verification suite]
63
+ ```
64
+
65
+ Rules:
66
+ - Patch order is sequential. Do not parallelise edits across files unless
67
+ confirmed safe and explicitly approved.
68
+ - Blast radius class must be declared before the first edit, not assessed after.
69
+ - If Human Checkpoint is YES, pause after the last patch and output
70
+ `[AWAITING CONFIRMATION]` before marking the task complete.
71
+ - Post-patch checks must be concrete commands, not "verify it works."
72
+
73
+ ---
74
+
75
+ ## Gate Failure Response
76
+
77
+ If either gate cannot be completed:
78
+
79
+ 1. State which field is blocking and why.
80
+ 2. Ask for the minimum information needed to fill it.
81
+ 3. Do not produce partial gates. Do not proceed without both gates confirmed.
82
+
83
+ A partial plan is not a plan. Implement nothing until both gates are signed off.
@@ -1,51 +1,51 @@
1
- # Research and Fit
2
-
3
- ## Live Research Gate
4
-
5
- Do not rely on model training data when a decision depends on current facts.
6
- Run live research first when the task involves:
7
- - Current package versions, APIs, pricing, licenses, limits, or deprecations.
8
- - Framework, runtime, cloud, database, or platform recommendations.
9
- - Security, legal, policy, compliance, or financial claims.
10
- - Recently released tools, libraries, models, protocols, or standards.
11
- - Anything the user describes as latest, current, modern, today, new, or changed.
12
-
13
- Research standard:
14
- - Prefer official docs, release notes, standards, or source repositories.
15
- - Use registry metadata for package versions and license facts.
16
- - Capture the date-sensitive fact, source, and date accessed in the response or decision log.
17
- - If live research is unavailable, say so and treat the claim as unverified.
18
-
19
- ## Default-Pattern Challenge
20
-
21
- Before choosing a stack, architecture, or product shape, challenge familiar defaults.
22
- Do not pick Node, Next.js, React, a SaaS dashboard, CRUD admin, or auth-first app
23
- unless the project context makes that choice fit.
24
-
25
- Required fit check:
26
- - User goal: what is the thing being built?
27
- - Primary user: who uses it and under what constraints?
28
- - Runtime/environment: browser, desktop, mobile, server, embedded, game engine, CLI, or hybrid.
29
- - Data shape: static, local-first, collaborative, realtime, batch, transactional, analytical, or media-heavy.
30
- - Interaction shape: workflow tool, creative tool, game, simulation, content site, dashboard, automation, API, or library.
31
- - Operational constraints: deployment target, offline needs, privacy, performance, budget, and maintenance burden.
32
-
33
- ## Recommendation Discipline
34
-
35
- When recommending an approach over alternatives:
36
- - Name at least two viable alternatives unless the user already chose the stack.
37
- - Explain why the chosen approach fits the project's constraints better.
38
- - Identify the main tradeoff or downside.
39
- - Do not optimize for what is easiest for the model to generate.
40
-
41
- ## Product-Shape Guardrail
42
-
43
- Do not assume every app is a SaaS dashboard.
44
- Match the first screen and navigation to the domain:
45
- - Operational tools can be dense and workflow-first.
46
- - Creative tools should put the canvas or creation surface first.
47
- - Games should open on the playable loop, not a marketing page.
48
- - Content sites should prioritize the content object or story.
49
- - Developer tools should expose the core command, API, or artifact early.
50
-
51
- If the shape is unclear, make a small fit assessment before designing or coding.
1
+ # Research and Fit
2
+
3
+ ## Live Research Gate
4
+
5
+ Do not rely on model training data when a decision depends on current facts.
6
+ Run live research first when the task involves:
7
+ - Current package versions, APIs, pricing, licenses, limits, or deprecations.
8
+ - Framework, runtime, cloud, database, or platform recommendations.
9
+ - Security, legal, policy, compliance, or financial claims.
10
+ - Recently released tools, libraries, models, protocols, or standards.
11
+ - Anything the user describes as latest, current, modern, today, new, or changed.
12
+
13
+ Research standard:
14
+ - Prefer official docs, release notes, standards, or source repositories.
15
+ - Use registry metadata for package versions and license facts.
16
+ - Capture the date-sensitive fact, source, and date accessed in the response or decision log.
17
+ - If live research is unavailable, say so and treat the claim as unverified.
18
+
19
+ ## Default-Pattern Challenge
20
+
21
+ Before choosing a stack, architecture, or product shape, challenge familiar defaults.
22
+ Do not pick Node, Next.js, React, a SaaS dashboard, CRUD admin, or auth-first app
23
+ unless the project context makes that choice fit.
24
+
25
+ Required fit check:
26
+ - User goal: what is the thing being built?
27
+ - Primary user: who uses it and under what constraints?
28
+ - Runtime/environment: browser, desktop, mobile, server, embedded, game engine, CLI, or hybrid.
29
+ - Data shape: static, local-first, collaborative, realtime, batch, transactional, analytical, or media-heavy.
30
+ - Interaction shape: workflow tool, creative tool, game, simulation, content site, dashboard, automation, API, or library.
31
+ - Operational constraints: deployment target, offline needs, privacy, performance, budget, and maintenance burden.
32
+
33
+ ## Recommendation Discipline
34
+
35
+ When recommending an approach over alternatives:
36
+ - Name at least two viable alternatives unless the user already chose the stack.
37
+ - Explain why the chosen approach fits the project's constraints better.
38
+ - Identify the main tradeoff or downside.
39
+ - Do not optimize for what is easiest for the model to generate.
40
+
41
+ ## Product-Shape Guardrail
42
+
43
+ Do not assume every app is a SaaS dashboard.
44
+ Match the first screen and navigation to the domain:
45
+ - Operational tools can be dense and workflow-first.
46
+ - Creative tools should put the canvas or creation surface first.
47
+ - Games should open on the playable loop, not a marketing page.
48
+ - Content sites should prioritize the content object or story.
49
+ - Developer tools should expose the core command, API, or artifact early.
50
+
51
+ If the shape is unclear, make a small fit assessment before designing or coding.
@@ -1,31 +1,31 @@
1
- # Execution and Safety
2
-
3
- ## Blast Radius Check
4
-
5
- Before deleting or rewriting any working code, define:
6
- 1. **What might break** - list affected modules and interfaces.
7
- 2. **How it will be tested** - specific test strategy or smoke test.
8
- 3. **Rollback procedure** - one-step command or revert method.
9
-
10
- Example: `git checkout HEAD -- path/to/file`
11
-
12
- Never skip for any rewrite touching core logic.
13
-
14
- ## Patch-First Editing
15
-
16
- - Prefer diff/patch edits over full file rewrites.
17
- - Only rewrite an entire file when structural changes affect >50% of its content.
18
- - All full rewrites require a Blast Radius Check first.
19
- - Surgical edits reduce accidental deletions, regression bugs, and context loss.
20
-
21
- ## Zero-Trust Secrets
22
-
23
- - Never generate code with hardcoded API keys, passwords, tokens, or placeholder secrets.
24
- - Always default to environment variables such as `.env` or secure vault implementations.
25
- - No "TODO: replace later" escape hatches. Ever.
26
-
27
- ## Dependency Freeze
28
-
29
- - Before any structural changes, list all current dependency versions.
30
- - Do not silently upgrade packages during a refactor.
31
- - Flag outdated dependencies separately. Do not fix without explicit confirmation.
1
+ # Execution and Safety
2
+
3
+ ## Blast Radius Check
4
+
5
+ Before deleting or rewriting any working code, define:
6
+ 1. **What might break** - list affected modules and interfaces.
7
+ 2. **How it will be tested** - specific test strategy or smoke test.
8
+ 3. **Rollback procedure** - one-step command or revert method.
9
+
10
+ Example: `git checkout HEAD -- path/to/file`
11
+
12
+ Never skip for any rewrite touching core logic.
13
+
14
+ ## Patch-First Editing
15
+
16
+ - Prefer diff/patch edits over full file rewrites.
17
+ - Only rewrite an entire file when structural changes affect >50% of its content.
18
+ - All full rewrites require a Blast Radius Check first.
19
+ - Surgical edits reduce accidental deletions, regression bugs, and context loss.
20
+
21
+ ## Zero-Trust Secrets
22
+
23
+ - Never generate code with hardcoded API keys, passwords, tokens, or placeholder secrets.
24
+ - Always default to environment variables such as `.env` or secure vault implementations.
25
+ - No "TODO: replace later" escape hatches. Ever.
26
+
27
+ ## Dependency Freeze
28
+
29
+ - Before any structural changes, list all current dependency versions.
30
+ - Do not silently upgrade packages during a refactor.
31
+ - Flag outdated dependencies separately. Do not fix without explicit confirmation.
@@ -1,66 +1,83 @@
1
- # Code-Warden Quality Gate — Project Template
2
- # https://github.com/Kodaxadev/Code-Warden
3
- #
4
- # Copy this file to .github/workflows/code-warden.yml in your project.
5
- #
6
- # What it enforces:
7
- # - File length limits (warden-lint.js, default 400 lines per codewarden.json)
8
- # - Zero-trust secrets (verify-secrets.js, hardcoded-credential patterns)
9
- #
10
- # How code-warden is made available in CI (choose one):
11
- #
12
- # Option A Download from release (recommended, no files to commit)
13
- # Set CODE_WARDEN_VERSION below to pin a specific release.
14
- # The "Install Code-Warden" step downloads and extracts automatically.
15
- #
16
- # Option B — Commit to your repo
17
- # Run: node /path/to/code-warden/install.js --target=claude
18
- # Add .claude/skills/code-warden/ to git tracking.
19
- # Set CODE_WARDEN_PATH: .claude/skills/code-warden
20
- # Remove the "Install Code-Warden" step.
21
- #
22
- # Customise thresholds in codewarden.json after install:
23
- # max_file_length (default 400 lines)
24
- # pre_flight_trigger_lines (default 150 lines)
25
- # human_checkpoint_files (default 2 files)
26
-
27
- name: Code-Warden Quality Gate
28
-
29
- on:
30
- push:
31
- branches: [main, master]
32
- pull_request:
33
- branches: [main, master]
34
-
35
- env:
36
- CODE_WARDEN_VERSION: v3.1.0
37
- CODE_WARDEN_PATH: .code-warden-ci
38
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
39
-
40
- jobs:
41
- code-warden:
42
- name: Code-Warden Quality Gate
43
- runs-on: ubuntu-latest
44
-
45
- steps:
46
- - name: Checkout
47
- uses: actions/checkout@v4
48
-
49
- - name: Setup Node.js
50
- uses: actions/setup-node@v4
51
- with:
52
- node-version: '24'
53
-
54
- # Option A: download from GitHub release (remove if using Option B)
55
- - name: Install Code-Warden
56
- run: |
57
- curl -fsSL -o cw.zip \
58
- "https://github.com/Kodaxadev/Code-Warden/releases/download/${{ env.CODE_WARDEN_VERSION }}/code-warden-${{ env.CODE_WARDEN_VERSION }}.zip"
59
- mkdir -p ${{ env.CODE_WARDEN_PATH }}
60
- unzip -q cw.zip -d ${{ env.CODE_WARDEN_PATH }}
61
-
62
- - name: Lint — enforce file length limits
63
- run: node ${{ env.CODE_WARDEN_PATH }}/tools/warden-lint.js .
64
-
65
- - name: Secrets zero-trust scan
66
- run: node ${{ env.CODE_WARDEN_PATH }}/tools/verify-secrets.js .
1
+ # Code-Warden Quality Gate — Project Template
2
+ # https://github.com/Kodaxadev/Code-Warden
3
+ #
4
+ # Copy this file to .github/workflows/code-warden.yml in your project.
5
+ #
6
+ # What it enforces:
7
+ # - File length limits (default 400 lines per codewarden.json)
8
+ # - Zero-trust secrets (hardcoded-credential patterns)
9
+ # - Behavioral tests (scanner and hook pass/fail verification)
10
+ # - Source integrity (required files present)
11
+ #
12
+ # What it produces:
13
+ # - .code-warden-report.json machine-readable governance artifact
14
+ # - Markdown summary on the workflow run / PR (via GITHUB_STEP_SUMMARY)
15
+ # - Uploaded artifact for audit trail (90-day retention)
16
+ #
17
+ # How code-warden is made available in CI (choose one):
18
+ #
19
+ # Option A — Download from release (recommended, no files to commit)
20
+ # Set CODE_WARDEN_VERSION below to pin a specific release.
21
+ # The "Install Code-Warden" step downloads and extracts automatically.
22
+ #
23
+ # Option B — Commit to your repo
24
+ # Run: node /path/to/code-warden/install.js --target=claude
25
+ # Add .claude/skills/code-warden/ to git tracking.
26
+ # Set CODE_WARDEN_PATH: .claude/skills/code-warden
27
+ # Remove the "Install Code-Warden" step.
28
+ #
29
+ # Customise thresholds in codewarden.json after install:
30
+ # max_file_length (default 400 lines)
31
+ # pre_flight_trigger_lines (default 150 lines)
32
+ # human_checkpoint_files (default 2 files)
33
+
34
+ name: Code-Warden Quality Gate
35
+
36
+ on:
37
+ push:
38
+ branches: [main, master]
39
+ pull_request:
40
+ branches: [main, master]
41
+
42
+ env:
43
+ CODE_WARDEN_VERSION: v3.2.0
44
+ CODE_WARDEN_PATH: .code-warden-ci
45
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
46
+
47
+ jobs:
48
+ code-warden:
49
+ name: Code-Warden Quality Gate
50
+ runs-on: ubuntu-latest
51
+
52
+ steps:
53
+ - name: Checkout
54
+ uses: actions/checkout@v4
55
+
56
+ - name: Setup Node.js
57
+ uses: actions/setup-node@v4
58
+ with:
59
+ node-version: '24'
60
+
61
+ # Option A: download from GitHub release (remove if using Option B)
62
+ - name: Install Code-Warden
63
+ run: |
64
+ curl -fsSL -o cw.zip \
65
+ "https://github.com/Kodaxadev/Code-Warden/releases/download/${{ env.CODE_WARDEN_VERSION }}/code-warden-${{ env.CODE_WARDEN_VERSION }}.zip"
66
+ mkdir -p ${{ env.CODE_WARDEN_PATH }}
67
+ unzip -q cw.zip -d ${{ env.CODE_WARDEN_PATH }}
68
+
69
+ - name: Governance report
70
+ run: node ${{ env.CODE_WARDEN_PATH }}/tools/governance-report.js .
71
+
72
+ - name: Publish governance summary
73
+ if: always()
74
+ run: node ${{ env.CODE_WARDEN_PATH }}/tools/governance-report.js . --format=md >> $GITHUB_STEP_SUMMARY
75
+
76
+ - name: Upload governance artifact
77
+ if: always()
78
+ uses: actions/upload-artifact@v4
79
+ with:
80
+ name: code-warden-report
81
+ path: .code-warden-report.json
82
+ if-no-files-found: ignore
83
+ retention-days: 90
@@ -1,91 +1,91 @@
1
- #!/usr/bin/env node
2
- /**
3
- * auto-detect.js
4
- * Detection logic for the code-warden auto-installer.
5
- * Exports scanTargets(targets) -> targets annotated with detected/method fields.
6
- *
7
- * Detection order per target:
8
- * 1. Binary found in PATH (where / which)
9
- * 2. Config directory exists in HOME
10
- * 3. App install path exists (platform-specific)
11
- */
12
-
13
- const fs = require('fs');
14
- const { execSync } = require('child_process');
15
-
16
- /**
17
- * Returns true if a CLI binary is resolvable via PATH.
18
- * Uses 'where' on Windows, 'which' on Unix.
19
- */
20
- function commandExists(bin) {
21
- try {
22
- const cmd = process.platform === 'win32' ? `where ${bin}` : `which ${bin}`;
23
- execSync(cmd, { stdio: 'ignore' });
24
- return true;
25
- } catch {
26
- return false;
27
- }
28
- }
29
-
30
- /**
31
- * Returns true if a path exists and is a directory.
32
- */
33
- function dirExists(p) {
34
- try {
35
- return fs.existsSync(p) && fs.statSync(p).isDirectory();
36
- } catch {
37
- return false;
38
- }
39
- }
40
-
41
- /**
42
- * Returns true if a path exists (file or directory).
43
- */
44
- function pathExists(p) {
45
- try {
46
- return fs.existsSync(p);
47
- } catch {
48
- return false;
49
- }
50
- }
51
-
52
- /**
53
- * Checks all detection signals for a single target.
54
- * Returns { detected: boolean, method: string|null }
55
- */
56
- function isInstalled(target) {
57
- for (const bin of (target.detect.binaries || [])) {
58
- if (commandExists(bin)) {
59
- return { detected: true, method: `binary:${bin}` };
60
- }
61
- }
62
-
63
- for (const dir of (target.detect.dirs || [])) {
64
- if (dirExists(dir)) {
65
- return { detected: true, method: `dir:${dir}` };
66
- }
67
- }
68
-
69
- const platformApps = (target.detect.apps || {})[process.platform] || [];
70
- for (const appPath of platformApps) {
71
- if (pathExists(appPath)) {
72
- return { detected: true, method: `app:${appPath}` };
73
- }
74
- }
75
-
76
- return { detected: false, method: null };
77
- }
78
-
79
- /**
80
- * Scans all targets and annotates each with detection results.
81
- * @param {Array} targets - TARGETS array from auto-targets.js
82
- * @returns {Array} - same array with detected and method fields added
83
- */
84
- function scanTargets(targets) {
85
- return targets.map(target => {
86
- const result = isInstalled(target);
87
- return { ...target, ...result };
88
- });
89
- }
90
-
91
- module.exports = { scanTargets, isInstalled, commandExists };
1
+ #!/usr/bin/env node
2
+ /**
3
+ * auto-detect.js
4
+ * Detection logic for the code-warden auto-installer.
5
+ * Exports scanTargets(targets) -> targets annotated with detected/method fields.
6
+ *
7
+ * Detection order per target:
8
+ * 1. Binary found in PATH (where / which)
9
+ * 2. Config directory exists in HOME
10
+ * 3. App install path exists (platform-specific)
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const { execSync } = require('child_process');
15
+
16
+ /**
17
+ * Returns true if a CLI binary is resolvable via PATH.
18
+ * Uses 'where' on Windows, 'which' on Unix.
19
+ */
20
+ function commandExists(bin) {
21
+ try {
22
+ const cmd = process.platform === 'win32' ? `where ${bin}` : `which ${bin}`;
23
+ execSync(cmd, { stdio: 'ignore' });
24
+ return true;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Returns true if a path exists and is a directory.
32
+ */
33
+ function dirExists(p) {
34
+ try {
35
+ return fs.existsSync(p) && fs.statSync(p).isDirectory();
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Returns true if a path exists (file or directory).
43
+ */
44
+ function pathExists(p) {
45
+ try {
46
+ return fs.existsSync(p);
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Checks all detection signals for a single target.
54
+ * Returns { detected: boolean, method: string|null }
55
+ */
56
+ function isInstalled(target) {
57
+ for (const bin of (target.detect.binaries || [])) {
58
+ if (commandExists(bin)) {
59
+ return { detected: true, method: `binary:${bin}` };
60
+ }
61
+ }
62
+
63
+ for (const dir of (target.detect.dirs || [])) {
64
+ if (dirExists(dir)) {
65
+ return { detected: true, method: `dir:${dir}` };
66
+ }
67
+ }
68
+
69
+ const platformApps = (target.detect.apps || {})[process.platform] || [];
70
+ for (const appPath of platformApps) {
71
+ if (pathExists(appPath)) {
72
+ return { detected: true, method: `app:${appPath}` };
73
+ }
74
+ }
75
+
76
+ return { detected: false, method: null };
77
+ }
78
+
79
+ /**
80
+ * Scans all targets and annotates each with detection results.
81
+ * @param {Array} targets - TARGETS array from auto-targets.js
82
+ * @returns {Array} - same array with detected and method fields added
83
+ */
84
+ function scanTargets(targets) {
85
+ return targets.map(target => {
86
+ const result = isInstalled(target);
87
+ return { ...target, ...result };
88
+ });
89
+ }
90
+
91
+ module.exports = { scanTargets, isInstalled, commandExists };