forgetrail 0.5.2 → 0.5.4
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 +6 -5
- package/content/COMPANION_TOOLS.md +1 -1
- package/content/FORGETRAIL_LITE.md +4 -4
- package/content/companion-tools.json +2 -2
- package/content/cursor-rules/forgetrail-updates-log.mdc +1 -1
- package/content/hooks/guard-shell.mjs +1 -1
- package/content/skills/forgetrail/SKILL_FACTS.md +1 -1
- package/docs/NPM.md +7 -7
- package/package.json +1 -1
- package/scripts/forgetrail-cli.mjs +8 -0
- package/scripts/install.mjs +9 -1
- package/scripts/mcp-lib.mjs +38 -10
- package/scripts/mcp-status.mjs +7 -0
- package/scripts/tracking-cutover.test.mjs +26 -1
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
ForgeTrail gives your next coding session a place to start. It keeps the phase, decisions, and handoff in your repository so an agent can read them when you resume. Start with Lite for a small project. You do not have to run all seven phases to try the method.
|
|
14
14
|
|
|
15
|
-
ForgeTrail instructs the agent to read
|
|
15
|
+
ForgeTrail instructs the agent to read the ledger, preserve approved decisions, and pause at approval gates. The agent must write the handoff. Those updates are not automatic. Optional hooks enforce the checks documented for their supported host.
|
|
16
16
|
|
|
17
|
-
In session one, the agent drafts a Phase 1 brief, you approve it, and the agent logs the stack decision and a note for next time. In session two, a fresh chat reads that record, skips the settled questions, finishes the open Phase 1 item, and asks to move into Phase 2 with that phase's guidance. The record is
|
|
17
|
+
In session one, the agent drafts a Phase 1 brief, you approve it, and the agent logs the stack decision and a note for next time. In session two, a fresh chat reads that record, skips the settled questions, finishes the open Phase 1 item, and asks to move into Phase 2 with that phase's guidance. The record is the [AppLedger](https://appledger.dev) ledger in `appledger/`: the phase in `profiles/forgetrail.yaml`, plus decision, lesson, and session records. `pnpm dlx appledger init` creates it. Labeled walk-through: [`content/examples/two-session-continuity.md`](content/examples/two-session-continuity.md).
|
|
18
18
|
|
|
19
19
|
**Docs:** [forgetrail.dev/docs](https://forgetrail.dev/docs) · **Site:** [forgetrail.dev](https://forgetrail.dev)
|
|
20
20
|
|
|
@@ -38,9 +38,10 @@ The shortest supported first task is: create `appledger/`, draft `docs/PHASE_1_B
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
pnpm dlx forgetrail install --lite --with-genesis-stub
|
|
41
|
+
pnpm dlx appledger init --name "Your app name"
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
MCP: `npx -y forgetrail-mcp`
|
|
44
|
+
MCP: `npx -y forgetrail-mcp` (0.4.2 or later finds the methodology without `FORGETRAIL_ROOT`). Prefer `pnpm dlx` on Windows. Do not add `forgetrail` to an app's `dependencies`. Do not merge the two packages.
|
|
44
45
|
|
|
45
46
|
## What you get
|
|
46
47
|
|
|
@@ -53,8 +54,8 @@ Optional hooks in [`content/hooks/`](content/hooks/README.md) load the current p
|
|
|
53
54
|
## xFacts label
|
|
54
55
|
|
|
55
56
|
- **AppFacts:** [viewer](https://appfacts.dev/v#af1.eNp1Uk1r3DAQ_StiTg1o10mOPrUYcihJCCS3UsqsNNaqK2uENHbiLvvfi2x320tvg3hf8zRnmKC90xBxIGjhgbOjt4w-gAaZU30riYxqlDAHHx1oKIIyFmgBjfiJQEPwhmKp4C8JzZF29_vbFWhO0J4hYHQjugr4ihO-muyTaPU2J1pn0JDHKH4J8cyW9j9LTbB5tvDEloLqOAp9iHrJLGw4qE9P3csNaDhykRXYBR5tHzATXDRYSgXab2eI0MLnoYqYVSNtEk2xJ9CQ_u9RKE-UlR9SoIGioHiOcNGr6C-2G_3VHGlANWHwdsEojFbVEpWPPWWKhq6094zRBcob929qZSkFnqvRUvmV4Uh6HyhlKmVjPfhAuwMWsmrZKooaMKJbYsLlu4Yymev6_zSjIUP7pzSVMk_eUlY9Z_VOh-KFFvZh9MHW_0toTujox6peuSmmoRacKXHxwnmugiKptE3jvBzHw97w0HQoGOYiu-Wydo-PXdPXSZYbu_wGeaPbgQ) · [raw](https://github.com/Catalyst-Forge-LLC/forgetrail/blob/main/APP_FACTS.md)
|
|
56
|
-
- **ToolFacts:** [viewer](https://toolfacts.dev/v#tf1.
|
|
57
|
-
- **SkillFacts:** [viewer](https://skillfacts.dev/v#sf1.
|
|
57
|
+
- **ToolFacts:** [viewer](https://toolfacts.dev/v#tf1.eNrFl01vGzcQhv_KgKekWCmOW_Rjb66bBEGc2HBc5BAEAU3O7jLictjhrFTV8H8vZndlq2qaSw-6CSuS8z6cT96ZtamfVybZHk1tXhK3eMM2RHh7fgXvkdfIpjIe1xgpI5vanFuxcVsExsWmMmvkEiiZ2pwsf1h-bypTxMpQTG2sk7DWNTE4TEVNnGXrOlycLk9MZVYheVOb3uVF2dniIUlQNXcG_0Q3yHR2JGfjIjM5LMVURtimkonF1KaID2TuK-MYPSYJNhbdz_jHEBi9qT9-uq8Mtqx76zsjGLFH4a2pTaKEI2KRkKxaK_N6IdJzPt7trmdtY_BW9IbcKqRWUYPHz9g06ESBGa1XBrSuU0NNiFi2RbBXmY4y6t8JZUO8ejQ-Y2ExdWNjwfvKBI99JsEkphYesDJ54EzjHZ5HW0potmBBz2kibT7LrGn5pVACtbuEM8gUkiBDKGCdwyzo9fOGg9jbiODJDT0mGReAo9TE4GRp7qsH6PxV0Fn410HnP_83JqWEGkFBtuA6dKsaGGXgVIBWFWTrVrZFmAOwgpeX169e3Fyfvb74fH15eVOBTR42HUqHDB8ur9-8vLj8sOw9bGyBhobk90FblKvOFnw1BG-Tw-NAX4-A0KN05ClSu4V2FgQNMViYk3F0W1bB8OT54qen-ygFLbvuAkvRaD4Kx_tRAkiHsFdV4igJvBV7q8pvt7DC7Yb40BM32Odo5bhOsPvSH1JFZmnQMPX6uTz7btn_A4CHdDb4IEdWX4QHJwOjB6tyIDP1WcY4Use4gXmMIqYv6ASeeMICiQSKs2lcokJKtg4hSMHYPD3w07mmZQzl2Kg7BLfTMydLr0aFEsLM3AwxPq46jLq5iL53Hfb2qEiqdYZaMDpiD4wNMiaHS7jpECK21m1hV_ihdDajFnIl70PLYzOrRn9aSLiZusIB8lmScGVFkI9VKWbgXX5psCYJizyLmtJMr2OvJh7Wbaaxt4c1_kZOveeHeNzasemC6xTqoV4UEALHqKUjJFANU_0-gHmHm6vJ8W-CW1HTHJWjZcTUBIwebomkCNsM2W4jWT_lWM4RfYv8DEIKOnuFv8bIG7svZf1pI5wPXIiBh4hlH3g1Mb56MHOkdmV7BFvga_cPmyAdhOTi4HHiuB4igp4xQiaCbNn2KMjfhntH0_7jQv5L1khIg4xptucpKDp_UfrPCP11FxJHjdG351eLJnARCGnqeTrFa7oVsazVby-Kd51ih-wob7V87rX6kITGq2DMdMD-uOwiHHk8GWerx5H34vXNCx1u9VE0Dvezm5VYyFH8JsnvWV825ehAqgWGSczkPuSxzkRqW_XTjgZam4u5_1SZFhOyFX3n3RndaGpzenL64-Lkl8Xpz-ZhAenztbPJL-wgHelY9GR840FIa0xCvJ2aTaM3I3ozi95lGJ-1OvgY4SG5yZIi3P8NruJhFA) · [raw](https://github.com/Catalyst-Forge-LLC/forgetrail/blob/main/mcp-server/TOOL_FACTS.md)
|
|
58
|
+
- **SkillFacts:** [viewer](https://skillfacts.dev/v#sf1.eNqdUsFu2zAM_RWDZzvutsMA3YpsHYYFa7HkVhSFItM2F1kSSMpdUOTfB9kb1gK7bDeBenzv8ZHPMIN5U0OwE4KBPvKAypY81NDhjD4mZDCwtWr9WbS6KQioYUYWigEMXG3eba6gBlGrWcCAdUpzwXhyGKTwXifrRmzeLsAThQ4MuMwSuZET-aKWMqe4gD-GPrLDSkdc5Q7FUPW-SaMVrDz16M7OY2VDV02WgloKlU3JYzcgt5WVpVfOojhVsa8YXeSuiHCcMdjgEMwzSMxcXjCqJjFtO5CO-bhxcWp_D9wsDprdbtu-CifloycZ_xbOpQYKopydUgzyyGjduOiN6D0YwB_JkyOFGgLqU-QTGKApecJisiePq3cwwGi75olJF16N0RfCHhmDww7M_T-bb48-HtuSWzu51AjyjNwebm93jzfX28N-MxUTA-pdiftTpm7Jaylt45RsoBj2eRhQlvmgBs7hOnfLQIKW3bhDkfVrtp46q2WH7kRhKJC1d5-PdsCgH9DFKUWhwrbKHHBK3uov0a_4dMfxOzr9Qu4U-34tf0PJE-5Ryh1-fhE4PNRwzKHz2D1aVuqtUwFz_1ADDowiZReKHidUPoOBEAMu9y5Kwa4c5v7hUsMYJ0x2eHkjf4LcdDiX4XExHxeq_7gk5Ryc1bJM5YyXn2iPRHc) · [raw](https://github.com/Catalyst-Forge-LLC/forgetrail/blob/main/content/skills/forgetrail/SKILL_FACTS.md)
|
|
58
59
|
|
|
59
60
|
## Development
|
|
60
61
|
|
|
@@ -17,7 +17,7 @@ Public shelf (current versions and install): [catalystforge.com/tools](https://c
|
|
|
17
17
|
3. FilePress publishes **Markdown**. It does not replace an interactive SvelteKit app.
|
|
18
18
|
4. LocalSlip and LocalHelm are for **two or more** local apps, not a first single project. If they accept LocalSlip, use the recipes below. Do not write `ensure-lease.mjs`.
|
|
19
19
|
5. Practice skills: install the one that matches the current job, not the set.
|
|
20
|
-
6.
|
|
20
|
+
6. Offer an xFacts label when a product ships (public URL or handoff). Generate one only if the user accepts. Record the subject disposition in `appledger/` either way.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ForgeTrail Lite — portable kickoff for any agentic chat
|
|
2
2
|
|
|
3
|
-
> **ForgeTrail Lite v2.2.
|
|
3
|
+
> **ForgeTrail Lite v2.2.1**
|
|
4
4
|
> © Catalyst Forge, LLC — [www.catalystforge.com](https://www.catalystforge.com)
|
|
5
5
|
> Part of the **ForgeTrail** open-source methodology ([Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) in the upstream ForgeTrail repo).
|
|
6
6
|
>
|
|
@@ -320,7 +320,7 @@ Record anything non-obvious as a lesson in **`appledger/`** (for example, Playwr
|
|
|
320
320
|
2. **`.forgetrail/cursor/rules/forgetrail-updates-log.mdc`** — use the §12.5 snippet verbatim. **Symlink or copy** into `.cursor/rules/` so Cursor reminds agents when to update `FORGETRAIL_LITE_UPDATES.md` (§1.6). Optional: copy the upstream **`FORGETRAIL_LITE_UPDATES.md`** starter into `.forgetrail/` when you expect protocol feedback during the project.
|
|
321
321
|
3. **`.forgetrail/hooks/` and `.cursor/hooks.json`** — install host safety hooks (`content/hooks/`) into `.forgetrail/hooks/` and copy `cursor-hooks.json` to `.cursor/hooks.json`. On Cursor, this runs pre-commit verification, enforces pnpm lock consistency, gates git push at the tool level, and injects live phase context at session start. For Claude Code, add `claude-settings-hooks.json` into `.claude/settings.json`.
|
|
322
322
|
If any of these already exists and its content conflicts with the Lite defaults, **do not overwrite** — flag the conflict to the user and ask how to reconcile. Record the reconciliation as a decision in `appledger/`.
|
|
323
|
-
4. **Create `appledger/`** if it does not exist
|
|
323
|
+
4. **Create `appledger/`** if it does not exist. Run `pnpm dlx appledger init --name "<application name>"` (or `appledger init` when it is installed globally), then `pnpm dlx appledger check`. Init writes `manifest.yaml`, `profiles/forgetrail.yaml` with the `plan` phase `in_progress`, an application record, and a session record. It does not overwrite existing files. Init leaves the purpose as "Not supplied." Write the one-line purpose into the application record from what the user has already said, then run `check` again. Do **not** create `.forgetrail/workflow_tracking.json`. Criteria stay `pending` until evidence exists. **Without Node:** write the same four files by hand in the shape of the [minimal example ledger](https://github.com/Catalyst-Forge-LLC/appledger/tree/main/examples/minimal/appledger), and run `appledger check` once Node is available. A hand-written ledger is not checked until then.
|
|
324
324
|
5. **If git was initialized in step 2**, make the first commit now so the user has a clean baseline. **What lands in the commit depends on §1.5:** if **committing `.forgetrail/`**, steps 3–4 artifacts are included; if **gitignoring `.forgetrail/`**, only `.gitignore` (and any `.cursor/rules/` copies) — the workspace stays local-only and that is expected, not a mistake. Example: `git add -A && git commit -m "chore: ForgeTrail Lite bootstrap"`. Skip this step if the repo already had history — do not squash or amend what's there. Skip entirely if the user is in no-git mode (§4.1).
|
|
325
325
|
6. **Ask the §5 intake questions.** Do not write any project code yet. For the first user-facing reply, follow §9 (plain product language, one clear "reply with," no methodology jargon).
|
|
326
326
|
7. **Create `docs/`** (if missing) and **draft `docs/PHASE_1_BRIEF.md`** from the §6 template using the user's answers. Show it to the user, iterate, then **lock it**: mark the brief criteria met in `appledger/profiles/forgetrail.yaml` only when the user has approved them, and record major commitments as decision records.
|
|
@@ -1003,7 +1003,7 @@ Save this as `.forgetrail/AGENTS.md` so agents that auto-load it (Codex, Cursor,
|
|
|
1003
1003
|
|
|
1004
1004
|
```markdown
|
|
1005
1005
|
<!--
|
|
1006
|
-
Agent protocol based on ForgeTrail Lite v2.2.
|
|
1006
|
+
Agent protocol based on ForgeTrail Lite v2.2.1.
|
|
1007
1007
|
© Catalyst Forge, LLC — www.catalystforge.com
|
|
1008
1008
|
Licensed under Apache License 2.0 (upstream ForgeTrail repo).
|
|
1009
1009
|
-->
|
|
@@ -1179,4 +1179,4 @@ ForgeTrail Lite covers the shape of a project. The full **ForgeTrail MCP server*
|
|
|
1179
1179
|
|
|
1180
1180
|
---
|
|
1181
1181
|
|
|
1182
|
-
**ForgeTrail Lite v2.2.
|
|
1182
|
+
**ForgeTrail Lite v2.2.1** · © Catalyst Forge, LLC · [www.catalystforge.com](https://www.catalystforge.com) · [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
{
|
|
191
191
|
"name": "Gap Last",
|
|
192
192
|
"homepage": "https://gaplast.dev",
|
|
193
|
-
"offer": "Gap Last separates observations, constraints, and remaining questions, then proposes hypotheses. A hypothesis is not the established cause.
|
|
193
|
+
"offer": "Gap Last separates observations, constraints, and remaining questions, then proposes hypotheses. A hypothesis is not the established cause. Record leftovers as open question records in appledger/."
|
|
194
194
|
}
|
|
195
195
|
]
|
|
196
196
|
},
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
{
|
|
204
204
|
"name": "xFacts",
|
|
205
205
|
"homepage": "https://xfacts.dev",
|
|
206
|
-
"offer": "xFacts
|
|
206
|
+
"offer": "When the product is public or handed off, offer an xFacts label (AppFacts and neighbors). Record the subject disposition either way, and bind an existing label with appledger bind. A validator checks shape, not truth."
|
|
207
207
|
}
|
|
208
208
|
]
|
|
209
209
|
},
|
|
@@ -16,7 +16,7 @@ After substantive work, if you find something that **should be fixed or clarifie
|
|
|
16
16
|
- Errors in the Lite defaults for **pnpm + TypeScript + monorepos** or **Cursor/agent** workflows.
|
|
17
17
|
- Doc structure suggestions (which § to patch).
|
|
18
18
|
|
|
19
|
-
**Do not** log routine bug fixes or product-only notes;
|
|
19
|
+
**Do not** log routine bug fixes or product-only notes; record a session-local incident as a lesson or open question record in `appledger/`. **Do** log items a **future agent bootstrapping any ForgeTrail Lite project** would benefit from if the upstream file were updated.
|
|
20
20
|
|
|
21
21
|
## How to write an entry
|
|
22
22
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* ForgeTrail Shell Guard
|
|
5
5
|
* Hook event: beforeShellExecution (Cursor) / PreToolUse (Claude Code)
|
|
6
|
-
* Enforces Lite §0,
|
|
6
|
+
* Enforces Lite §0, verify-before-commit, pnpm consistency, and git push guards.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { readFileSync, existsSync } from "node:fs";
|
package/docs/NPM.md
CHANGED
|
@@ -10,12 +10,12 @@ Two public unscoped packages:
|
|
|
10
10
|
|
|
11
11
|
| Package | This tree | On npm until you publish | From | What it is |
|
|
12
12
|
| --- | --- | --- | --- | --- |
|
|
13
|
-
| [`forgetrail`](https://www.npmjs.com/package/forgetrail) | 0.5.
|
|
14
|
-
| [`forgetrail-mcp`](https://www.npmjs.com/package/forgetrail-mcp) | 0.4.
|
|
13
|
+
| [`forgetrail`](https://www.npmjs.com/package/forgetrail) | 0.5.3 | 0.5.3 | repo root | CLI + Lite + templates. `pnpm dlx forgetrail install --lite` |
|
|
14
|
+
| [`forgetrail-mcp`](https://www.npmjs.com/package/forgetrail-mcp) | 0.4.3 | 0.4.2 | `mcp-server/` | MCP bin. Depends on `forgetrail` for content. `pnpm dlx forgetrail-mcp` |
|
|
15
15
|
|
|
16
16
|
These are **installer and MCP channels**, not a library. App repos should not add `forgetrail` to `dependencies`.
|
|
17
17
|
|
|
18
|
-
`forgetrail@0.5.
|
|
18
|
+
`forgetrail@0.5.3` and `forgetrail-mcp@0.4.2` are on npm. This tree adds `forgetrail --version`, forgetrail.dev `llms.txt`, and companion text that names `appledger/`, so `forgetrail` needs a version bump before its next publish. `forgetrail-mcp@0.4.3` changes the startup banner and the `ingestPlanArtifact` description, and is not published yet. `getCompanionSuggestions` reads its text from the installed `forgetrail`, so the new companion text reaches MCP users only after `forgetrail` is published. `pnpm run pack:check` is the gate. It does not publish.
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -59,11 +59,11 @@ The `Debugger listening` line on MCP `prepack` is Cursor attaching to a one-line
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
# Prefer pnpm dlx. npm 12 `npx forgetrail --help` on Windows failed to spawn the bin.
|
|
62
|
-
pnpm dlx forgetrail
|
|
63
|
-
pnpm dlx forgetrail
|
|
62
|
+
pnpm dlx forgetrail@<version> --help
|
|
63
|
+
pnpm dlx forgetrail@<version> install --lite --dry-run
|
|
64
64
|
|
|
65
|
-
# MCP starts
|
|
66
|
-
pnpm dlx forgetrail-mcp@0.4.
|
|
65
|
+
# MCP starts and reads content from its forgetrail dependency. ping should not say "content missing".
|
|
66
|
+
pnpm dlx forgetrail-mcp@0.4.3
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
Optional Cursor MCP (content from a `forgetrail` install, or set the path):
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { cwd } from "node:process";
|
|
4
5
|
import {
|
|
5
6
|
INSTALL_FULL_HELP,
|
|
@@ -39,6 +40,7 @@ Install options:
|
|
|
39
40
|
--skip-tracking Accepted and ignored. A tracking JSON file is not installed.
|
|
40
41
|
--path, -p <dir> Install elsewhere (default: current directory)
|
|
41
42
|
--help, -h Show help
|
|
43
|
+
--version, -v Print the forgetrail version
|
|
42
44
|
|
|
43
45
|
First-time prove-it (no MCP): see TRY_FORGETRAIL.md in the ForgeTrail repo.
|
|
44
46
|
Run \`forgetrail mcp --help\` for all MCP subcommands.
|
|
@@ -54,6 +56,12 @@ function main() {
|
|
|
54
56
|
|
|
55
57
|
const cmd = argv[0];
|
|
56
58
|
|
|
59
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
60
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
61
|
+
console.log(pkg.version);
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
if (cmd === "mcp") {
|
|
58
66
|
runMcpCommand(argv.slice(1));
|
|
59
67
|
return;
|
package/scripts/install.mjs
CHANGED
|
@@ -88,6 +88,7 @@ export function runInstallForgetrail(rawArgv, { defaultToCwd = false } = {}) {
|
|
|
88
88
|
console.log(" First chat: _forgetrail/INITIAL_PROMPT.md");
|
|
89
89
|
console.log(" Project record: appledger/ (not workflow_tracking.json)");
|
|
90
90
|
console.log(" Hooks: .forgetrail/hooks/ (enforced via .cursor/hooks.json)");
|
|
91
|
+
printLedgerNext();
|
|
91
92
|
return { target, mode: "full" };
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -145,11 +146,18 @@ export function runInstallLite(rawArgv, { defaultToCwd = false } = {}) {
|
|
|
145
146
|
console.log(" Hooks: .forgetrail/hooks/ (enforced via .cursor/hooks.json)");
|
|
146
147
|
if (args.withGenesisStub) {
|
|
147
148
|
console.log(" Genesis: docs/GENESIS.md (stub — replace with your spec)");
|
|
148
|
-
console.log(" Next: see TRY_FORGETRAIL.md in the ForgeTrail repo");
|
|
149
149
|
}
|
|
150
|
+
printLedgerNext();
|
|
150
151
|
return { target, mode: "lite", withGenesisStub: !!args.withGenesisStub };
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
function printLedgerNext() {
|
|
155
|
+
console.log("\nNext, create the ledger in the app folder (the agent can run these):");
|
|
156
|
+
console.log(' pnpm dlx appledger init --name "Your app name"');
|
|
157
|
+
console.log(" pnpm dlx appledger check");
|
|
158
|
+
console.log("Then paste the kickoff line from https://forgetrail.dev/docs/try");
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
export const INSTALL_FULL_HELP = `
|
|
154
162
|
ForgeTrail template-in-repo install (offline / no MCP server required)
|
|
155
163
|
|
package/scripts/mcp-lib.mjs
CHANGED
|
@@ -125,21 +125,36 @@ export function posixPath(p) {
|
|
|
125
125
|
return p.replace(/\\/g, "/");
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/** True when this CLI runs from a clone that has mcp-server/ (the npm package does not ship it). */
|
|
129
|
+
export function isClone() {
|
|
130
|
+
return existsSync(join(MCP_SERVER_DIR, "package.json"));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** pnpm dlx and npx unpack into a cache that can be removed, so its path is not a stable FORGETRAIL_ROOT. */
|
|
134
|
+
export function isTemporaryInstall(p = FORGETRAIL_ROOT) {
|
|
135
|
+
return /[\\/](dlx|_npx)[\\/]/i.test(p);
|
|
136
|
+
}
|
|
137
|
+
|
|
128
138
|
export function mcpClientConfigObject() {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
if (existsSync(MCP_ENTRY)) {
|
|
140
|
+
return {
|
|
141
|
+
mcpServers: {
|
|
142
|
+
forgetrail: {
|
|
143
|
+
command: "node",
|
|
144
|
+
args: [posixPath(MCP_ENTRY)],
|
|
145
|
+
env: { FORGETRAIL_ROOT: posixPath(FORGETRAIL_ROOT) },
|
|
146
|
+
},
|
|
135
147
|
},
|
|
136
|
-
}
|
|
137
|
-
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const server = { command: "npx", args: ["-y", "forgetrail-mcp"] };
|
|
151
|
+
if (!isTemporaryInstall()) server.env = { FORGETRAIL_ROOT: posixPath(FORGETRAIL_ROOT) };
|
|
152
|
+
return { mcpServers: { forgetrail: server } };
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
/** Copy-paste MCP client setup — shown after `forgetrail mcp build`. */
|
|
141
156
|
export function printMcpClientSetupBanner({ showBuildComplete = true } = {}) {
|
|
142
|
-
const
|
|
157
|
+
const fromClone = existsSync(MCP_ENTRY);
|
|
143
158
|
|
|
144
159
|
if (showBuildComplete) {
|
|
145
160
|
console.log("\n✓ Build complete.\n");
|
|
@@ -149,8 +164,21 @@ export function printMcpClientSetupBanner({ showBuildComplete = true } = {}) {
|
|
|
149
164
|
console.log(" Cursor — project .cursor/mcp.json or Settings → MCP → add server:");
|
|
150
165
|
console.log(JSON.stringify(mcpClientConfigObject(), null, 2));
|
|
151
166
|
console.log("");
|
|
167
|
+
if (!fromClone) {
|
|
168
|
+
if (isTemporaryInstall()) {
|
|
169
|
+
console.log(" This forgetrail copy is in a temporary pnpm dlx or npx cache, so FORGETRAIL_ROOT is not set.");
|
|
170
|
+
console.log(" forgetrail-mcp finds the forgetrail package it depends on. For a fixed path: pnpm add -g forgetrail, then rerun this command.");
|
|
171
|
+
} else {
|
|
172
|
+
console.log(` FORGETRAIL_ROOT is this installed forgetrail package: ${posixPath(FORGETRAIL_ROOT)}`);
|
|
173
|
+
}
|
|
174
|
+
console.log("");
|
|
175
|
+
}
|
|
152
176
|
console.log(" Claude Desktop — %APPDATA%\\Claude\\claude_desktop_config.json (same mcpServers block)");
|
|
153
|
-
console.log(
|
|
177
|
+
console.log(
|
|
178
|
+
fromClone
|
|
179
|
+
? ` Claude Code — claude mcp add forgetrail node ${posixPath(MCP_ENTRY)}`
|
|
180
|
+
: " Claude Code — claude mcp add forgetrail -- npx -y forgetrail-mcp"
|
|
181
|
+
);
|
|
154
182
|
console.log("");
|
|
155
183
|
console.log(" Then reload MCP in Cursor (Settings → MCP) or restart Cursor.");
|
|
156
184
|
console.log(" Verify: forgetrail mcp ping | Reprint: forgetrail mcp cursor-config");
|
package/scripts/mcp-status.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
MCP_SERVER_DIR,
|
|
9
9
|
analyzeCursorMcpConfig,
|
|
10
10
|
findCursorMcpJson,
|
|
11
|
+
isClone,
|
|
11
12
|
staticMcpChecks,
|
|
12
13
|
printCursorConfigTemplate,
|
|
13
14
|
runMcpBuild,
|
|
@@ -121,6 +122,12 @@ export function runMcpCommand(argv) {
|
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
if (cmd !== "cursor-config" && !isClone()) {
|
|
126
|
+
console.error(`forgetrail mcp ${cmd} runs from a ForgeTrail clone. This npm install does not include mcp-server/.`);
|
|
127
|
+
console.error("Use the published server instead: forgetrail mcp cursor-config prints a config that starts forgetrail-mcp.");
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
124
131
|
switch (cmd) {
|
|
125
132
|
case "build":
|
|
126
133
|
runMcpBuild();
|
|
@@ -44,9 +44,34 @@ test("install does not create workflow_tracking.json", () => {
|
|
|
44
44
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
45
45
|
assert.doesNotMatch(result.stdout, /\\workflow_tracking\.json/);
|
|
46
46
|
assert.match(result.stdout, /Project record: appledger/);
|
|
47
|
+
assert.match(result.stdout, /pnpm dlx appledger init --name/);
|
|
48
|
+
assert.match(result.stdout, /pnpm dlx appledger check/);
|
|
47
49
|
}
|
|
48
50
|
});
|
|
49
51
|
|
|
52
|
+
test("Lite step 4 runs appledger init and check", () => {
|
|
53
|
+
const lite = readFileSync(join(root, "content", "FORGETRAIL_LITE.md"), "utf8");
|
|
54
|
+
const step = lite.split("\n").find((line) => line.startsWith("4. **Create `appledger/`**"));
|
|
55
|
+
assert.ok(step);
|
|
56
|
+
assert.match(step, /pnpm dlx appledger init --name/);
|
|
57
|
+
assert.match(step, /appledger check/);
|
|
58
|
+
assert.match(step, /examples\/minimal\/appledger/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("cursor-config from an npm install starts forgetrail-mcp, not a missing dist file", async () => {
|
|
62
|
+
const { mcpClientConfigObject, isTemporaryInstall } = await import("./mcp-lib.mjs");
|
|
63
|
+
const server = mcpClientConfigObject().mcpServers.forgetrail;
|
|
64
|
+
const hasDist = spawnSync(process.execPath, ["-e", "process.exit(require('node:fs').existsSync(process.argv[1]) ? 0 : 1)", join(root, "mcp-server", "dist", "index.js")]).status === 0;
|
|
65
|
+
if (hasDist) {
|
|
66
|
+
assert.equal(server.command, "node");
|
|
67
|
+
} else {
|
|
68
|
+
assert.deepEqual([server.command, ...server.args], ["npx", "-y", "forgetrail-mcp"]);
|
|
69
|
+
}
|
|
70
|
+
assert.equal(isTemporaryInstall("C:/Users/u/AppData/Local/pnpm-cache/dlx/abc/node_modules/forgetrail"), true);
|
|
71
|
+
assert.equal(isTemporaryInstall("/home/u/.npm/_npx/abc/node_modules/forgetrail"), true);
|
|
72
|
+
assert.equal(isTemporaryInstall("C:/Users/u/AppData/Local/pnpm/global/5/node_modules/forgetrail"), false);
|
|
73
|
+
});
|
|
74
|
+
|
|
50
75
|
test("session start warns on a legacy file and does not quote it as the live phase", () => {
|
|
51
76
|
const dir = mkdtempSync(join(tmpdir(), "ft-session-"));
|
|
52
77
|
mkdirSync(join(dir, ".forgetrail"), { recursive: true });
|
|
@@ -76,7 +101,7 @@ test("Lite and workflow instructions name the ledger", () => {
|
|
|
76
101
|
assert.ok(legacyAt > 0 && resumeAt > legacyAt);
|
|
77
102
|
const instructional = `${lite.slice(0, legacyAt)}\n${lite.slice(resumeAt)}`;
|
|
78
103
|
assert.match(lite.slice(legacyAt, resumeAt), /"schemaVersion": "lite-1"/);
|
|
79
|
-
assert.match(instructional, /ForgeTrail Lite v2\.2\.
|
|
104
|
+
assert.match(instructional, /ForgeTrail Lite v2\.2\.1/);
|
|
80
105
|
for (const line of instructional.split("\n")) {
|
|
81
106
|
if (!line.includes("workflow_tracking.json")) continue;
|
|
82
107
|
assert.match(line, /[Dd]o not|does not|not the live phase|not recreate/, line);
|