fdeops 3.9.19 → 3.9.20

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/README.md CHANGED
@@ -31,6 +31,7 @@ Day to day you only need `@fde` and normal English. No command cheat sheet.
31
31
  |------|--------------|--------------|
32
32
  | **Start of week** | Open your AI coding agent (nothing to paste) | It already knows where you left off - trust, phase, what's next |
33
33
  | **After a meeting** | `@fde` debrief these notes *(paste or attach them)* | Proposed updates to the record - you review, then confirm |
34
+ | **Pull from tools** | `@fde` connect Granola *(once)* · then `@fde` pull today's Acme transcript | Wire any source MCP you choose; FDEOps stages → proposes → you confirm. Recipes: [mcp/recipes/](mcp/recipes/) |
34
35
  | **Before a stakeholder meeting** | `@fde` prep me for tomorrow's meeting with the sponsor | A short brief from what you already logged - not a blank chat |
35
36
  | **Someone disputes scope** | `@fde` when did we agree to drop that feature? | Dated answers from the record (or a clear gap if nothing was logged) |
36
37
  | **End of week** | `@fde` draft the sponsor update from the record | Status grounded in what actually happened |
@@ -91,7 +92,7 @@ npx fdeops resume # prints a short "where we are" for this clien
91
92
  - **You** describe the situation with `@fde` (or plain language once the skill is loaded)
92
93
  - **Session start / end** - small hooks load where you left off and capture what changed (no re-paste)
93
94
  - **Local CLI** - memory writes, search, and status with no model tokens; the agent runs it. You do not need to learn it for daily use ([docs/USAGE.md](docs/USAGE.md))
94
- - **Pluggable pull (ingest)** - large transcripts and emails can land in `.inbox/` via `fde ingest stage` after **your** source MCPs fetch them (Granola, Gmail, Notion, custom not bundled in fdeops). Same propose → confirm → apply loop as debrief; nothing unreviewed enters the fieldbook. No ambient sync.
95
+ - **Pluggable pull (ingest)** - FDEOps is the **sink**, not a connector pack. You add any source MCP (Granola, Notion, Drive, …) in Cursor/Claude; say `@fde connect …` for a guided config + recipe, then pull in plain language. Raw text → `.inbox/` → propose → you confirm → `.fde/`. No ambient sync; nothing unreviewed enters the fieldbook. See [mcp/recipes/](mcp/recipes/).
95
96
 
96
97
  fdeops complements repo memory: CLAUDE.md holds how the *code* works; the fieldbook holds how the *client engagement* works.
97
98
 
package/bin/check.js CHANGED
@@ -351,7 +351,15 @@ if (!fs.existsSync(path.join(root, 'mcp', 'fdeops-ingest', 'server.js'))) {
351
351
  fail('ingest MCP must expose ingest_stage')
352
352
  } else if (!read('skills/fde/references/ingest.md').includes('stage')) {
353
353
  fail('skills/fde/references/ingest.md missing stage contract')
354
- } else ok('ingest MCP + skill reference')
354
+ } else if (!fs.existsSync(path.join(root, 'skills', 'fde', 'references', 'ingest-connect.md'))) {
355
+ fail('skills/fde/references/ingest-connect.md missing')
356
+ } else {
357
+ for (const recipe of ['file.md', 'granola.md', 'notion.md']) {
358
+ if (!fs.existsSync(path.join(root, 'mcp', 'recipes', recipe))) fail(`mcp/recipes/${recipe} missing`)
359
+ }
360
+ if (!read('README.md').includes('mcp/recipes')) fail('README must point at mcp/recipes for connect clarity')
361
+ ok('ingest MCP + connect recipes + skill reference')
362
+ }
355
363
 
