fdeops 3.13.0 → 3.15.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.
- package/AGENTS.md +1 -1
- package/README.md +178 -115
- package/adapters/README.md +1 -1
- package/bin/check.js +44 -22
- package/bin/fde.js +20 -11
- package/bin/lib/trust.js +3 -2
- package/mcp/fdeops-ingest/package.json +1 -1
- package/package.json +2 -2
- package/plugin.json +3 -3
- package/skills/fde/SKILL.md +78 -158
- package/skills/fde/references/pattern-extract.md +9 -9
- package/skills/fde/archive/sdlc/README.md +0 -7
- package/skills/fde/archive/sdlc/build.md +0 -154
- package/skills/fde/archive/sdlc/debug.md +0 -55
- package/skills/fde/archive/sdlc/observability.md +0 -103
- package/skills/fde/archive/sdlc/qa-live.md +0 -113
- package/skills/fde/archive/sdlc/security-audit.md +0 -105
- package/skills/fde/archive/sdlc/test-on-legacy.md +0 -108
package/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md - working in the fdeops repository
|
|
2
2
|
|
|
3
|
-
This repository **is** fdeops
|
|
3
|
+
This repository **is** fdeops — the engagement record for Forward Deployed Engineers. One `@fde` skill, the `fde` CLI for deterministic work, and per-customer memory in `.fde/` as a side effect of the work (you still confirm judgment).
|
|
4
4
|
|
|
5
5
|
## If you are helping use fdeops in an engagement
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -1,134 +1,235 @@
|
|
|
1
1
|
# FDEOps
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Forward deployed engineering skills for AI coding agents.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://github.com/suboss87/fdeops/actions)
|
|
7
|
-
[](https://skills.sh/suboss87/fdeops)
|
|
8
|
-
[](LICENSE)
|
|
9
|
-
[](https://nodejs.org)
|
|
5
|
+
Skills encode the workflows, quality gates, and judgment Forward Deployed Engineers use on someone else's site. Packaged so an AI coding agent follows them consistently — and writes a dated record you can defend. The host agent still writes the TypeScript.
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
```text
|
|
8
|
+
LAND DISCOVER PLAN SHIP PROVE CLOSE
|
|
9
|
+
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
|
|
10
|
+
│ Brief │ ───▶ │ Reality│ ───▶ │ Sequence│───▶ │ Live │ ───▶ │ Signed │ ───▶ │ They │
|
|
11
|
+
│ Trust │ │ Terrain│ │ Align │ │ slice │ │ off │ │ run │
|
|
12
|
+
└────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘
|
|
13
|
+
/brief /discover /plan /ship /got /close
|
|
14
|
+
```
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
---
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
## Commands
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
6 slash commands that map to the engagement. Each one loads `@fde`. You never pick a method.
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
| What you're doing | Command | Principle |
|
|
23
|
+
|-------------------|---------|-----------|
|
|
24
|
+
| Land the embed | `/brief` | Brief and trust before code |
|
|
25
|
+
| Find the real problem | `/discover` | Brief is a hypothesis |
|
|
26
|
+
| Plan the sequence | `/plan` | Backwards from done |
|
|
27
|
+
| Ship a slice | `/ship` | Pre-flight, then live |
|
|
28
|
+
| Prove what they got | `/got` | Promised → measured → accepted |
|
|
29
|
+
| Close the embed | `/close` | They can run it without you |
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
Also: `/debrief` (after a meeting) · `/prep` (walk-in) · `/quiet` (sponsor silent) · `/agreed` (scope dispute) · `/status` (Friday readout).
|
|
22
32
|
|
|
23
|
-
`@fde` plus English.
|
|
33
|
+
`@fde` plus English activates the same skill automatically. Ordinary TypeScript, unit tests, and git commits stay in the host agent.
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|------|--------------|--------------|
|
|
27
|
-
| **The brief is wrong** | `@fde this is Acme. Brief says they want a portal.` | Real problem, or a gap. First chat: you name the client; the AI coding agent binds. |
|
|
28
|
-
| **They went quiet** | `@fde the sponsor went quiet` | Trust signal in the record — process gap vs trust problem. |
|
|
29
|
-
| **When did we agree?** | `@fde when did we agree to drop that?` | Dated receipts, or a clear gap. |
|
|
30
|
-
| **What did they get?** | `@fde what did they get this week` | Friday ledger: promised → measured → accepted. |
|
|
31
|
-
| **After a meeting** | `@fde` debrief these notes *(paste or attach)* | Proposed updates. You review, then confirm. |
|
|
32
|
-
| **Optional: pull** | `@fde` connect Granola *(once)* · `@fde` pull today's Acme transcript | You add that source MCP. We **pull** on request — no push, no sync. [mcp/recipes/](mcp/recipes/) |
|
|
35
|
+
---
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
## Quick Start
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npx skills add suboss87/fdeops --skill fde
|
|
41
|
+
```
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
Then one chat. Name the client. The AI coding agent binds.
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
```text
|
|
46
|
+
@fde this is Acme
|
|
47
|
+
```
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
Paste kickoff notes in the same thread. `@fde` routes; you confirm judgment. Same folder every time: `~/fde-engagements/<client>/.fde/`. Workflow: [docs/USAGE.md](docs/USAGE.md).
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
<details>
|
|
52
|
+
<summary><b>Claude Code (recommended)</b></summary>
|
|
45
53
|
|
|
46
54
|
```text
|
|
47
55
|
/plugin marketplace add suboss87/fdeops
|
|
48
56
|
/plugin install fdeops@fdeops
|
|
49
57
|
```
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
Hooks load where you left off. Slash commands match the map above.
|
|
60
|
+
|
|
61
|
+
</details>
|
|
62
|
+
|
|
63
|
+
<details>
|
|
64
|
+
<summary><b>Cursor</b></summary>
|
|
52
65
|
|
|
53
66
|
```bash
|
|
54
67
|
npx skills add suboss87/fdeops --skill fde
|
|
55
68
|
```
|
|
56
69
|
|
|
57
|
-
|
|
70
|
+
Or `npx fdeops adapters .` — [adapters/](adapters/README.md).
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
@fde this is Acme
|
|
61
|
-
```
|
|
72
|
+
</details>
|
|
62
73
|
|
|
63
|
-
|
|
74
|
+
<details>
|
|
75
|
+
<summary><b>Other agents</b></summary>
|
|
64
76
|
|
|
65
|
-
|
|
77
|
+
```bash
|
|
78
|
+
npx skills add suboss87/fdeops --skill fde
|
|
79
|
+
```
|
|
66
80
|
|
|
67
|
-
|
|
68
|
-
<summary>Terminal bind · other hosts · env</summary>
|
|
81
|
+
Gemini, Copilot, Codex, local LLMs: [adapters/](adapters/README.md). Air-gapped: `git clone https://github.com/suboss87/fdeops.git && node bin/install.js`.
|
|
69
82
|
|
|
70
|
-
Fallback if the agent cannot bind
|
|
83
|
+
Fallback if the agent cannot bind:
|
|
71
84
|
|
|
72
85
|
```bash
|
|
73
86
|
npx fdeops resume --init acme # ~/fde-engagements/acme + bind this checkout
|
|
74
|
-
npx fdeops resume # where we are
|
|
75
87
|
```
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
- **Local LLMs:** load `skills/fde/SKILL.md` — [guide](adapters/LOCAL-LLM.md)
|
|
79
|
-
- **Air-gapped:** `git clone https://github.com/suboss87/fdeops.git && cd fdeops && node bin/install.js`
|
|
80
|
-
- **No install:** `npx fdeops demo` · `npx fdeops scan` (heuristic recon, not findings)
|
|
81
|
-
- **Requires:** Node.js >= 18
|
|
82
|
-
- **Override:** `FDEOPS_ENGAGEMENT` — [docs/install.md](docs/install.md)
|
|
89
|
+
Requires Node.js >= 18. Override: `FDEOPS_ENGAGEMENT` — [docs/install.md](docs/install.md). Try the loop: `npx fdeops demo`.
|
|
83
90
|
|
|
84
91
|
</details>
|
|
85
92
|
|
|
86
93
|
---
|
|
87
94
|
|
|
88
|
-
##
|
|
95
|
+
## All 31 Methods
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
The commands above are the entry points. One `@fde` skill routes to these 31 methods — each a structured workflow with an artifact and a checkpoint. You never pick a method by name. Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
98
|
+
|
|
99
|
+
### Land
|
|
100
|
+
|
|
101
|
+
| Method | What it does | Use when |
|
|
102
|
+
|--------|--------------|----------|
|
|
103
|
+
| [land](skills/fde/references/land.md) | Interrogate the brief, map stakeholders, define success | New client, first meeting, just got the brief |
|
|
104
|
+
| [audit](skills/fde/references/audit.md) | Verify claims, find the load-bearing wall | Taking over, previous consultant left |
|
|
105
|
+
| [stakeholder-radar](skills/fde/references/stakeholder-radar.md) | Who decides, who blocks, who escalates | Need to know who matters |
|
|
106
|
+
| [trust-engineering](skills/fde/references/trust-engineering.md) | Observer → trusted; navigate AI policy | Need access or credibility |
|
|
107
|
+
| [scope-defense](skills/fde/references/scope-defense.md) | Scope receipts; the accumulation conversation | "Also can you…", timeline unchanged |
|
|
108
|
+
|
|
109
|
+
### Discover
|
|
110
|
+
|
|
111
|
+
| Method | What it does | Use when |
|
|
112
|
+
|--------|--------------|----------|
|
|
113
|
+
| [discover](skills/fde/references/discover.md) | Repo + workaround + the real problem | Brief feels wrong, shadow processes |
|
|
114
|
+
| [assumption-audit](skills/fde/references/assumption-audit.md) | Untested assumptions by blast radius | Brief feels too neat |
|
|
115
|
+
| [use-case-scoring](skills/fde/references/use-case-scoring.md) | Value × urgency × alignment / complexity | Everything is P0 |
|
|
116
|
+
| [sketch](skills/fde/references/sketch.md) | Kill the killer assumption in a day | Need to de-risk a direction |
|
|
117
|
+
|
|
118
|
+
### Plan
|
|
119
|
+
|
|
120
|
+
| Method | What it does | Use when |
|
|
121
|
+
|--------|--------------|----------|
|
|
122
|
+
| [plan](skills/fde/references/plan.md) | Backwards from done, PR-sized | What order, what is done |
|
|
123
|
+
| [business-case](skills/fde/references/business-case.md) | Cost of nothing → investment → return | Defend budget or timeline |
|
|
124
|
+
| [options-analysis](skills/fde/references/options-analysis.md) | Three genuine options | "What should we do?" |
|
|
125
|
+
| [initiative-triage](skills/fde/references/initiative-triage.md) | Pick three from twenty urgents | Everything is urgent |
|
|
126
|
+
|
|
127
|
+
### Ship
|
|
128
|
+
|
|
129
|
+
| Method | What it does | Use when |
|
|
130
|
+
|--------|--------------|----------|
|
|
131
|
+
| [incremental-build](skills/fde/references/incremental-build.md) | Vertical slices, visible every 2–3 days | Large feature on their codebase |
|
|
132
|
+
| [blast-radius](skills/fde/references/blast-radius.md) | Impact from contained → irreversible | Touching shared infrastructure |
|
|
133
|
+
| [rescue](skills/fde/references/rescue.md) | Production fire or trust fire | Down, or they went quiet |
|
|
134
|
+
| [ship](skills/fde/references/ship.md) | Intent vs diff, pre-flight, rollback | Going live |
|
|
135
|
+
| [review](skills/fde/references/review.md) | Did we only build what we agreed | Before merge, scope creep |
|
|
136
|
+
| [rollback-drill](skills/fde/references/rollback-drill.md) | Test the escape route before 2am | "We can always revert" |
|
|
137
|
+
|
|
138
|
+
### Prove
|
|
93
139
|
|
|
94
|
-
|
|
140
|
+
| Method | What it does | Use when |
|
|
141
|
+
|--------|--------------|----------|
|
|
142
|
+
| [status](skills/fde/references/status.md) | Promised → measured → accepted | Friday, sponsor update |
|
|
143
|
+
| [demo-prep](skills/fde/references/demo-prep.md) | One number, five hard questions | Demo or exec walkthrough |
|
|
144
|
+
| [debrief](skills/fde/references/debrief.md) | Meeting notes into the record | Just left a meeting |
|
|
145
|
+
| [exec-narrative](skills/fde/references/exec-narrative.md) | Board / sponsor's boss | Justify continued investment |
|
|
146
|
+
| [dashboard](skills/fde/references/dashboard.md) | Portfolio, trust-ordered | All my customers |
|
|
147
|
+
| [ingest](skills/fde/references/ingest.md) | Pull text you confirm | Transcript, Notion, Slack |
|
|
148
|
+
| [ingest-connect](skills/fde/references/ingest-connect.md) | Wire a source MCP | Connect Granola |
|
|
95
149
|
|
|
96
|
-
|
|
150
|
+
### Close
|
|
97
151
|
|
|
98
|
-
|
|
152
|
+
| Method | What it does | Use when |
|
|
153
|
+
|--------|--------------|----------|
|
|
154
|
+
| [close](skills/fde/references/close.md) | Handoff that survives you | Wrapping up |
|
|
155
|
+
| [handoff-engineering](skills/fde/references/handoff-engineering.md) | Runbook, confidence scoring | They must operate without you |
|
|
156
|
+
| [multi-customer-ops](skills/fde/references/multi-customer-ops.md) | Switch without bleed | 2+ clients |
|
|
157
|
+
| [pattern-extract](skills/fde/references/pattern-extract.md) | If you did it twice, encode it | It will apply again |
|
|
158
|
+
| [red-team](skills/fde/references/red-team.md) | Stress-test before they do | "Poke holes in this" |
|
|
99
159
|
|
|
100
|
-
|
|
160
|
+
Overlays (on signal, not on request): [ai](skills/fde/references/ai.md) · [artifacts](skills/fde/references/artifacts.md) · [fintech](skills/fde/references/fintech.md) · [healthcare](skills/fde/references/healthcare.md) · [gov](skills/fde/references/gov.md) · [eval-pack](skills/fde/references/eval-pack.md)
|
|
161
|
+
|
|
162
|
+
Optional pull: you add the source MCP; we **pull** on request. [mcp/recipes/](mcp/recipes/)
|
|
101
163
|
|
|
102
164
|
---
|
|
103
165
|
|
|
104
166
|
## How it works
|
|
105
167
|
|
|
106
|
-
|
|
107
|
-
- **Hooks (Claude Code)** load where you left off and snapshot on the way out. Other hosts: same CLI and files; you call `@fde`.
|
|
108
|
-
- **Local CLI** — writes, receipts, status. Zero model tokens. The AI coding agent runs it; you do not live in the CLI. Friday, `fde status` prints promised → measured → accepted. [docs/USAGE.md](docs/USAGE.md)
|
|
109
|
-
- **Pull (optional)** — FDEOps is the sink. Paste is the daily path. A source MCP you add (Granola, Slack, Notion, …) can fetch text; `@fde connect …` walks config. No push, no sync, no tokens in `.fde/`. [mcp/recipes/](mcp/recipes/)
|
|
168
|
+
Every method follows the same anatomy:
|
|
110
169
|
|
|
111
|
-
|
|
170
|
+
```
|
|
171
|
+
┌─────────────────────────────────────────────┐
|
|
172
|
+
│ @fde (one skill) │
|
|
173
|
+
│ │
|
|
174
|
+
│ ┌─ Frontmatter ─────────────────────────┐ │
|
|
175
|
+
│ │ name: fde │ │
|
|
176
|
+
│ │ description: Use when [client work] │ │
|
|
177
|
+
│ └───────────────────────────────────────┘ │
|
|
178
|
+
│ │
|
|
179
|
+
│ Commands load it. English loads it. │
|
|
180
|
+
│ You confirm. Then .fde/ is written. │
|
|
181
|
+
└─────────────────────────────────────────────┘
|
|
182
|
+
│
|
|
183
|
+
▼
|
|
184
|
+
references/<method>.md fde CLI (local)
|
|
185
|
+
one file, then stop dating, gates, redaction
|
|
186
|
+
```
|
|
112
187
|
|
|
113
|
-
|
|
188
|
+
- **Process, not prose.** Methods are workflows with an artifact and a checkpoint.
|
|
189
|
+
- **You confirm.** Nothing is written until you say so.
|
|
190
|
+
- **Progressive disclosure.** `SKILL.md` is the entry point. One `references/*.md` loads when routed.
|
|
191
|
+
- **Local CLI.** Writes, receipts, status. Zero model tokens. The AI coding agent runs it.
|
|
114
192
|
|
|
115
|
-
Change hosts, install `@fde` on the new one, bind if needed, keep talking.
|
|
193
|
+
The record lives at `~/fde-engagements/<client>/.fde/` — not inside any vendor. Change hosts, install `@fde` on the new one, bind if needed, keep talking.
|
|
116
194
|
|
|
117
|
-
|
|
118
|
-
<summary>Engagement verbs</summary>
|
|
195
|
+
**Words used here, once:** *engagement* - one client's body of work, one folder. *Fieldbook* - that folder (`.fde/`), the record itself. *Brief vs reality* - what they said the problem was, and what it turned out to be. *Terrain* - their systems and org as you actually found them. *Trust signal* - green / amber / red on one relationship. *Receipts* - the dated line proving something was agreed. *Vault* - the Obsidian copy `fde vault` generates to read it all in one window.
|
|
119
196
|
|
|
120
|
-
|
|
121
|
-
|------|------|
|
|
122
|
-
| **land** | First days — brief, stakeholders, success |
|
|
123
|
-
| **discover** | The brief is wrong — evidence from the repo |
|
|
124
|
-
| **plan** | Sequence backwards from done, PR-sized |
|
|
125
|
-
| log delivery | After the host agent codes — what shipped, how it rolls back |
|
|
126
|
-
| **ship** | Pre-flight, canary, rollback |
|
|
127
|
-
| **close** | Handoff, retro, receipts that survive you |
|
|
197
|
+
---
|
|
128
198
|
|
|
129
|
-
|
|
199
|
+
## Project Structure
|
|
130
200
|
|
|
131
|
-
|
|
201
|
+
```
|
|
202
|
+
fdeops/
|
|
203
|
+
├── skills/fde/ # the one skill
|
|
204
|
+
│ ├── SKILL.md # router
|
|
205
|
+
│ └── references/ # 31 methods + overlays
|
|
206
|
+
├── .claude/commands/ # slash commands (each loads @fde)
|
|
207
|
+
├── bin/fde.js # local CLI — git + files, no network
|
|
208
|
+
├── hooks/ # session-start / session-stop / pre-compact
|
|
209
|
+
├── adapters/ # Cursor, Gemini, Copilot, Codex pointers
|
|
210
|
+
├── templates/.fde/ # memory files created on bind
|
|
211
|
+
└── docs/ # usage, schema, install
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Why this exists
|
|
217
|
+
|
|
218
|
+
### 1. The brief is wrong
|
|
219
|
+
|
|
220
|
+
The most common failure on an embed is building the portal they asked for. Ops has been running a spreadsheet for two years. `/brief` then `/discover` — who in their company would have to agree it worked?
|
|
221
|
+
|
|
222
|
+
### 2. They went quiet
|
|
223
|
+
|
|
224
|
+
A sponsor who stops answering is not a Jira gap. It is a trust color. `/quiet` — process vs trust, then a dated signal in the record.
|
|
225
|
+
|
|
226
|
+
### 3. When did we agree?
|
|
227
|
+
|
|
228
|
+
Arguments from memory lose. `/agreed` searches dated receipts. No hit is a gap, not proof.
|
|
229
|
+
|
|
230
|
+
### 4. What did they get?
|
|
231
|
+
|
|
232
|
+
A number only you agree with is claimed, not delivered. `/got` reads promised → measured → accepted out loud.
|
|
132
233
|
|
|
133
234
|
---
|
|
134
235
|
|
|
@@ -145,15 +246,7 @@ One folder per client. Plain markdown. Grep it, copy it, defend it.
|
|
|
145
246
|
| `trust-profile.md` | Sacred data, AI policy, approval chain |
|
|
146
247
|
| `decisions.md` / `risks.md` / `delivery.md` | Dated choices; live risks; what shipped and how it rolls back |
|
|
147
248
|
|
|
148
|
-
Schema: [docs/schema.md](docs/schema.md).
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Fieldbook UI
|
|
153
|
-
|
|
154
|
-
Local HTML: trust, phase, next, the record. `@fde` dashboard, or `npx fdeops dashboard` (`--all` for the portfolio).
|
|
155
|
-
|
|
156
|
-
<p align="center"><img width="1336" height="624" alt="fdeops Fieldbook in the browser" src="https://github.com/user-attachments/assets/5683614c-7730-4a3a-860d-185053a377eb" /></p>
|
|
249
|
+
Schema: [docs/schema.md](docs/schema.md). Local HTML: `npx fdeops dashboard`.
|
|
157
250
|
|
|
158
251
|
---
|
|
159
252
|
|
|
@@ -161,23 +254,17 @@ Local HTML: trust, phase, next, the record. `@fde` dashboard, or `npx fdeops das
|
|
|
161
254
|
|
|
162
255
|
| You are | What this is |
|
|
163
256
|
|---------|----------------|
|
|
164
|
-
| **Forward Deployed Engineer** |
|
|
257
|
+
| **Forward Deployed Engineer** | Client work that has to survive Monday morning |
|
|
165
258
|
| **Consultant / contractor on site** | The engagement stops resetting every morning |
|
|
166
259
|
| **Solutions architect** | Politics and architecture in the same record |
|
|
167
260
|
| **Agency, 3–5 clients** | One `.fde/` each — they stop blurring |
|
|
168
261
|
| **Fractional CTO on client work** | System of record for the embed, and the billable trail |
|
|
169
262
|
|
|
170
|
-
Ordinary TypeScript, unit tests, and git commits stay in the host agent.
|
|
171
|
-
|
|
172
263
|
---
|
|
173
264
|
|
|
174
265
|
## Your data stays yours
|
|
175
266
|
|
|
176
|
-
|
|
177
|
-
- **Plain markdown.** No database.
|
|
178
|
-
- **No new data path.** The model sees client code only when you point the AI coding agent at it. `<private>` is redacted from CLI, dashboard, and hooks — do not open raw private blocks with file tools.
|
|
179
|
-
- **Nothing unreviewed.** Draft → you confirm. `fde debrief --dry-run` shows routing first.
|
|
180
|
-
- **Know the sync surface.** `~/fde-engagements` is in `$HOME`. iCloud/Dropbox is an NDA incident waiting. `resume --init` warns. [PRIVACY.md](PRIVACY.md) before the first NDA.
|
|
267
|
+
Local only — `git` + files, no network, no telemetry. Plain markdown. The model sees client code only when you point the AI coding agent at it. `<private>` is redacted from CLI, dashboard, and hooks. Nothing is written until you confirm. `~/fde-engagements` is in `$HOME`; iCloud/Dropbox is an NDA incident waiting.
|
|
181
268
|
|
|
182
269
|
[PRIVACY.md](PRIVACY.md) · [SECURITY.md](SECURITY.md)
|
|
183
270
|
|
|
@@ -193,36 +280,12 @@ Ordinary TypeScript, unit tests, and git commits stay in the host agent.
|
|
|
193
280
|
|
|
194
281
|
---
|
|
195
282
|
|
|
196
|
-
## Updating
|
|
197
|
-
|
|
198
|
-
Re-run the Quickstart install, or from a clone: `git pull && node bin/install.js`
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
283
|
## Contributing
|
|
203
284
|
|
|
204
285
|
**[Subash Natarajan](https://www.linkedin.com/in/subashn/)**. [Issues](https://github.com/suboss87/fdeops/issues) · [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
205
286
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
[FDE Methodology](FDE-METHODOLOGY.md) · [SECURITY.md](SECURITY.md) · [PRIVACY.md](PRIVACY.md) · [Repo layout](docs/REPO_LAYOUT.md) · [Skills matrix](docs/skills.md) · MIT
|
|
287
|
+
Methods should be **specific** (actionable steps), **verifiable** (an artifact in `.fde/`), and **minimal**. The `fde` CLI stays local-only.
|
|
209
288
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<details>
|
|
213
|
-
<summary>31 field methods (you never pick one)</summary>
|
|
289
|
+
## License
|
|
214
290
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
| Domain | Methods |
|
|
218
|
-
|--------|---------|
|
|
219
|
-
| **1. Embed & Trust** | [land](skills/fde/references/land.md) · [audit](skills/fde/references/audit.md) · [stakeholder-radar](skills/fde/references/stakeholder-radar.md) · [trust-engineering](skills/fde/references/trust-engineering.md) · [scope-defense](skills/fde/references/scope-defense.md) |
|
|
220
|
-
| **2. Discover & Diagnose** | [discover](skills/fde/references/discover.md) · [assumption-audit](skills/fde/references/assumption-audit.md) · [use-case-scoring](skills/fde/references/use-case-scoring.md) · [sketch](skills/fde/references/sketch.md) |
|
|
221
|
-
| **3. Plan & Align** | [plan](skills/fde/references/plan.md) · [business-case](skills/fde/references/business-case.md) · [options-analysis](skills/fde/references/options-analysis.md) · [initiative-triage](skills/fde/references/initiative-triage.md) |
|
|
222
|
-
| **4. Build & Guard** | [incremental-build](skills/fde/references/incremental-build.md) · [blast-radius](skills/fde/references/blast-radius.md) · [rescue](skills/fde/references/rescue.md) |
|
|
223
|
-
| **5. Ship & Verify** | [ship](skills/fde/references/ship.md) · [review](skills/fde/references/review.md) · [rollback-drill](skills/fde/references/rollback-drill.md) |
|
|
224
|
-
| **6. Operate & Close** | [status](skills/fde/references/status.md) · [demo-prep](skills/fde/references/demo-prep.md) · [debrief](skills/fde/references/debrief.md) · [exec-narrative](skills/fde/references/exec-narrative.md) · [dashboard](skills/fde/references/dashboard.md) · [multi-customer-ops](skills/fde/references/multi-customer-ops.md) · [close](skills/fde/references/close.md) · [handoff-engineering](skills/fde/references/handoff-engineering.md) · [pattern-extract](skills/fde/references/pattern-extract.md) · [red-team](skills/fde/references/red-team.md) · [ingest](skills/fde/references/ingest.md) · [ingest-connect](skills/fde/references/ingest-connect.md) |
|
|
225
|
-
|
|
226
|
-
Overlays: [ai](skills/fde/references/ai.md) · [artifacts](skills/fde/references/artifacts.md) · [fintech](skills/fde/references/fintech.md) · [healthcare](skills/fde/references/healthcare.md) · [gov](skills/fde/references/gov.md)
|
|
227
|
-
|
|
228
|
-
</details>
|
|
291
|
+
MIT — use these skills on client work.
|
package/adapters/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**One brain, thin adapters.** fdeops has a single source of truth - the `@fde` skill at `skills/fde/SKILL.md` and the `fde` CLI. Each AI coding tool discovers it through a small pointer file in the place that tool already looks. No forked logic, no five copies to maintain - every adapter says the same thing: *route via `@fde`, read/write `.fde/` memory, talk like a peer, never touch what isn't yours.*
|
|
4
4
|
|
|
5
|
-
**Switching tools:** the fieldbook does not live in the agent. It lives at `~/fde-engagements/<client>/.fde/`. Point a new tool at a bound workspace, drop adapters (or install the skill/plugin for that tool), and the same client record opens. Auto session hooks are Claude Code–first; elsewhere load via `@fde` / `fde resume`. See [README §
|
|
5
|
+
**Switching tools:** the fieldbook does not live in the agent. It lives at `~/fde-engagements/<client>/.fde/`. Point a new tool at a bound workspace, drop adapters (or install the skill/plugin for that tool), and the same client record opens. Auto session hooks are Claude Code–first; elsewhere load via `@fde` / `fde resume`. See [README § How it works](../README.md#how-it-works).
|
|
6
6
|
|
|
7
7
|
## What goes where
|
|
8
8
|
|
package/bin/check.js
CHANGED
|
@@ -41,12 +41,11 @@ for (const f of requiredTemplates) {
|
|
|
41
41
|
else ok(`template ${f}`)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} else {
|
|
48
|
-
ok('media/demo.gif')
|
|
44
|
+
const deadMedia = ['demo.gif', 'demo.sh', 'demo.tape', 'terminal-demo.svg', 'fieldbook-dashboard.png', 'fieldbook-detail.png', 'fieldbook-walkthrough.gif']
|
|
45
|
+
for (const name of deadMedia) {
|
|
46
|
+
if (fs.existsSync(path.join(root, 'media', name))) fail(`dead media/${name} must not ship — the recorded session is session.gif`)
|
|
49
47
|
}
|
|
48
|
+
ok('no staged mock media')
|
|
50
49
|
|
|
51
50
|
for (const dir of fs.readdirSync(path.join(root, 'skills'))) {
|
|
52
51
|
const skill = path.join(root, 'skills', dir, 'SKILL.md')
|
|
@@ -57,6 +56,10 @@ for (const dir of fs.readdirSync(path.join(root, 'skills'))) {
|
|
|
57
56
|
}
|
|
58
57
|
ok('skills structure')
|
|
59
58
|
|
|
59
|
+
if (fs.existsSync(path.join(root, 'skills', 'fde', 'archive'))) {
|
|
60
|
+
fail('skills/fde/archive must not exist — unrouted methods are dead code')
|
|
61
|
+
} else ok('no archived skill dump')
|
|
62
|
+
|
|
60
63
|
// v3: one skill + phase references (progressive disclosure)
|
|
61
64
|
const requiredReferences = [
|
|
62
65
|
'land.md', 'discover.md', 'audit.md', 'plan.md', 'review.md',
|
|
@@ -118,7 +121,7 @@ ok(`router dispatch (${mentioned.length} reference targets verified) + memory co
|
|
|
118
121
|
// Every routing row must parse. A row this misses is a method that could go
|
|
119
122
|
// undocumented for free, so an unparsed row is a hard failure, not a silent skip.
|
|
120
123
|
const routed = new Set()
|
|
121
|
-
const routing = (router.split(
|
|
124
|
+
const routing = (router.split(/^## Routing[^\n]*$/m)[1] || '').split('**Overlays')[0]
|
|
122
125
|
const methodCell = line => (line.split('|')[2] || '').trim().replace(/\s*\([^)]*\)\s*$/, '')
|
|
123
126
|
for (const line of routing.split('\n')) {
|
|
124
127
|
if (!/^\|/.test(line)) continue
|
|
@@ -197,6 +200,11 @@ ok(`router dispatch (${mentioned.length} reference targets verified) + memory co
|
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
ok(`public method count is verifiable (${documented.size} documented, ${routed.size} routed)`)
|
|
203
|
+
|
|
204
|
+
const refDir = path.join(root, 'skills', 'fde', 'references')
|
|
205
|
+
const extra = fs.readdirSync(refDir).filter(f => f.endsWith('.md') && !mentioned.includes(f))
|
|
206
|
+
if (extra.length) fail(`unrouted reference file(s) — dead method: ${extra.join(', ')}`)
|
|
207
|
+
else ok('no unrouted reference files')
|
|
200
208
|
}
|
|
201
209
|
|
|
202
210
|
const install = read('bin/install.js')
|
|
@@ -215,26 +223,20 @@ if (read('package.json').includes('postinstall')) {
|
|
|
215
223
|
}
|
|
216
224
|
|
|
217
225
|
const readme = read('README.md')
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// it must stay reachable and reproducible - an orphaned gif rots silently.
|
|
226
|
-
const recordingHosts = ['README.md', 'docs/USAGE.md'].filter(f => read(f).includes('media/session.gif'))
|
|
227
|
-
if (!recordingHosts.length) {
|
|
228
|
-
fail('media/session.gif must be embedded in README.md or docs/USAGE.md (the recorded session is the proof)')
|
|
229
|
-
} else if (!recordingHosts.some(f => read(f).includes('media/record-session.sh'))) {
|
|
230
|
-
fail('link media/record-session.sh next to the recording, so it can be re-recorded')
|
|
226
|
+
if (/session\.gif|demo\.gif|<img /i.test(readme)) {
|
|
227
|
+
fail('README must not embed images — front door is text; the recording lives in docs/USAGE.md')
|
|
228
|
+
} else ok('README is text (no gif)')
|
|
229
|
+
|
|
230
|
+
const usage = read('docs/USAGE.md')
|
|
231
|
+
if (!usage.includes('media/session.gif') || !usage.includes('media/record-session.sh')) {
|
|
232
|
+
fail('docs/USAGE.md must embed media/session.gif and link media/record-session.sh')
|
|
231
233
|
} else {
|
|
232
234
|
const gifPath = path.join(root, 'media', 'session.gif')
|
|
233
235
|
const rec = path.join(root, 'media', 'record-session.sh')
|
|
234
236
|
if (!fs.existsSync(gifPath) || fs.statSync(gifPath).size < 50000) fail('media/session.gif missing or too small')
|
|
235
237
|
else if (!fs.existsSync(rec)) fail('media/record-session.sh missing - the recording must be reproducible')
|
|
236
238
|
else if (!fs.existsSync(path.join(root, 'media', 'session.cast'))) fail('media/session.cast missing - keep the source recording next to the gif')
|
|
237
|
-
else ok(
|
|
239
|
+
else ok('recorded session in docs/USAGE.md (gif + reproducible recorder + cast)')
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
// Every repo-relative README link and image must resolve, or the front door 404s.
|
|
@@ -249,10 +251,10 @@ else ok('README links all resolve')
|
|
|
249
251
|
|
|
250
252
|
for (const section of [
|
|
251
253
|
'How it works',
|
|
252
|
-
'
|
|
254
|
+
'Quick Start',
|
|
253
255
|
'Engagement memory',
|
|
254
256
|
'Who this is for',
|
|
255
|
-
'
|
|
257
|
+
'Commands',
|
|
256
258
|
'Principles',
|
|
257
259
|
]) {
|
|
258
260
|
if (!readme.includes(section)) fail(`README missing section: ${section}`)
|
|
@@ -262,6 +264,16 @@ if (!readme.includes('AI coding agent')) {
|
|
|
262
264
|
}
|
|
263
265
|
ok('README clarity sections')
|
|
264
266
|
|
|
267
|
+
for (const cmd of ['/brief', '/discover', '/plan', '/ship', '/got', '/close', '/debrief', '/prep', '/quiet', '/agreed', '/status']) {
|
|
268
|
+
if (!readme.includes(cmd)) fail(`README must document slash command ${cmd}`)
|
|
269
|
+
}
|
|
270
|
+
ok('README slash commands documented')
|
|
271
|
+
|
|
272
|
+
// Front-door map is the embed left-to-right (LAND → CLOSE), not a pile of situations.
|
|
273
|
+
if (!readme.slice(0, 4000).includes('LAND') || !readme.slice(0, 4000).includes('/discover')) {
|
|
274
|
+
fail('README must include the LAND→CLOSE command-map diagram near the top')
|
|
275
|
+
} else ok('README command-map diagram')
|
|
276
|
+
|
|
265
277
|
if (readme.includes('your-client-repo')) {
|
|
266
278
|
fail('README must not instruct install in customer repo (your-client-repo)')
|
|
267
279
|
} else ok('README no customer-repo install')
|
|
@@ -535,6 +547,16 @@ const plugin = JSON.parse(read('.claude-plugin/plugin.json'))
|
|
|
535
547
|
if (pkg.version !== plugin.version) {
|
|
536
548
|
fail(`version mismatch package.json ${pkg.version} vs plugin ${plugin.version}`)
|
|
537
549
|
} else ok('plugin version aligned')
|
|
550
|
+
if (plugin.commands !== './.claude/commands' || plugin.skills !== './skills') {
|
|
551
|
+
fail('.claude-plugin/plugin.json must declare skills and commands')
|
|
552
|
+
} else {
|
|
553
|
+
for (const cmd of ['brief', 'discover', 'plan', 'ship', 'got', 'close', 'debrief', 'prep', 'quiet', 'agreed', 'status']) {
|
|
554
|
+
const rel = `.claude/commands/${cmd}.md`
|
|
555
|
+
if (!fs.existsSync(path.join(root, rel))) fail(`${rel} missing`)
|
|
556
|
+
else if (!read(rel).includes('@fde')) fail(`${rel} must load @fde`)
|
|
557
|
+
}
|
|
558
|
+
ok('slash commands load @fde')
|
|
559
|
+
}
|
|
538
560
|
|
|
539
561
|
if (!fs.existsSync(path.join(root, 'mcp', 'fdeops-ingest', 'server.js'))) {
|
|
540
562
|
fail('mcp/fdeops-ingest/server.js missing (ingest MCP sink)')
|
package/bin/fde.js
CHANGED
|
@@ -738,11 +738,20 @@ function appendLogEntry(eng, type, entry, opts = {}) {
|
|
|
738
738
|
// degrading to "nothing found" rather than guessing when the shape does not
|
|
739
739
|
// match. Never fabricate a number, a name, or a signal that is not in the text.
|
|
740
740
|
|
|
741
|
+
const PHASES = ['land', 'discover', 'plan', 'ship', 'prove', 'close']
|
|
742
|
+
const PHASE_ALIASES = { build: 'ship' } // legacy SDLC name; public map is ship
|
|
741
743
|
const PHASE_LABELS = {
|
|
742
|
-
land: '
|
|
743
|
-
|
|
744
|
+
land: 'Land', discover: 'Discover', plan: 'Plan',
|
|
745
|
+
ship: 'Ship', prove: 'Prove', close: 'Close',
|
|
746
|
+
}
|
|
747
|
+
function canonicalPhase(phase) {
|
|
748
|
+
const p = String(phase).toLowerCase()
|
|
749
|
+
return PHASE_ALIASES[p] || p
|
|
750
|
+
}
|
|
751
|
+
function phaseLabel(phase) {
|
|
752
|
+
const p = canonicalPhase(phase)
|
|
753
|
+
return PHASE_LABELS[p] || phase
|
|
744
754
|
}
|
|
745
|
-
function phaseLabel(phase) { return PHASE_LABELS[String(phase).toLowerCase()] || phase }
|
|
746
755
|
|
|
747
756
|
// First real (non-blank, non-heading) line of a prose file, bold-label prefix
|
|
748
757
|
// stripped ("**Confirmed:** text..." -> "text...") - same spirit as the
|
|
@@ -1312,11 +1321,11 @@ function cmdLog(args) {
|
|
|
1312
1321
|
const eng = resolveEngagement({ forWrite: true })
|
|
1313
1322
|
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
1314
1323
|
|
|
1315
|
-
// fde log phase <land|discover|plan|
|
|
1324
|
+
// fde log phase <land|discover|plan|ship|prove|close> - advances portfolio phase
|
|
1316
1325
|
if (type === 'phase') {
|
|
1317
|
-
const phase = (text || '').toLowerCase().trim()
|
|
1318
|
-
if (!
|
|
1319
|
-
console.error(
|
|
1326
|
+
const phase = canonicalPhase((text || '').toLowerCase().trim())
|
|
1327
|
+
if (!PHASES.includes(phase)) {
|
|
1328
|
+
console.error(`usage: fde log phase <${PHASES.join('|')}>`)
|
|
1320
1329
|
process.exit(1)
|
|
1321
1330
|
}
|
|
1322
1331
|
const hash = setContextPhase(eng, phase)
|
|
@@ -1324,7 +1333,7 @@ function cmdLog(args) {
|
|
|
1324
1333
|
return
|
|
1325
1334
|
}
|
|
1326
1335
|
|
|
1327
|
-
if (!LOG_FILES[type] || !text) { console.error(
|
|
1336
|
+
if (!LOG_FILES[type] || !text) { console.error(`usage: fde log <decision|risk|delivery|contact> <text> [--signal red|amber|green] [--force]\n fde log phase <${PHASES.join('|')}>\n fde log --undo`); process.exit(1) }
|
|
1328
1337
|
if (signal && type !== 'contact') { console.error('--signal only applies to: fde log contact'); process.exit(1) }
|
|
1329
1338
|
const hit = findSecretHit(text)
|
|
1330
1339
|
if (hit && !force) { refuseSecret('log text', hit); process.exit(1) }
|
|
@@ -2091,7 +2100,7 @@ function collectDoctorIssues(eng) {
|
|
|
2091
2100
|
|
|
2092
2101
|
if (s.phase === '?' || s.phase === 'unset') {
|
|
2093
2102
|
if (hasDatedWork) {
|
|
2094
|
-
issues.push(
|
|
2103
|
+
issues.push(`phase is unset but dated work exists - run: fde log phase <${PHASES.join('|')}>`)
|
|
2095
2104
|
}
|
|
2096
2105
|
}
|
|
2097
2106
|
if (s.stale) issues.push(`trust signal is STALE (${s.signalAge}d) - reconfirm with fde log contact ... --signal`)
|
|
@@ -2161,7 +2170,7 @@ function collectDoctorIssues(eng) {
|
|
|
2161
2170
|
}
|
|
2162
2171
|
// Failure-path (exception-led operating map): required once past discover.
|
|
2163
2172
|
// Land seeds; discover fills; plan+ without a real break→owner row is wallpaper.
|
|
2164
|
-
if (/^(plan|
|
|
2173
|
+
if (/^(plan|ship|prove|close)$/.test(s.phase) && !hasOperatingMapContent(eng)) {
|
|
2165
2174
|
issues.push(
|
|
2166
2175
|
`phase is ${s.phase} with empty operating map - fill terrain.md ## Operating map (exception-led): break → who notices → workaround → evidence`
|
|
2167
2176
|
)
|
|
@@ -3178,7 +3187,7 @@ function printUsage() {
|
|
|
3178
3187
|
fde resume --bind show what this workspace is bound to, and what resolves
|
|
3179
3188
|
fde triage TRIAGE block only (hooks / Cursor session entry)
|
|
3180
3189
|
fde log <type> <text> append decision|risk|delivery|contact (contact takes --signal red|amber|green; --force to allow secret-like text)
|
|
3181
|
-
fde log phase <phase> set engagement phase (land|discover|plan|
|
|
3190
|
+
fde log phase <phase> set engagement phase (land|discover|plan|ship|prove|close)
|
|
3182
3191
|
fde log --undo remove the last CLI log/debrief entry from memory
|
|
3183
3192
|
fde debrief [file] meeting notes → memory (prefixed lines; --dry-run; --force)
|
|
3184
3193
|
fde debrief --smart heuristic propose (prefix + light keywords); agent routes, CLI gates → --apply
|
package/bin/lib/trust.js
CHANGED
|
@@ -64,8 +64,9 @@ function createTrustApi(deps) {
|
|
|
64
64
|
if (!m) return '?'
|
|
65
65
|
const raw = m[1].replace(/\*/g, '').trim()
|
|
66
66
|
if (!raw || /\|/.test(raw) || /^unset$/i.test(raw) || /^[\[(]/.test(raw)) return '?'
|
|
67
|
-
const one = raw.toLowerCase().match(/^(land|discover|plan|build|ship|close)\b/)
|
|
68
|
-
|
|
67
|
+
const one = raw.toLowerCase().match(/^(land|discover|plan|build|ship|prove|close)\b/)
|
|
68
|
+
if (!one) return '?'
|
|
69
|
+
return one[1] === 'build' ? 'ship' : one[1]
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
function countOpenRisks(eng) {
|