forgedock 1.0.8 → 1.0.9
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.
- package/bin/forge-utils.mjs +164 -0
- package/bin/forgedock.mjs +1914 -394
- package/bin/hooks/session-start.mjs +317 -0
- package/bin/init-detect.mjs +325 -0
- package/bin/init-enrich-api.mjs +203 -0
- package/bin/registry.mjs +358 -0
- package/bin/tui.mjs +453 -2
- package/commands/forgedock-init.md +305 -384
- package/docs/CONFIG.md +167 -0
- package/docs/superpowers/specs/2026-06-09-intelligent-onboarding-design.md +172 -0
- package/package.json +5 -1
- package/scripts/verify-route-registration.sh +23 -17
package/docs/CONFIG.md
CHANGED
|
@@ -12,6 +12,21 @@ echo "forge.yaml" >> .gitignore # if your credentials path is sensitive
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## CLI Flags for `init`
|
|
16
|
+
|
|
17
|
+
`npx forgedock init` accepts two optional flags that change how configuration is generated:
|
|
18
|
+
|
|
19
|
+
| Flag | Behavior |
|
|
20
|
+
|------|----------|
|
|
21
|
+
| `--manual` | Skips AI autopilot enrichment entirely. Presents the annotated review screen with detection baseline values only (no AI-enriched suggestions). Detection still runs to provide defaults — you review and edit the raw inferred values on the same single-screen interface. |
|
|
22
|
+
| `--verbose` | Surfaces each field's detection source and confidence rationale during the init flow. Helpful for understanding where ForgeDock inferred values from (e.g. `git remote`, `package.json`, `gh api`). |
|
|
23
|
+
|
|
24
|
+
The two flags can be combined: `npx forgedock init --manual --verbose` shows the annotated review screen with detection baseline values and field provenance at each entry.
|
|
25
|
+
|
|
26
|
+
The default (`npx forgedock init` with no flags) remains the zero-question autopilot: AI enrichment fills in everything it can, and you review the result on a single annotated screen.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
15
30
|
## Schema Overview
|
|
16
31
|
|
|
17
32
|
| Section | Required | Purpose |
|
|
@@ -468,6 +483,96 @@ Do **not** mix the two patterns for the same block of variables (e.g., `yq` with
|
|
|
468
483
|
|
|
469
484
|
---
|
|
470
485
|
|
|
486
|
+
## ConfigDraft Contract
|
|
487
|
+
|
|
488
|
+
`ConfigDraft` is the shared data structure emitted by `bin/init-detect.mjs` and consumed by the AI enrichment backends (`init-enrich`) and the annotated review renderer (`review-render`). It mirrors the required sections of `forge.yaml` at a per-field granularity, adding provenance and confidence metadata.
|
|
489
|
+
|
|
490
|
+
### Field shape
|
|
491
|
+
|
|
492
|
+
Every leaf value in a `ConfigDraft` is a **ConfigField** object:
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
{
|
|
496
|
+
value: string, // The detected or inferred value
|
|
497
|
+
confidence: "high" | "medium" | "low", // How certain the detection was
|
|
498
|
+
source: string, // Human-readable label for where the value came from
|
|
499
|
+
why: string, // Plain-language explanation of why this value was chosen
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Confidence levels:**
|
|
504
|
+
|
|
505
|
+
| Level | Meaning |
|
|
506
|
+
|-------|---------|
|
|
507
|
+
| `"high"` | Verified from a concrete, unambiguous source (e.g., parsed from the git remote URL) |
|
|
508
|
+
| `"medium"` | Inferred from available signals; likely correct but not guaranteed (e.g., current branch name, name derived from repo slug) |
|
|
509
|
+
| `"low"` | Guessed default — no supporting evidence was found (e.g., no git remote, not a git repo) |
|
|
510
|
+
|
|
511
|
+
### ConfigDraft shape
|
|
512
|
+
|
|
513
|
+
```js
|
|
514
|
+
{
|
|
515
|
+
project: {
|
|
516
|
+
owner: ConfigField, // GitHub org or username
|
|
517
|
+
repo: ConfigField, // Repository name (without owner prefix)
|
|
518
|
+
name: ConfigField, // Human-readable project name
|
|
519
|
+
},
|
|
520
|
+
paths: {
|
|
521
|
+
root: ConfigField, // Absolute path to the project root
|
|
522
|
+
worktreeBase: ConfigField, // Absolute path to the git worktree base dir
|
|
523
|
+
},
|
|
524
|
+
branches: {
|
|
525
|
+
default: ConfigField, // Default branch (e.g. "main")
|
|
526
|
+
staging: ConfigField, // Staging branch for fast-lane PRs
|
|
527
|
+
},
|
|
528
|
+
meta: {
|
|
529
|
+
remoteDetected: boolean, // true iff a parseable git remote URL was found
|
|
530
|
+
},
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Example output (high-confidence repo)
|
|
535
|
+
|
|
536
|
+
```js
|
|
537
|
+
{
|
|
538
|
+
project: {
|
|
539
|
+
owner: { value: "acme-org", confidence: "high", source: "git remote origin (SSH)", why: "Parsed from SSH remote URL: git@github.com:acme-org/acme-platform.git" },
|
|
540
|
+
repo: { value: "acme-platform", confidence: "high", source: "git remote origin (SSH)", why: "Parsed from SSH remote URL: git@github.com:acme-org/acme-platform.git" },
|
|
541
|
+
name: { value: "Acme Platform", confidence: "medium", source: "derived from repo slug", why: "Title-cased version of repo name 'acme-platform' (split on hyphens/underscores)" },
|
|
542
|
+
},
|
|
543
|
+
paths: {
|
|
544
|
+
root: { value: "/home/user/acme", confidence: "high", source: "process.cwd()", why: "Absolute path passed to detectConfig — the project root" },
|
|
545
|
+
worktreeBase: { value: "/home/user/acme/.claude/worktrees", confidence: "high", source: "derived from root", why: "Convention: {root}/.claude/worktrees" },
|
|
546
|
+
},
|
|
547
|
+
branches: {
|
|
548
|
+
default: { value: "main", confidence: "high", source: "git symbolic-ref refs/remotes/origin/HEAD", why: "Remote HEAD points to main" },
|
|
549
|
+
staging: { value: "staging", confidence: "high", source: "git branch -r", why: "Found 'origin/staging' in the remote branch listing" },
|
|
550
|
+
},
|
|
551
|
+
meta: { remoteDetected: true },
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Producing a ConfigDraft
|
|
556
|
+
|
|
557
|
+
```js
|
|
558
|
+
import { detectConfig } from "./bin/init-detect.mjs";
|
|
559
|
+
|
|
560
|
+
const draft = await detectConfig(process.cwd());
|
|
561
|
+
// draft.project.owner.value → "acme-org"
|
|
562
|
+
// draft.project.owner.confidence → "high"
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
`detectConfig(cwd)` is the sole public API. It never throws — every error path produces a `low`-confidence default.
|
|
566
|
+
|
|
567
|
+
### Consuming a ConfigDraft
|
|
568
|
+
|
|
569
|
+
Downstream consumers read `field.value` for the raw value and `field.confidence` to decide how to handle it:
|
|
570
|
+
|
|
571
|
+
- **`init-enrich`** (AI enrichment): passes `low`- and `medium`-confidence fields to the AI backend to raise their confidence; leaves `high`-confidence fields untouched.
|
|
572
|
+
- **`review-render`** (TUI review screen): shows each field with its source and why; highlights `low`-confidence fields with a `# TODO(forgedock:<field>)` annotation in the generated YAML.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
471
576
|
## CLAUDE.md Integration
|
|
472
577
|
|
|
473
578
|
ForgeDock can inject a managed usage block into your project's `CLAUDE.md` so every Claude Code session opened in the repo automatically knows that ForgeDock drives development here and which commands to use.
|
|
@@ -510,6 +615,68 @@ npx forgedock integrate
|
|
|
510
615
|
|
|
511
616
|
---
|
|
512
617
|
|
|
618
|
+
## Per-Directory State Registry
|
|
619
|
+
|
|
620
|
+
ForgeDock tracks per-directory opt-out state in a central registry file on the local machine.
|
|
621
|
+
|
|
622
|
+
### Registry File Location
|
|
623
|
+
|
|
624
|
+
```
|
|
625
|
+
~/.claude/forgedock/registry.json
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
The directory (`~/.claude/forgedock/`) is created automatically on first use with mode `0700`. The file is never committed to version control — it is per-user, per-machine state.
|
|
629
|
+
|
|
630
|
+
### Registry Schema
|
|
631
|
+
|
|
632
|
+
```json
|
|
633
|
+
{
|
|
634
|
+
"version": 1,
|
|
635
|
+
"optedOut": {
|
|
636
|
+
"/absolute/path/to/project": { "at": "2026-06-09T12:00:00.000Z" }
|
|
637
|
+
},
|
|
638
|
+
"nudgeSeen": {
|
|
639
|
+
"/absolute/path/to/project": { "at": "2026-06-09T12:00:00.000Z" }
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
| Field | Type | Description |
|
|
645
|
+
|-------|------|-------------|
|
|
646
|
+
| `version` | number | Schema version — currently `1` |
|
|
647
|
+
| `optedOut` | object | Map of absolute directory paths to opt-out metadata |
|
|
648
|
+
| `optedOut[path].at` | string | ISO-8601 timestamp of when the opt-out was recorded |
|
|
649
|
+
| `nudgeSeen` | object | Map of absolute directory paths where the one-time "Enable ForgeDock here?" nudge has already been shown |
|
|
650
|
+
| `nudgeSeen[path].at` | string | ISO-8601 timestamp of when the nudge was shown for this directory |
|
|
651
|
+
|
|
652
|
+
### State Resolution
|
|
653
|
+
|
|
654
|
+
The `registry` module resolves one of three states for any directory:
|
|
655
|
+
|
|
656
|
+
| State | Meaning |
|
|
657
|
+
|-------|---------|
|
|
658
|
+
| `managed-active` | Directory contains `forge.yaml` or `.forgedock` and is **not** opted out — ForgeDock is active here |
|
|
659
|
+
| `managed-optedout` | Directory has a managed marker but the user has explicitly opted out — ForgeDock stays silent |
|
|
660
|
+
| `unmanaged` | No `forge.yaml` or `.forgedock` marker found — ForgeDock has no presence here |
|
|
661
|
+
|
|
662
|
+
**Opt-out wins over managed**: if a directory contains `forge.yaml` but its path is listed in `optedOut`, the state is `managed-optedout`.
|
|
663
|
+
|
|
664
|
+
### Failure Behaviour
|
|
665
|
+
|
|
666
|
+
A missing or corrupt `registry.json` is treated as an empty opt-out set. The registry always fails open — it never throws and never blocks a Claude Code session.
|
|
667
|
+
|
|
668
|
+
### Managing Opt-Out State
|
|
669
|
+
|
|
670
|
+
Use the `forgedock enable` and `forgedock disable` commands to add or remove a directory from the opt-out set:
|
|
671
|
+
|
|
672
|
+
```bash
|
|
673
|
+
npx forgedock enable [dir] # Remove directory from opt-out set (default: cwd)
|
|
674
|
+
npx forgedock disable [dir] # Add directory to opt-out set (default: cwd)
|
|
675
|
+
npx forgedock status [dir] # Show resolved state for a directory
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
513
680
|
## Complete Example
|
|
514
681
|
|
|
515
682
|
See [`forge.yaml.example`](../forge.yaml.example) at the repository root for a fully annotated example covering all sections.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Intelligent, Idiot-Proof Onboarding — Design Spec
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-09
|
|
4
|
+
**Status:** Approved (design); pending implementation plan
|
|
5
|
+
**Scope:** One milestone, four phases
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Today's onboarding (`npx forgedock` TUI + `forgedock init` + the `forgedock-init`
|
|
10
|
+
skill) works but asks the first-time user ~12 questions across project / paths /
|
|
11
|
+
branches / optional sections, with little explanation of *why* each is asked or
|
|
12
|
+
*what* a good answer looks like. The deterministic auto-detect is decent, but the
|
|
13
|
+
genuinely hard sections (project board field mapping, satellites, review context,
|
|
14
|
+
verification endpoints) still fall back to manual prompts. There is no per-directory
|
|
15
|
+
control: ForgeDock commands install globally and there is no notion of "activate
|
|
16
|
+
here / stay silent there."
|
|
17
|
+
|
|
18
|
+
We want onboarding to be **intelligent** (AI fills the config), **idiot-proof**
|
|
19
|
+
(near-zero questions, everything explained), and **scoped** (a Claude Code hook
|
|
20
|
+
that activates ForgeDock only in directories the user has opted into).
|
|
21
|
+
|
|
22
|
+
## Vision
|
|
23
|
+
|
|
24
|
+
Open `claude` in any repo → a SessionStart hook resolves the directory's ForgeDock
|
|
25
|
+
state → if managed and active and unconfigured, an AI agent infers the **entire**
|
|
26
|
+
`forge.yaml` from the codebase + GitHub → the user reviews **one annotated screen**
|
|
27
|
+
and presses Enter. No wizard. Per-directory enable/disable means ForgeDock only
|
|
28
|
+
wakes up where wanted.
|
|
29
|
+
|
|
30
|
+
## Architecture — three pillars
|
|
31
|
+
|
|
32
|
+
### Pillar A — Autopilot config generation (the "intelligence")
|
|
33
|
+
|
|
34
|
+
Layered backends behind **one interface** so the experience degrades gracefully
|
|
35
|
+
(the chosen "hybrid" model):
|
|
36
|
+
|
|
37
|
+
- **`init-detect`** — deterministic, no AI. Refactors today's git / tech-stack /
|
|
38
|
+
board detection into a pure module that returns a `ConfigDraft` plus a
|
|
39
|
+
**confidence score per field** (`high` = verified from a concrete source,
|
|
40
|
+
`medium` = inferred, `low` = guessed default).
|
|
41
|
+
- **`init-enrich`** — AI enrichment. Consumes a `ConfigDraft`, deeply infers the
|
|
42
|
+
hard sections, and raises confidences. Two interchangeable backends sharing one
|
|
43
|
+
contract:
|
|
44
|
+
- **skill backend** — runs inside Claude Code; no API key; uses existing CC auth.
|
|
45
|
+
*Primary path.*
|
|
46
|
+
- **api backend** — the CLI calls the Anthropic API directly with a BYO
|
|
47
|
+
`ANTHROPIC_API_KEY`. *Used when Claude Code is not present.*
|
|
48
|
+
- **`review-render`** — a single annotated review screen. Every field is shown with
|
|
49
|
+
its **source**, **confidence**, and a plain-language "why this value." Enter to
|
|
50
|
+
accept all; inline-edit to change any field. **Unknowable items get a sensible
|
|
51
|
+
default plus an inline `# TODO(forgedock:<field>)` flag in the YAML — never a
|
|
52
|
+
blocking prompt.**
|
|
53
|
+
|
|
54
|
+
**Backend selection logic:**
|
|
55
|
+
1. Always run `init-detect` to produce the baseline draft.
|
|
56
|
+
2. If running inside Claude Code → `init-enrich` via **skill** backend.
|
|
57
|
+
3. Else if `ANTHROPIC_API_KEY` present → `init-enrich` via **api** backend.
|
|
58
|
+
4. Else → skip enrichment; render the deterministic baseline (today's quality, but
|
|
59
|
+
through the new annotated review screen).
|
|
60
|
+
|
|
61
|
+
### Pillar B — Claude Code hook + per-directory toggle
|
|
62
|
+
|
|
63
|
+
- **SessionStart hook**, installed into `~/.claude/settings.json` by
|
|
64
|
+
`forgedock install`. On each CC session it resolves the cwd's state and acts:
|
|
65
|
+
- **managed + active** → inject ForgeDock context (a `forge.yaml` summary +
|
|
66
|
+
available commands); if config is missing or stale, offer a one-shot autopilot
|
|
67
|
+
init.
|
|
68
|
+
- **managed + opted-out** → completely silent (no output, no context injection).
|
|
69
|
+
- **unmanaged** → an optional, one-time, suppressible "Enable ForgeDock here?"
|
|
70
|
+
nudge.
|
|
71
|
+
- **State model** ("marker + global opt-out"):
|
|
72
|
+
- A directory is **managed** iff it contains `forge.yaml` **or** a lightweight
|
|
73
|
+
`.forgedock` marker (lets a project signal "ForgeDock-managed" before a full
|
|
74
|
+
config exists).
|
|
75
|
+
- A central `~/.claude/forgedock/registry.json` tracks explicit **per-directory
|
|
76
|
+
opt-out** plus last-seen metadata. Opt-out wins over the marker.
|
|
77
|
+
- **New CLI commands:** `forgedock enable [dir]`, `forgedock disable [dir]`,
|
|
78
|
+
`forgedock status [dir]`.
|
|
79
|
+
- **Known constraint (explicit, accepted):** Claude Code slash-commands install
|
|
80
|
+
**globally**, so "disable per dir" means the **hook stays silent and injects
|
|
81
|
+
nothing** in that directory — it does not physically remove or hide the global
|
|
82
|
+
commands. This is the achievable and intended semantics.
|
|
83
|
+
|
|
84
|
+
### Pillar C — Idiot-proofing (cross-cutting)
|
|
85
|
+
|
|
86
|
+
- **Self-healing `validate`** — on a validation failure, the AI proposes (and, with
|
|
87
|
+
confirmation, applies) the fix instead of only printing an error.
|
|
88
|
+
- **Actionable preflight** — failures (`gh` missing, not authed, Node too old) show
|
|
89
|
+
copy-paste fix commands (partially exists today; standardize it).
|
|
90
|
+
- **Escape hatch** — `--manual` / `--verbose` runs the full guided wizard for power
|
|
91
|
+
users who *want* every prompt.
|
|
92
|
+
|
|
93
|
+
## Components & interfaces (designed for isolation)
|
|
94
|
+
|
|
95
|
+
| Unit | Purpose | Depends on | Testable in isolation? |
|
|
96
|
+
|------|---------|-----------|------------------------|
|
|
97
|
+
| `init-detect` | Pure baseline `ConfigDraft` + per-field confidence | git, fs (read-only) | Yes — fixture repos → expected draft |
|
|
98
|
+
| `init-enrich` (skill) | AI enrichment inside CC | CC session, draft contract | Contract test + recorded fixture |
|
|
99
|
+
| `init-enrich` (api) | AI enrichment via Anthropic API | `ANTHROPIC_API_KEY`, draft contract | Contract test against same interface |
|
|
100
|
+
| `review-render` | Annotated review/accept/edit TUI | draft + confidence, tui.mjs | Yes — render snapshot from a draft |
|
|
101
|
+
| `registry` | Resolve dir state; read/write opt-out + markers | fs | Yes — state-resolution matrix |
|
|
102
|
+
| `session-hook` | Thin SessionStart script; emits context JSON | registry | Yes — simulated SessionStart payloads |
|
|
103
|
+
|
|
104
|
+
**`ConfigDraft` contract** (shared by detect → enrich → render): a structured
|
|
105
|
+
object mirroring `forge.yaml` sections, where each leaf is `{ value, confidence,
|
|
106
|
+
source, why }`. This is the single seam that lets the three enrichment backends and
|
|
107
|
+
the renderer stay decoupled.
|
|
108
|
+
|
|
109
|
+
## Data flow (autopilot happy path)
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
open `claude` in repo
|
|
113
|
+
→ SessionStart hook fires
|
|
114
|
+
→ registry resolves: managed (forge.yaml absent) + active
|
|
115
|
+
→ hook suggests autopilot init (or user runs `forgedock init`)
|
|
116
|
+
→ init-detect → baseline ConfigDraft + confidences
|
|
117
|
+
→ init-enrich (skill) → enriched ConfigDraft
|
|
118
|
+
→ review-render → annotated screen → Enter to accept
|
|
119
|
+
→ write forge.yaml (with any # TODO(forgedock:…) flags)
|
|
120
|
+
→ validate (self-healing on failure)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Error handling
|
|
124
|
+
|
|
125
|
+
- **No git remote / detached repo:** `init-detect` lowers confidence, leaves fields
|
|
126
|
+
as `low`-confidence defaults with TODO flags; never crashes.
|
|
127
|
+
- **`gh` unauthenticated:** GitHub-derived sections (board, satellites) are skipped
|
|
128
|
+
with a flagged TODO and a copy-paste `gh auth login` hint.
|
|
129
|
+
- **Enrichment backend unavailable / API error:** fall through the backend-selection
|
|
130
|
+
ladder; worst case is the deterministic baseline through the review screen.
|
|
131
|
+
- **`forge.yaml` already exists:** existing backup-and-overwrite behavior is
|
|
132
|
+
preserved; review screen shows a diff-style "changed vs current."
|
|
133
|
+
- **Registry file missing/corrupt:** treat as empty opt-out set; the hook fails open
|
|
134
|
+
to silent (never blocks a CC session).
|
|
135
|
+
|
|
136
|
+
## Testing strategy
|
|
137
|
+
|
|
138
|
+
- **`registry`:** unit-test the full state matrix — managed/unmanaged × opted-in/out
|
|
139
|
+
× `forge.yaml` present/absent.
|
|
140
|
+
- **`init-detect`:** fixture repos (node, python, rust, multi-repo) → expected
|
|
141
|
+
`ConfigDraft` snapshots.
|
|
142
|
+
- **`session-hook`:** feed simulated SessionStart payloads; assert silent vs
|
|
143
|
+
context-injecting vs nudge output.
|
|
144
|
+
- **`init-enrich`:** test the `ConfigDraft` contract both backends must satisfy;
|
|
145
|
+
pin one recorded enrichment fixture for regression.
|
|
146
|
+
- **`review-render`:** snapshot the annotated screen from a known draft.
|
|
147
|
+
|
|
148
|
+
## Phasing (milestone → issues)
|
|
149
|
+
|
|
150
|
+
1. **Foundation** — `registry` module + `enable`/`disable`/`status` commands +
|
|
151
|
+
marker handling. Unblocks the hook.
|
|
152
|
+
2. **Hook** — SessionStart hook install into `settings.json` + context injection +
|
|
153
|
+
opt-out respect + unmanaged nudge.
|
|
154
|
+
3. **Autopilot brain** — `init-detect` refactor (`ConfigDraft` + confidence) →
|
|
155
|
+
`init-enrich` skill upgrade → annotated `review-render` screen.
|
|
156
|
+
4. **Resilience** — BYO-key `api` enrichment backend + self-healing `validate` +
|
|
157
|
+
`--manual` escape hatch.
|
|
158
|
+
|
|
159
|
+
All four phases ship in **one milestone**.
|
|
160
|
+
|
|
161
|
+
## Out of scope / non-goals
|
|
162
|
+
|
|
163
|
+
- Physically hiding global slash-commands per directory (see Pillar B constraint).
|
|
164
|
+
- A standalone web onboarding UI.
|
|
165
|
+
- Multi-user / team-shared registries (registry is per-machine, user-level).
|
|
166
|
+
|
|
167
|
+
## Open questions
|
|
168
|
+
|
|
169
|
+
- Exact confidence thresholds that trigger a TODO flag vs a silent default
|
|
170
|
+
(tune during Phase 3).
|
|
171
|
+
- Whether the unmanaged-directory nudge defaults to on or off (lean: off, opt-in via
|
|
172
|
+
a global setting) — decide in Phase 2.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgedock",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "GitHub as a knowledge graph for AI agents. Autonomous dev pipeline for Claude Code.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"forgedock": "bin/forgedock.mjs"
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
35
|
"bin/",
|
|
36
|
+
"!bin/tests/",
|
|
36
37
|
"commands/",
|
|
37
38
|
"docs/",
|
|
38
39
|
"scripts/",
|
|
@@ -40,6 +41,9 @@
|
|
|
40
41
|
"LICENSE",
|
|
41
42
|
"README.md"
|
|
42
43
|
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"test": "node --test \"bin/tests/**/*.test.mjs\""
|
|
46
|
+
},
|
|
43
47
|
"engines": {
|
|
44
48
|
"node": ">=18"
|
|
45
49
|
}
|
|
@@ -64,25 +64,31 @@ while read -r f; do
|
|
|
64
64
|
done
|
|
65
65
|
ROUTE_SEGMENT=$(echo "$URL_PATH" | sed 's|/api/v1/||; s|/.*||')
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
# Guard: skip shadow checks when ROUTE_SEGMENT is empty (e.g. /api/ root handlers).
|
|
68
|
+
# An empty ROUTE_SEGMENT causes grep -qF "" to match every line — false-positive WARNINGs.
|
|
69
|
+
if [ -z "$ROUTE_SEGMENT" ]; then
|
|
70
|
+
echo "OK: Route handler $f ($URL_PATH) — root handler, no segment to match, skipping shadow checks"
|
|
71
|
+
else
|
|
72
|
+
# Check if next.config.js has a rewrite that might shadow this route
|
|
73
|
+
if [ -f "$REPO_ROOT/web/next.config.js" ]; then
|
|
74
|
+
# Use -F (fixed string) so ROUTE_SEGMENT is not interpreted as a regex pattern
|
|
75
|
+
if grep -qF "$ROUTE_SEGMENT" "$REPO_ROOT/web/next.config.js" 2>/dev/null; then
|
|
76
|
+
echo "WARNING: Route handler $f ($URL_PATH) — next.config.js references '$ROUTE_SEGMENT', may shadow this route"
|
|
77
|
+
WARNINGS=$((WARNINGS + 1))
|
|
78
|
+
else
|
|
79
|
+
echo "OK: Route handler $f ($URL_PATH) — no shadowing rewrites found in next.config.js"
|
|
80
|
+
fi
|
|
75
81
|
fi
|
|
76
|
-
fi
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
# Check if nginx routes this path to Next.js or directly to backend
|
|
84
|
+
if [ -f "$REPO_ROOT/infra/nginx/nginx.conf" ]; then
|
|
85
|
+
# Check for location blocks mentioning ROUTE_SEGMENT — use two -F searches to avoid
|
|
86
|
+
# embedding the variable in a regex: first find lines with "location", then filter
|
|
87
|
+
# for the literal segment. This avoids ROUTE_SEGMENT being interpreted as a BRE.
|
|
88
|
+
if grep -F "location" "$REPO_ROOT/infra/nginx/nginx.conf" 2>/dev/null | grep -qF "$ROUTE_SEGMENT"; then
|
|
89
|
+
echo "WARNING: Route handler $f ($URL_PATH) — nginx.conf has a location block for '$ROUTE_SEGMENT', may bypass Next.js"
|
|
90
|
+
WARNINGS=$((WARNINGS + 1))
|
|
91
|
+
fi
|
|
86
92
|
fi
|
|
87
93
|
fi
|
|
88
94
|
done < <(echo "$FILES" | while IFS= read -r line; do
|