356
364
  if (!fs.existsSync(path.join(root, '.github', 'ISSUE_TEMPLATE', 'bug_report.yml'))) {
357
365
  fail('GitHub issue template missing')
package/mcp/README.md CHANGED
@@ -36,6 +36,10 @@ Source MCP(s) fdeops-ingest MCP fde CLI
36
36
  |---------|------|---------|
37
37
  | `fdeops-ingest-mcp` | [`fdeops-ingest/`](./fdeops-ingest/) | Ingest sink (stage, list, propose, apply) |
38
38
 
39
+ ## Recipes (copy-paste connect)
40
+
41
+ See [`recipes/`](./recipes/) for file, Granola-shaped, and Notion-shaped setup. In chat: `@fde I want to connect Granola` → skill `ingest-connect` walks the FDE through config + reload + verify.
42
+
39
43
  ## Adding a source MCP
40
44
 
41
45
  Source MCPs are **not** bundled in fdeops. To add Granola, Gmail, or another provider:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fdeops-ingest-mcp",
3
- "version": "3.9.19",
3
+ "version": "3.9.20",
4
4
  "private": true,
5
5
  "description": "Thin stdio MCP sink for FDEOps ingest (stage → propose → apply). Zero runtime dependencies.",
6
6
  "bin": {
@@ -0,0 +1,15 @@
1
+ # Ingest source recipes
2
+
3
+ FDEOps does **not** bundle Granola / Notion / Drive OAuth. These recipes show how an FDE wires a **source MCP** (or file drop) into the FDEOps **sink**.
4
+
5
+ **Contract every source must satisfy:** fetch text → `fde ingest stage` (or MCP `ingest_stage`) with `{ source, title, content }` → propose → FDE confirms → apply.
6
+
7
+ | Recipe | When |
8
+ |--------|------|
9
+ | [file.md](./file.md) | Local transcript / export already on disk (no source MCP) |
10
+ | [granola.md](./granola.md) | Meeting transcripts via a Granola-shaped MCP (or export) |
11
+ | [notion.md](./notion.md) | Notion pages / meeting notes via a Notion MCP |
12
+
13
+ Also wire the sink once: [fdeops-ingest/README.md](../fdeops-ingest/README.md).
14
+
15
+ **Natural language:** `@fde I want to connect Granola` → agent follows `skills/fde/references/ingest-connect.md` and this recipe.
@@ -0,0 +1,25 @@
1
+ # Recipe: local file / paste (no source MCP)
2
+
3
+ **Use when:** you already have a transcript, `.eml`, or export on disk — or you paste into chat.
4
+
5
+ ## Setup
6
+
7
+ None beyond the FDEOps sink (`fde` CLI and optionally `fdeops-ingest` MCP).
8
+
9
+ ## Pull phrase
10
+
11
+ ```text
12
+ @fde stage this transcript into the fieldbook and propose updates
13
+ ```
14
+
15
+ (or attach / point at a path)
16
+
17
+ ## Agent steps
18
+
19
+ 1. Bind engagement.
20
+ 2. `fde ingest stage --source file --title "<short>" <path>` (or stdin).
21
+ 3. `fde ingest propose <id>` → rewrite prefixes → show FDE → on confirm `fde ingest apply`.
22
+
23
+ ## mcp.json
24
+
25
+ Not required for the source. Optional sink only — see [../fdeops-ingest/README.md](../fdeops-ingest/README.md).
@@ -0,0 +1,58 @@
1
+ # Recipe: Granola-shaped meeting transcripts
2
+
3
+ **Use when:** meeting notes live in Granola (or a similar notes MCP). FDEOps does not ship a Granola server — you add whichever MCP/export path you trust.
4
+
5
+ ## Setup (once)
6
+
7
+ 1. Install / enable a **Granola (or notes) MCP** in Cursor/Claude per that product’s docs.
8
+ 2. Add the FDEOps **sink** MCP (`fdeops-ingest`) — [../fdeops-ingest/README.md](../fdeops-ingest/README.md).
9
+ 3. Reload MCP / restart the agent host.
10
+ 4. Test: `@fde what can you pull?` — agent should see both sink tools and the notes source tools.
11
+
12
+ ### Example mcp.json shape (illustrative)
13
+
14
+ Replace `granola-mcp` command/args with whatever the real server documents. FDEOps only needs *some* tool that returns transcript text.
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "granola": {
20
+ "command": "npx",
21
+ "args": ["-y", "YOUR-GRANOLA-MCP-PACKAGE"],
22
+ "env": {
23
+ "GRANOLA_API_KEY": "from-your-secrets"
24
+ }
25
+ },
26
+ "fdeops-ingest": {
27
+ "command": "node",
28
+ "args": ["/absolute/path/to/fdeops/mcp/fdeops-ingest/server.js"],
29
+ "env": {
30
+ "FDEOPS_ENGAGEMENT": "/Users/you/fde-engagements/acme/.fde"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ **No Granola MCP available?** Export transcript to a file → follow [file.md](./file.md).
38
+
39
+ ## Pull phrase
40
+
41
+ ```text
42
+ @fde pull today's Acme Granola into the fieldbook
43
+ ```
44
+
45
+ ## Agent steps
46
+
47
+ 1. Capability check — if no notes/Granola tools, run connect flow (`ingest-connect.md`).
48
+ 2. Fetch transcript via source MCP (or ask which meeting).
49
+ 3. `ingest_stage` / `fde ingest stage --source granola --title "…"`.
50
+ 4. Propose → confirm → apply. Never auto-apply.
51
+
52
+ ## Common fails
53
+
54
+ | Symptom | Fix |
55
+ |---------|-----|
56
+ | Agent says it can’t reach Granola | MCP not saved / host not reloaded / wrong env key |
57
+ | Wrong client inbox | Set `FDEOPS_ENGAGEMENT` or bind workspace (`fde resume --init`) |
58
+ | Empty propose | Agent must rewrite `.debrief-propose` with type prefixes |
@@ -0,0 +1,56 @@
1
+ # Recipe: Notion docs / meeting notes
2
+
3
+ **Use when:** useful engagement notes live in Notion. FDEOps does not ship a Notion server — use a Notion MCP (or export markdown).
4
+
5
+ ## Setup (once)
6
+
7
+ 1. Enable a **Notion MCP** (official or community) with a token that can read the pages you need.
8
+ 2. Add **fdeops-ingest** sink — [../fdeops-ingest/README.md](../fdeops-ingest/README.md).
9
+ 3. Reload MCP / restart host.
10
+ 4. Test: `@fde what can you pull?`
11
+
12
+ ### Example mcp.json shape (illustrative)
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "notion": {
18
+ "command": "npx",
19
+ "args": ["-y", "YOUR-NOTION-MCP-PACKAGE"],
20
+ "env": {
21
+ "NOTION_TOKEN": "from-your-secrets"
22
+ }
23
+ },
24
+ "fdeops-ingest": {
25
+ "command": "node",
26
+ "args": ["/absolute/path/to/fdeops/mcp/fdeops-ingest/server.js"],
27
+ "env": {
28
+ "FDEOPS_ENGAGEMENT": "/Users/you/fde-engagements/acme/.fde"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ **No Notion MCP?** Export page to markdown → [file.md](./file.md).
36
+
37
+ ## Pull phrase
38
+
39
+ ```text
40
+ @fde pull the Acme discovery notes Notion page into the fieldbook
41
+ ```
42
+
43
+ ## Agent steps
44
+
45
+ 1. Capability check — Notion tools present?
46
+ 2. Fetch page/block text via Notion MCP (ask which page if ambiguous).
47
+ 3. Stage with `--source notion`.
48
+ 4. Propose → confirm → apply.
49
+
50
+ ## Common fails
51
+
52
+ | Symptom | Fix |
53
+ |---------|-----|
54
+ | 401 / forbidden | Token lacks access to that workspace/page |
55
+ | Huge page dump | Stage full text in `.inbox/`; propose only short dated facts |
56
+ | Wrong engagement | Bind / `FDEOPS_ENGAGEMENT` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fdeops",
3
- "version": "3.9.19",
3
+ "version": "3.9.20",
4
4
  "description": "Field kit for engineers embedded in client work - a real CLI (recon, memory, portfolio), one @fde skill with field judgment on top, and hooks that make it automatic. Claude Code plugin and any agent that loads skills.",
5
5
  "bin": {
6
6
  "fdeops": "bin/install.js",
@@ -83,7 +83,8 @@ When NOT to interrogate or challenge: unambiguous one-liners, mechanical ops, FD
83
83
  | (session entry / where are we) | `fde resume` or use injected TRIAGE; `fde resume --init <name>` only if unbound |
84
84
  | Day-1 look at the repo | `fde scan` - then you interpret against the brief |
85
85
  | "Debrief these notes" / pastes meeting notes | Prefer `fde debrief --smart <notes>` → **you** (the agent) rewrite `.debrief-propose` with `decision:`/`risk:`/`delivery:`/`contact:`/`next:` prefixes where needed → show FDE → on confirm `fde debrief --apply`. `--smart` is a prefix/keyword gate, not a brain. Fallback: structure prefixed lines yourself, show FDE, then `fde debrief` |
86
- | "Make sure we're up to date" / "pull relevant info" / "pull from Granola/email/transcript" | Bind engagement; if ambiguous ask which meeting/thread. Use whatever source MCPs the FDE has configured (Granola, Gmail, Notion, custom **not bundled in fdeops**) to fetch raw text → `fde ingest stage [--source NAME] [--title TEXT]` → `fde ingest propose <id>` **you** rewrite `.debrief-propose` with type prefixes → show FDE → on confirm `fde ingest apply`. **Never auto-apply. Never ambient sync.** Detail: `references/ingest.md` |
86
+ | "Make sure we're up to date" / "pull relevant info" / "pull from Granola/email/transcript" | Bind engagement; **capability check** (which source MCPs exist this session never pretend). If missing connect flow. Else fetch → `fde ingest stage` → `fde ingest propose` → rewrite prefixes → show FDE → on confirm `fde ingest apply`. **Never auto-apply. Never ambient sync.** Detail: `references/ingest.md` |
87
+ | "Connect a new MCP" / "connect Granola/Notion" / "what can you pull?" | Follow `references/ingest-connect.md`: ask which source → emit `mcp.json` from `mcp/recipes/` + sink block → they save/reload in Cursor/Claude → verify tools → optional test stage to `.inbox/` only. You cannot silently install host MCPs. |
87
88
  | "Prep me for the meeting with …" / walk-in brief | `fde prep "<short label>"` - present the brief in plain language; do not invent facts missing from `.fde/` |
88
89
  | "When did we agree…?" / scope dispute | `fde receipts <term>` - answer with dates; no hit = gap, not proof |
89
90
  | "Draft the sponsor update" / how are we doing | `fde status` then follow `references/status.md` for the narrative |
@@ -270,7 +271,8 @@ Running the engagement and ending it well.
270
271
  | Weekly update due, "need to send the sponsor something" | status | `references/status.md` |
271
272
  | Demo coming up, show-and-tell, exec walkthrough | demo-prep | `references/demo-prep.md` |
272
273
  | Just out of a meeting, raw notes, "they said…", "debrief" | debrief | the debrief verb (above) + `references/debrief.md` |
273
- | Make sure we're up to date, pull what's relevant, fetch from Granola/Gmail/transcript | ingest | `references/ingest.md` (stage → propose → confirm → apply; source MCPs are user-configured) |
274
+ | Make sure we're up to date, pull what's relevant, fetch from Granola/Gmail/transcript | ingest | `references/ingest.md` (capability check → stage → propose → confirm → apply) |
275
+ | Connect a new MCP / connect Granola or Notion / what can you pull | ingest-connect | `references/ingest-connect.md` (+ `mcp/recipes/`) |
274
276
  | Prep me for a meeting / walk-in brief / "what should I know before I talk to…" | - | run `fde prep "<label>"`, present in plain language |
275
277
  | Sponsor's boss needs a summary, board update, justify continued investment | exec-narrative | `references/exec-narrative.md` |
276
278
  | Status across all my customers | dashboard | `references/dashboard.md` |
@@ -0,0 +1,36 @@
1
+ # ingest-connect - wire a source MCP in plain language
2
+
3
+ **Enter when:** the FDE says "I want to connect a new MCP", "connect Granola / Notion / Drive", "how do I pull from …", or a pull request fails because no source tools exist.
4
+
5
+ **Read first:** `references/ingest.md` (sink contract). Recipe pack: `mcp/recipes/` in the fdeops install (file, granola, notion).
6
+
7
+ **Who runs setup:** you guide; the **host** (Cursor/Claude) must save MCP config. You cannot silently install servers into the host.
8
+
9
+ ## Honest contract
10
+
11
+ - FDEOps = **sink** (`fdeops-ingest` / `fde ingest`). Sources = **whatever MCP the FDE adds**.
12
+ - You produce a ready config snippet + steps. They save + reload. Then you verify with a capability check + optional test stage.
13
+ - Never invent that Granola/Notion is available if tools are missing. Never ambient sync. Never auto-apply to `.fde/`.
14
+
15
+ ## Method
16
+
17
+ 1. **Ask one question** — which source? (`file` / `granola` / `notion` / other name). If "other", ask for the MCP package or docs URL they intend to use.
18
+ 2. **Capability check (current session)** — list MCP tools you can actually call:
19
+ - Sink present? (`ingest_stage` / `ingest_list` / or `fde ingest` CLI)
20
+ - Source present? (anything that can fetch that system's content)
21
+ - Say clearly: *available now* vs *needs config*.
22
+ 3. **Emit config** — open the matching recipe under `mcp/recipes/<source>.md`. Fill absolute paths:
23
+ - path to `mcp/fdeops-ingest/server.js` (from this fdeops install)
24
+ - `FDEOPS_ENGAGEMENT` → this client's `…/<slug>/.fde`
25
+ - placeholders for source API keys (tell them to paste secrets into host env — do not commit keys into the fieldbook)
26
+ 4. **Tell them where to paste** — Cursor: MCP settings / `~/.cursor/mcp.json` (or project MCP). Claude Code: MCP config per their docs. One sentence: save → reload MCP / restart session.
27
+ 5. **Verify** — after they confirm reload: re-run capability check. If source tools appear, offer a **test pull** into `.inbox/` only (stage + show list). Stop before apply unless they ask to propose.
28
+ 6. **Handoff phrase** — give them the daily line, e.g. `@fde pull today's Acme Granola into the fieldbook`.
29
+
30
+ ## If they only want the sink
31
+
32
+ Still wire `fdeops-ingest` (or rely on CLI). File drops work with [mcp/recipes/file.md](../../../mcp/recipes/file.md) without any source MCP.
33
+
34
+ ## Checkpoint
35
+
36
+ Before ending connect: (1) sink reachable, (2) source reachable or honest gap, (3) they know the pull phrase. Do not write `.fde/` during connect.
@@ -2,6 +2,8 @@
2
2
 
3
3
  **Enter when:** the FDE wants to catch the engagement up from external sources — "make sure Acme is up to date," "pull what's relevant," "grab today's Granola and Denise's last email." Raw transcripts and long emails that are too big to paste usefully.
4
4
 
5
+ **Connect / capability (different entry):** "connect a new MCP", "connect Granola/Notion", "what can you pull?" → `references/ingest-connect.md` first. Recipes: `mcp/recipes/` (file, granola, notion).
6
+
5
7
  **Read first:** `context.md` (what's already logged, what's stale). Bind the engagement before staging anything.
6
8
 
7
9
  **Who runs the CLI:** you (the agent). Never tell the FDE to type `fde ingest …`. Never auto-apply. Never background-sync or poll sources on your own.
@@ -13,10 +15,18 @@
13
15
  - The core `fde` CLI stays local (git + file reads). Source credentials live with that MCP; fdeops never stores them.
14
16
  - After apply, raw stays in `.inbox/`; the system of record (`.fde/`) stays thin dated facts.
15
17
 
18
+ ## Capability check (before every pull)
19
+
20
+ List what you can actually call **this session**:
21
+
22
+ 1. **Sink** — `ingest_stage` / `fde ingest` available?
23
+ 2. **Sources** — which fetch tools exist (Granola-shaped, Notion, Drive, file-only)?
24
+ 3. Tell the FDE in one line: *I can pull from X; Y is not connected.* If they asked to pull Y and it is missing → switch to `ingest-connect.md`. Never pretend a source exists.
25
+
16
26
  ## Ground loop (you do this work)
17
27
 
18
28
  1. **Bind** the engagement (`fde resume` / registry). If multiple meetings or threads could apply, ask **one** clarifying question — which meeting, which thread, which date range.
19
- 2. **Fetch** via the FDE's available source MCP(s). You pull; the CLI does not reach the network.
29
+ 2. **Capability check** (above). Then **fetch** via available source MCP(s). You pull; the CLI does not reach the network.
20
30
  3. **Stage** — `fde ingest stage [--source NAME] [--title TEXT] [file|-]` writes raw text into `<engagement>/.inbox/` (outside the memory git ledger).
21
31
  4. **List** (optional) — `fde ingest list` shows staged items when you need an id or filename.
22
32
  5. **Propose** — `fde ingest propose <id-or-filename>` runs the debrief `--smart` path on the staged body (+ provenance line). Opens `.debrief-propose`.
@@ -47,9 +57,9 @@ fde ingest apply
47
57
 
48
58
  When a staged fact came from a named source, carry `via:<source>` on the applied line where useful (e.g. `via:granola`, `via:gmail`). Helps receipts and sponsor disputes later — not mandatory on every context line.
49
59
 
50
- ## MCP sink
60
+ ## MCP sink + recipes
51
61
 
52
- Optional `mcp/fdeops-ingest` wraps the same verbs over stdio. Source MCPs remain separate — the FDE adds whichever fetch tools they trust.
62
+ Optional `mcp/fdeops-ingest` wraps the same verbs over stdio. Source MCPs remain separate — the FDE adds whichever fetch tools they trust. Setup coach: `ingest-connect.md`. Copy-paste recipes: `mcp/recipes/`.
53
63
 
54
64
  ## Checkpoint
55
65