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.
- package/CONTINUATION_PROMPT.md +3 -3
- package/INITIAL_PROMPT.md +7 -7
- package/README.md +2 -2
- package/WORKFLOW.md +12 -12
- package/content/AGENT_INTEGRATION_claude.md +1 -1
- package/content/AGENT_INTEGRATION_cursor.md +2 -2
- package/content/AGENT_INTEGRATION_generic.md +3 -3
- package/content/AGENT_INTEGRATION_grok.md +6 -6
- package/content/COMPANION_TOOLS.md +2 -2
- package/content/FORGETRAIL_LITE.md +12 -13
- package/content/FORGETRAIL_PROGRESS.md +1 -1
- package/content/GENESIS_SPEC_PROMPT.md +1 -1
- package/content/GENESIS_STUB.md +1 -1
- package/content/GREENFIELD_INTAKE.md +2 -2
- package/content/KICKOFF_WITHOUT_MCP.md +1 -1
- package/content/NEW_PROJECT_BOOTSTRAP.md +10 -10
- package/content/PLAN_MODE_PATTERNS.md +2 -2
- package/content/POCKETBASE_SCHEMA_SCRIPT.md +1 -1
- package/content/POST_BOOTSTRAP_USER_MESSAGE.md +2 -2
- package/content/SESSION_RESUME_MCP.md +1 -1
- package/content/companion-tools.json +3 -3
- package/content/cursor-rules/forgetrail-phase-status.mdc +3 -3
- package/content/cursor-rules/forgetrail-updates-log.mdc +1 -1
- package/content/examples/two-session-continuity.md +2 -2
- package/content/hooks/README.md +1 -1
- package/content/hooks/guard-shell.mjs +1 -1
- package/content/hooks/session-start.mjs +0 -1
- package/content/hooks/session-stop.mjs +76 -4
- package/content/scripts/forgetrail-dev-launcher.mjs +1 -1
- package/content/skills/forgetrail/SKILL.md +5 -6
- package/content/skills/forgetrail/SKILL_FACTS.md +1 -1
- package/docs/NPM.md +4 -4
- package/docs/PHASE_1_BRIEF.md +2 -2
- package/docs/SPEC_FEATURE_TEMPLATE.md +1 -1
- package/package.json +1 -1
- package/scripts/forgetrail-cli.mjs +8 -0
- package/scripts/install.mjs +1 -1
- 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\.
|
|
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, /
|
|
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, /
|
|
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.
|
|
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`/);
|