Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
"url": "https://github.com/suboss87"
},
"metadata": {
"description": "Second brain for Forward Deployed Engineers - one @fde skill, phase methods, and local .fde/ engagement memory."
"description": "Engagement memory for AI coding agents - one @fde skill, phase methods, and local .fde/ client memory."
},
"plugins": [
{
"name": "fdeops",
"source": "./",
"description": "Second brain for Forward Deployed Engineers: one @fde entry point that routes by situation (land, discover, build, rescue, ship, close), does the phase work, and keeps per-customer engagement memory in local .fde/ files. Healthcare, fintech, and government overlays included."
"description": "Engagement memory for AI coding agents. One entry point that routes by situation (land, discover, build, rescue, ship, close), does the phase work, and keeps per-client memory - sponsor, promise, decision, acceptance, dated - in local .fde/ files. For Forward Deployed Engineers running several clients at once; healthcare, fintech, and government overlays included."
}
]
}
6 changes: 4 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "fdeops",
"description": "Second brain for Forward Deployed Engineers. One @fde skill - enter at day one, mid-project, or mid-fire; it routes and does the phase work; engagement memory lands in .fde/ as you confirm judgment.",
"version": "3.11.1",
"description": "Engagement memory for AI coding agents. One @fde skill routes the client work - land, discover, plan, build, ship, close - and the record of it (sponsor, promise, decision, acceptance, dated) lands in local .fde/ files as you confirm judgment. For Forward Deployed Engineers running several clients at once.",
"version": "3.12.0",
"category": "productivity",
"tags": [
"community-managed"
Expand All @@ -14,6 +14,8 @@
"repository": "https://github.com/suboss87/fdeops",
"license": "MIT",
"keywords": [
"engagement-memory",
"agent-memory",
"fde",
"forward-deployed",
"legacy-code",
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 3.12.0 — 2026-08-27

Vocabulary: standard words on the outside, so nothing has to be learned before it works.

### Changed
- **The skill fires on client work, not on a password.** The frontmatter `description` - the only text a host reads before loading a skill - triggered on `@fde` plus a list of our own nouns ("hygiene", "receipts"), so an FDE who simply talked about their client got no memory. It now names the intents in the standard `Use when …` form: mentioning a client or sponsor, debriefing a meeting, asking what was agreed, prepping a readout, opening a session in a client workspace. `@fde` remains, as one trigger among several rather than the gate.
- **`fde tidy` is the verb; `fde garden` still works.** "Garden" was a metaphor only we used. Nothing is removed - the old name routes to the same code, and the alias is covered by a test.
- **`capture` and `preserve` left the human help.** Only hooks call them; listing them invited an FDE to run a snapshot by hand.
- **"Engagement memory for AI coding agents"** replaces "second brain" in the npm, plugin and marketplace descriptions - the phrase someone would actually search, and an accurate one.

### Added
- **One glossary.** The six words that carry the method (fieldbook, brief vs reality, terrain, trust signal, receipts, vault) are defined once in the README instead of being met scattered and guessed. A gate keeps it in place, and a second gate keeps the description triggering on intent.

## 3.11.1 — 2026-08-27

Adoption: one skill, and the CLI is never missing.
Expand Down
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)

Skill packs teach your AI coding agent how to *build*. None of them remember who the client is, what you promised, or who agreed it was delivered. FDEOps adds that layer: a private fieldbook per engagement (`.fde/`), a field methodology from land to close, and one `@fde` skill that routes it.
**Engagement memory for AI coding agents.** Skill packs teach your agent how to *build*. None of them remember who the client is, what you promised, or who agreed it was delivered - close the window and it is gone. FDEOps adds that missing layer: a private record per client (`.fde/`), a field methodology from land to close, and one skill that routes it.

Built for Forward Deployed Engineers and anyone embedded in client work - consultants, agency developers, solutions architects, fractional CTOs. Feels like a second brain; behaves like a defensible record: dated, sourced, on your laptop.
Built for Forward Deployed Engineers and anyone embedded in client work - consultants, agency developers, solutions architects, fractional CTOs. Not notes: a defensible record - dated, sourced, on your laptop.

```
land discover plan build ship close
Expand Down Expand Up @@ -58,9 +58,11 @@ npx fdeops resume # where we are
**3. Work** in plain language:

```text
@fde New client. Payments platform. They want it live before the Q3 audit.
New client, Garvey. Payments platform. They want it live before the Q3 audit.
```

No prefix needed - naming a client is enough. Say `@fde` when you want it explicitly.

**It is working if** `npx fdeops resume` prints this client's phase, trust signal, and next action - and prints the same thing tomorrow, from a new session, with no explaining. Full workflow: [docs/USAGE.md](docs/USAGE.md).

<details>
Expand All @@ -79,16 +81,16 @@ npx fdeops resume # where we are

## The week

`@fde` plus English. No cheat sheet.
**Say what happened, in your words.** There is nothing to memorise - no flags, no cheat sheet. The skill picks it up when you mention a client, debrief a meeting, or ask what was agreed; `@fde` is only the explicit way to summon it.

| When | What you say | What you get |
|------|--------------|--------------|
| **Start of week** | `@fde` - or just open Claude Code | Fieldbook on disk either way. **Claude Code** injects trust, phase, next before you type. **Cursor / Codex / others:** say `@fde` or `resume` - nothing auto-loads. |
| **After a meeting** | `@fde` debrief these notes *(paste or attach)* | Proposed updates. You review, then confirm. |
| **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/) |
| **Before a stakeholder meeting** | `@fde` prep me for tomorrow with the sponsor | Brief from what you already logged. |
| **Scope dispute** | `@fde` when did we agree to drop that? | Dated answers, or a clear gap. |
| **End of week** | `@fde` draft the sponsor update from the record | Status grounded in what happened. |
| **After a meeting** | here are my notes from the Acme call *(paste or attach)* | Proposed updates. You review, then confirm. |
| **Optional: pull** | connect Granola *(once)* · pull today's Acme transcript | You add that source MCP. We **pull** on request - no push, no sync. [mcp/recipes/](mcp/recipes/) |
| **Before a stakeholder meeting** | prep me for tomorrow with the sponsor | Brief from what you already logged. |
| **Scope dispute** | when did we agree to drop that? | Dated answers, or a clear gap. |
| **End of week** | draft the sponsor update from the record | Status grounded in what happened. |

Same folder every time: `~/fde-engagements/<client>/.fde/`.

Expand Down Expand Up @@ -149,6 +151,8 @@ One folder per client. Plain markdown, so you can grep it, diff it, copy it into

A day-one fieldbook ships **empty** - headings and allowed values, no invented rows - so anything you read in it is something that actually happened. Schema: [docs/schema.md](docs/schema.md).

**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.

---

## The field methods
Expand Down
27 changes: 27 additions & 0 deletions bin/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ if (!read('skills/fde/SKILL.md').includes('npx --yes fdeops')) {
fail('skills/fde/SKILL.md must fall back to npx --yes fdeops when the CLI is not installed')
} else ok('SKILL.md npx CLI fallback')

// The frontmatter description is the only text every host reads before deciding
// to load the skill. If it triggers on "@fde" alone, an FDE who just talks about
// their client gets no memory - so it must carry natural-intent triggers in the
// "Use when …" convention, and @fde must be one of several, never the gate.
{
const fm = /^---\n([\s\S]*?)\n---/.exec(read('skills/fde/SKILL.md'))
const desc = fm ? (/^description:[^\S\n]*(.*)$/m.exec(fm[1]) || [])[1] || '' : ''
const triggers = desc.match(/Use when/g) || []
if (triggers.length < 4) {
fail(`SKILL.md description needs several "Use when …" triggers so it fires on intent (found ${triggers.length})`)
} else if (/Use when the human says @fde or/.test(desc)) {
fail('SKILL.md description must not gate on @fde - name the client-work intents first')
} else ok('SKILL.md description triggers on intent')
}

// Six invented words (fieldbook, terrain, reality, trust signal, receipts,
// vault) carry the method. They were never defined in one place, so a stranger
// met them scattered through the docs and guessed.
{
const gloss = /Words used here[\s\S]{0,1200}/.exec(readme)
const missing = ['fieldbook', 'reality', 'terrain', 'trust signal', 'receipts', 'vault']
.filter(w => !gloss || !new RegExp(w, 'i').test(gloss[0]))
if (!gloss) fail('README must define its invented words once (a "Words used here" line)')
else if (missing.length) fail(`README glossary is missing: ${missing.join(', ')}`)
else ok('README defines its own vocabulary')
}

if (!fs.existsSync(path.join(root, 'docs', 'USAGE.md'))) {
fail('docs/USAGE.md missing')
} else ok('docs/USAGE.md')
Expand Down
31 changes: 16 additions & 15 deletions bin/fde.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* fde debrief --smart propose routing from messy notes; --apply commits it
* fde prep [label] grounded walk-in brief from existing .fde/ only
* fde doctor deterministic memory lint (stale signals, gaps)
* fde garden [--apply] propose safe consolidations; apply only with --apply
* fde tidy [--apply] propose safe consolidations; apply only with --apply (was: garden)
* fde ingest … stage → propose → apply pull sink (.inbox/; never auto-writes .fde/)
* fde owner [set …] who keeps this engagement record
* fde receipts <term> "what did we agree?" - search memory with dates
Expand Down Expand Up @@ -2488,18 +2488,18 @@ function cmdGarden(args) {
// Gardener contract (from Rowboat note_curation): no new facts, no deleted substance,
// reversible via git when healthy, confirm before apply. Mechanical only - no LLM rewrite.
if (gitHealth.ok) {
console.log('GARDEN (contract: no new facts · no deleted substance · reversible via memory git)')
console.log('TIDY (contract: no new facts · no deleted substance · reversible via memory git)')
} else if (gitHealth.reason === 'broken') {
console.log('GARDEN (contract: no new facts · no deleted substance · ⚠ memory git BROKEN — NOT reversible until ledger is repaired)')
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory git BROKEN — NOT reversible until ledger is repaired)')
} else {
console.log('GARDEN (contract: no new facts · no deleted substance · ⚠ memory not git-versioned — NOT reversible)')
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory not git-versioned — NOT reversible)')
}
console.log(resumeTriage(eng))
if (!gitHealth.ok) {
console.log(
gitHealth.reason === 'broken'
? '\n⚠ ledger is UNVERSIONED (corrupt .git). Repair before trusting garden apply: mv .fde/.git .fde/.git.broken && run any fde write to re-init.'
: '\n⚠ no memory git — garden apply cannot create a reversible commit until the ledger exists.'
? '\n⚠ ledger is UNVERSIONED (corrupt .git). Repair before trusting tidy apply: mv .fde/.git .fde/.git.broken && run any fde write to re-init.'
: '\n⚠ no memory git — tidy apply cannot create a reversible commit until the ledger exists.'
)
}
const proposals = []
Expand Down Expand Up @@ -2539,18 +2539,18 @@ function cmdGarden(args) {
})
}
if (!proposals.length) {
console.log('\nNothing to garden.')
console.log('\nNothing to tidy.')
return
}
console.log(`\n${proposals.length} proposal(s):`)
proposals.forEach((p, i) => console.log(` ${i + 1}. [${p.kind}] ${p.text}`))
if (!apply) {
console.log('\nApply mechanical items only: fde garden --apply')
console.log('\nApply mechanical items only: fde tidy --apply')
console.log('Manual items stay yours. Every apply commits to memory git when the ledger is healthy.')
return
}
if (!gitHealth.ok && gitHealth.reason === 'broken') {
console.error('refusing garden --apply while memory git is broken - repair the ledger first')
console.error('refusing tidy --apply while memory git is broken - repair the ledger first')
process.exit(1)
}
ensureMemoryGit(eng)
Expand Down Expand Up @@ -2603,9 +2603,9 @@ function cmdGarden(args) {
touched.add('context-archive.md')
console.log(`applied: archived ${p.sessionBlocks.length} old session-end blocks → context-archive.md`)
}
const hash = commitMemory(eng, 'garden', { files: [...touched] })
const hash = commitMemory(eng, 'tidy', { files: [...touched] })
if (!applied) console.log('no mechanical proposals applied (manual items remain)')
else console.log(`garden done${hash ? ` @${hash}` : ''}`)
else console.log(`tidy done${hash ? ` @${hash}` : ''}`)
}

// Keep the first open-risk bullet per fingerprint; move later echoes under ## Retired.
Expand Down Expand Up @@ -2647,7 +2647,7 @@ function applyRiskDedupe(eng, clusters) {
const stamp = new Date().toISOString().slice(0, 10)
const block = retiredLines.map(l => {
const body = l.trim().replace(/^-\s*/, '')
return `- [${stamp}] (garden dedupe) ${body}`
return `- [${stamp}] (tidy dedupe) ${body}`
}).join('\n')
out = appendUnderSection(out, 'Retired', block)
withFileLock(p, () => { atomicWriteFile(p, out.endsWith('\n') ? out : out + '\n') })
Expand Down Expand Up @@ -3150,14 +3150,13 @@ function printUsage() {
fde prep [label] grounded walk-in brief from existing .fde/ only
fde doctor lint engagement memory (stale signals, gaps)
fde redact <term> preview/remove lines containing a buried term (pass --apply to commit)
fde garden [--apply] propose safe consolidations (contract: no new facts; git-reversible)
fde tidy [--apply] propose safe consolidations (contract: no new facts; git-reversible)
fde owner [set email] who keeps this engagement record
fde receipts <term> "what did we agree?" with dates
fde capture session-end memory snapshot (hooks use this)
fde preserve pre-compaction context snapshot (hook-internal; hooks use this)
fde status [--all] current engagement status (pass --all for full portfolio)
fde dashboard [--all] current engagement fieldbook (pass --all for every client)
fde vault derived Obsidian vault of every engagement (--current for one, --redacted for a shared screen, --out <dir>)
hooks call these; you do not: capture (session-end snapshot), preserve (pre-compaction snapshot)
env FDEOPS_ENGAGEMENTS_ROOT override ~/fde-engagements (init/status/dashboard/registry)
writes require a workspace bind (or FDEOPS_ENGAGEMENT) - folder-name match is read-only
.fde/ is git-versioned locally for tamper-evident receipts (no remote, no telemetry)
Expand All @@ -3176,6 +3175,8 @@ switch (cmd) {
case 'prep': cmdPrep(args); break
case 'doctor': cmdDoctor(); break
case 'redact': cmdRedact(args); break
// `garden` was the name through 3.11.x; it keeps working.
case 'tidy':
case 'garden': cmdGarden(args); break
case 'owner': cmdOwner(args); break
case 'receipts': cmdReceipts(args); break
Expand Down
2 changes: 1 addition & 1 deletion bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ function cmdInstall(opts = {}) {
// through to the CLI (fde.js reads process.argv itself, so require() is enough).
const FDE_SUBCOMMANDS = [
'demo', 'scan', 'resume', 'triage', 'log', 'debrief', 'ingest', 'prep', 'doctor', 'redact',
'garden', 'owner', 'receipts', 'capture', 'preserve', 'status', 'dashboard', 'vault', 'help',
'tidy', 'garden', 'owner', 'receipts', 'capture', 'preserve', 'status', 'dashboard', 'vault', 'help',
]

const INSTALL_SUBCOMMANDS = ['init', 'adapters', 'install']
Expand Down
2 changes: 1 addition & 1 deletion docs/REPO_LAYOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
| `adapters/` | Thin per-tool pointers (Codex/`AGENTS.md`, Gemini, Cursor, Copilot, local LLMs) - `node bin/install.js adapters <dir>` |
| `templates/.fde/` | Core memory templates for `fde resume --init` (phase artifacts are created by phases on demand; `evals.md` is optional) |
| `examples/` | Fictional walkthroughs with sample `.fde/` files |
| `bin/fde.js` | Deterministic CLI - scan, resume, triage, log, debrief, ingest, prep, doctor, garden, redact, receipts, capture, preserve, status, dashboard |
| `bin/fde.js` | Deterministic CLI - scan, resume, triage, log, debrief, ingest, prep, doctor, tidy, redact, receipts, capture, preserve, status, dashboard, vault |
| `mcp/` | Optional MCP sink (`fdeops-ingest`) + source **recipes** (`mcp/recipes/` — file, granola, notion); source MCPs remain user-configured |
| `bin/lib/` | Shared memory / trust / render helpers used by the CLI |
| `bin/check.js` | Structural + install smoke gate (`npm run check`) |
Expand Down
2 changes: 1 addition & 1 deletion mcp/fdeops-ingest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fdeops-ingest-mcp",
"version": "3.11.1",
"version": "3.12.0",
"private": true,
"description": "Thin stdio MCP sink for FDEOps ingest (stage → propose → apply). Zero runtime dependencies.",
"bin": {
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "fdeops",
"version": "3.11.1",
"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.",
"version": "3.12.0",
"description": "Engagement memory for AI coding agents. Your agent forgets the client every morning - the sponsor, the promise, who signed off. FDEOps keeps that as dated markdown on your laptop: one @fde skill routing land-to-close methodology, a deterministic local CLI, and hooks that make it automatic. Claude Code plugin and any agent that loads skills.",
"bin": {
"fdeops": "bin/install.js",
"fde": "bin/fde.js"
Expand Down Expand Up @@ -30,6 +30,9 @@
"ai-agent",
"skills",
"plugin",
"engagement-memory",
"agent-memory",
"client-context",
"fde",
"forward-deployed",
"forward-deployed-engineer",
Expand Down
4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "fdeops",
"version": "3.11.1",
"description": "Engagement fieldbook for Forward Deployed Engineers: per-client memory in local .fde/ files, one @fde skill, land to close methodology. Local-only, no network.",
"version": "3.12.0",
"description": "Engagement memory for AI coding agents: per-client memory in local .fde/ files, one @fde skill, land to close methodology. Local-only, no network.",
"author": {
"name": "Subash Natarajan",
"url": "https://github.com/suboss87"
Expand Down
Loading
Loading