forgetrail 0.5.3 → 0.5.5

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.
Files changed (38) hide show
  1. package/CONTINUATION_PROMPT.md +3 -3
  2. package/INITIAL_PROMPT.md +7 -7
  3. package/README.md +2 -2
  4. package/WORKFLOW.md +12 -12
  5. package/content/AGENT_INTEGRATION_claude.md +1 -1
  6. package/content/AGENT_INTEGRATION_cursor.md +2 -2
  7. package/content/AGENT_INTEGRATION_generic.md +3 -3
  8. package/content/AGENT_INTEGRATION_grok.md +6 -6
  9. package/content/COMPANION_TOOLS.md +2 -2
  10. package/content/FORGETRAIL_LITE.md +12 -13
  11. package/content/FORGETRAIL_PROGRESS.md +1 -1
  12. package/content/GENESIS_SPEC_PROMPT.md +1 -1
  13. package/content/GENESIS_STUB.md +1 -1
  14. package/content/GREENFIELD_INTAKE.md +2 -2
  15. package/content/KICKOFF_WITHOUT_MCP.md +1 -1
  16. package/content/NEW_PROJECT_BOOTSTRAP.md +10 -10
  17. package/content/PLAN_MODE_PATTERNS.md +2 -2
  18. package/content/POCKETBASE_SCHEMA_SCRIPT.md +1 -1
  19. package/content/POST_BOOTSTRAP_USER_MESSAGE.md +2 -2
  20. package/content/SESSION_RESUME_MCP.md +1 -1
  21. package/content/companion-tools.json +3 -3
  22. package/content/cursor-rules/forgetrail-phase-status.mdc +3 -3
  23. package/content/cursor-rules/forgetrail-updates-log.mdc +1 -1
  24. package/content/examples/two-session-continuity.md +2 -2
  25. package/content/hooks/README.md +1 -1
  26. package/content/hooks/guard-shell.mjs +1 -1
  27. package/content/hooks/session-start.mjs +0 -1
  28. package/content/hooks/session-stop.mjs +76 -4
  29. package/content/scripts/forgetrail-dev-launcher.mjs +1 -1
  30. package/content/skills/forgetrail/SKILL.md +5 -6
  31. package/content/skills/forgetrail/SKILL_FACTS.md +1 -1
  32. package/docs/NPM.md +4 -4
  33. package/docs/PHASE_1_BRIEF.md +2 -2
  34. package/docs/SPEC_FEATURE_TEMPLATE.md +1 -1
  35. package/package.json +1 -1
  36. package/scripts/forgetrail-cli.mjs +8 -0
  37. package/scripts/install.mjs +1 -1
  38. package/scripts/tracking-cutover.test.mjs +67 -5
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { mkdtempSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
+ import { mkdtempSync, readFileSync, writeFileSync, mkdirSync, utimesSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -101,10 +101,10 @@ test("Lite and workflow instructions name the ledger", () => {
101
101
  assert.ok(legacyAt > 0 && resumeAt > legacyAt);
102
102
  const instructional = `${lite.slice(0, legacyAt)}\n${lite.slice(resumeAt)}`;
103
103
  assert.match(lite.slice(legacyAt, resumeAt), /"schemaVersion": "lite-1"/);
104
- assert.match(instructional, /ForgeTrail Lite v2\.2\.1/);
104
+ assert.match(instructional, /ForgeTrail Lite v2\.2\.2/);
105
105
  for (const line of instructional.split("\n")) {
106
106
  if (!line.includes("workflow_tracking.json")) continue;
107
- assert.match(line, /[Dd]o not|does not|not the live phase|not recreate/, line);
107
+ assert.match(line, /legacy/, line);
108
108
  }
109
109
  const workflow = readFileSync(join(root, "WORKFLOW.md"), "utf8");
110
110
  assert.match(workflow, /not_applicable/);
@@ -112,10 +112,72 @@ test("Lite and workflow instructions name the ledger", () => {
112
112
  assert.doesNotMatch(workflow, /Where this file names that JSON/);
113
113
  for (const line of workflow.split("\n")) {
114
114
  if (!line.includes("workflow_tracking.json")) continue;
115
- assert.match(line, /[Dd]o not|does not|not the system of record|not the live phase/, line);
115
+ assert.match(line, /legacy/, line);
116
116
  }
117
117
  });
118
118
 
119
+ test("fresh-project instructions do not name the tracking file", () => {
120
+ const files = [
121
+ "INITIAL_PROMPT.md",
122
+ "CONTINUATION_PROMPT.md",
123
+ "content/AGENT_INTEGRATION_claude.md",
124
+ "content/AGENT_INTEGRATION_cursor.md",
125
+ "content/AGENT_INTEGRATION_generic.md",
126
+ "content/AGENT_INTEGRATION_grok.md",
127
+ "content/COMPANION_TOOLS.md",
128
+ "content/FORGETRAIL_PROGRESS.md",
129
+ "content/GENESIS_SPEC_PROMPT.md",
130
+ "content/GENESIS_STUB.md",
131
+ "content/GREENFIELD_INTAKE.md",
132
+ "content/KICKOFF_WITHOUT_MCP.md",
133
+ "content/NEW_PROJECT_BOOTSTRAP.md",
134
+ "content/PLAN_MODE_PATTERNS.md",
135
+ "content/POCKETBASE_SCHEMA_SCRIPT.md",
136
+ "content/POST_BOOTSTRAP_USER_MESSAGE.md",
137
+ "content/SESSION_RESUME_MCP.md",
138
+ "content/companion-tools.json",
139
+ "content/cursor-rules/forgetrail-phase-status.mdc",
140
+ "content/skills/forgetrail/SKILL.md",
141
+ "docs/PHASE_1_BRIEF.md",
142
+ "docs/SPEC_FEATURE_TEMPLATE.md",
143
+ ];
144
+ for (const file of files) {
145
+ for (const line of readFileSync(join(root, file), "utf8").split("\n")) {
146
+ if (!line.includes("workflow_tracking.json")) continue;
147
+ assert.match(line, /legacy|exists|validateTracking/, `${file}: ${line}`);
148
+ }
149
+ }
150
+ });
151
+
152
+ function stopHook(dir, input) {
153
+ const result = spawnSync(process.execPath, [join(root, "content", "hooks", "session-stop.mjs")], {
154
+ cwd: dir,
155
+ input: JSON.stringify(input),
156
+ encoding: "utf8",
157
+ });
158
+ assert.equal(result.status, 0, result.stderr);
159
+ return JSON.parse(result.stdout);
160
+ }
161
+
162
+ test("session stop reminds only after work newer than the session record", () => {
163
+ const dir = mkdtempSync(join(tmpdir(), "ft-stop-"));
164
+ mkdirSync(join(dir, "appledger", "records", "session"), { recursive: true });
165
+ writeFileSync(join(dir, "appledger", "manifest.yaml"), "format: appledger\n");
166
+ const session = join(dir, "appledger", "records", "session", "session-1.md");
167
+ writeFileSync(session, "session\n");
168
+ spawnSync("git", ["init", "-q"], { cwd: dir });
169
+ const old = new Date(Date.now() - 60_000);
170
+ utimesSync(session, old, old);
171
+ assert.deepEqual(stopHook(dir, { status: "completed", loop_count: 0 }), {});
172
+ writeFileSync(join(dir, "app.ts"), "export {};\n");
173
+ const reminder = stopHook(dir, { status: "completed", loop_count: 0 });
174
+ assert.match(reminder.followup_message, /session record/);
175
+ assert.doesNotMatch(reminder.followup_message, /workflow_tracking/);
176
+ assert.deepEqual(stopHook(dir, { status: "completed", loop_count: 1 }), {});
177
+ writeFileSync(session, "session updated\n");
178
+ assert.deepEqual(stopHook(dir, { status: "completed", loop_count: 0 }), {});
179
+ });
180
+
119
181
  test("kickoff names appledger init and does not require the tracking file", () => {
120
182
  const kickoff = readFileSync(join(root, "mcp-server", "src", "index.ts"), "utf8");
121
183
  assert.match(kickoff, /appledger init/);
@@ -123,7 +185,7 @@ test("kickoff names appledger init and does not require the tracking file", () =
123
185
  const plan = readFileSync(join(root, "content", "PLAN_MODE_PATTERNS.md"), "utf8");
124
186
  assert.match(plan, /decision record/);
125
187
  assert.doesNotMatch(plan, /decisions\[\]/);
126
- assert.match(plan, /Do not create `\.forgetrail\/workflow_tracking\.json`/);
188
+ assert.doesNotMatch(plan, /workflow_tracking/);
127
189
  const genesis = readFileSync(join(root, "content", "GENESIS_STUB.md"), "utf8");
128
190
  assert.match(genesis, /appledger init/);
129
191
  assert.doesNotMatch(genesis, /Create `\.forgetrail\/workflow_tracking\.json`/);