forgetrail 0.4.13 → 0.4.15
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/INITIAL_PROMPT.md +1 -1
- package/README.md +1 -1
- package/TRY_FORGETRAIL.md +44 -5
- package/WORKFLOW.md +15 -2
- package/content/AGENT_INTEGRATION_claude.md +3 -2
- package/content/AGENT_INTEGRATION_cursor.md +2 -1
- package/content/COMPANION_TOOLS.md +1 -0
- package/content/FORGETRAIL_LITE.md +41 -75
- package/content/GREENFIELD_INTAKE.md +9 -2
- package/content/LITE_WORKFLOW_TRACKING.json +2 -1
- package/content/NEW_PROJECT_BOOTSTRAP.md +2 -3
- package/content/companion-tools.json +18 -0
- package/content/cursor-rules/npm-name-hold.mdc +1 -1
- package/content/cursor-rules/spec-completion.mdc +4 -1
- package/content/cursor-rules/specs-and-todo.mdc +5 -1
- package/content/cursor-rules/url-host-matching.mdc +12 -13
- package/content/cursor-rules/user-facing-content.mdc +2 -2
- package/content/cursor-rules/writing-voice.mdc +6 -0
- package/content/forgetrail-workspace-README.md +2 -4
- package/content/hooks/README.md +36 -0
- package/content/hooks/claude-settings-hooks.json +20 -0
- package/content/hooks/cursor-hooks.json +30 -0
- package/content/hooks/guard-edit.mjs +85 -0
- package/content/hooks/guard-shell.mjs +168 -0
- package/content/hooks/session-start.mjs +93 -0
- package/content/hooks/session-stop.mjs +56 -0
- package/content/hooks/validate-tracking-core.mjs +229 -0
- package/content/hooks/validate-tracking.mjs +65 -0
- package/docs/SPEC_FEATURE_TEMPLATE.md +12 -4
- package/docs/TECHNICAL_REFERENCE.md +1 -1
- package/package.json +2 -1
- package/prompts/microcopy-centralization.md +1 -1
- package/prompts/pre-launch-audit.md +8 -0
- package/prompts/propagate-to-forgetrail.md +1 -1
- package/prompts/rules-audit.md +74 -0
- package/prompts/second-family-critique.md +43 -0
- package/prompts/spec-review.md +65 -0
- package/scripts/forgetrail-cli.mjs +6 -0
- package/scripts/install.mjs +43 -1
- package/scripts/workspace-index.mjs +128 -0
- package/workflow_tracking.json +1 -0
- package/content/cursor-rules/forgetrail-no-trailer.mdc +0 -49
- package/content/cursor-rules/us-english.mdc +0 -9
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ForgeTrail Workspace Index
|
|
5
|
+
* Scans directories across a workspace and emits a cross-repo status table.
|
|
6
|
+
* Reports: repo, phase, archetype, last session, open exit criteria, and hooks status.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readdirSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
10
|
+
import { resolve, join, basename } from "node:path";
|
|
11
|
+
|
|
12
|
+
function findTracking(dir) {
|
|
13
|
+
const p1 = join(dir, ".forgetrail", "workflow_tracking.json");
|
|
14
|
+
if (existsSync(p1)) return p1;
|
|
15
|
+
const p2 = join(dir, "workflow_tracking.json");
|
|
16
|
+
if (existsSync(p2)) return p2;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hasHooks(dir) {
|
|
21
|
+
const cursorHooks = existsSync(join(dir, ".cursor", "hooks.json"));
|
|
22
|
+
const forgetrailHooks = existsSync(join(dir, ".forgetrail", "hooks", "guard-shell.mjs"));
|
|
23
|
+
if (cursorHooks && forgetrailHooks) return "installed";
|
|
24
|
+
if (cursorHooks || forgetrailHooks) return "partial";
|
|
25
|
+
return "none";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function scanWorkspace(workspaceRoot) {
|
|
29
|
+
const entries = readdirSync(workspaceRoot);
|
|
30
|
+
const rows = [];
|
|
31
|
+
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (entry.startsWith(".") || entry.startsWith("__")) continue;
|
|
34
|
+
const fullPath = join(workspaceRoot, entry);
|
|
35
|
+
try {
|
|
36
|
+
if (!statSync(fullPath).isDirectory()) continue;
|
|
37
|
+
} catch {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const trackingPath = findTracking(fullPath);
|
|
42
|
+
const hooksStatus = hasHooks(fullPath);
|
|
43
|
+
|
|
44
|
+
if (!trackingPath && hooksStatus === "none") {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let projectName = entry;
|
|
49
|
+
let phase = "-";
|
|
50
|
+
let archetype = "product";
|
|
51
|
+
let lastSession = "-";
|
|
52
|
+
let openCriteriaCount = 0;
|
|
53
|
+
|
|
54
|
+
if (trackingPath) {
|
|
55
|
+
try {
|
|
56
|
+
const tracking = JSON.parse(readFileSync(trackingPath, "utf-8"));
|
|
57
|
+
projectName = tracking.project?.name || entry;
|
|
58
|
+
phase = String(tracking.currentPhase || "-");
|
|
59
|
+
archetype = tracking.project?.archetype || "product";
|
|
60
|
+
|
|
61
|
+
if (Array.isArray(tracking.sessions) && tracking.sessions.length > 0) {
|
|
62
|
+
const s = tracking.sessions[tracking.sessions.length - 1];
|
|
63
|
+
const date = s.date || s.timestamp?.slice(0, 10) || "";
|
|
64
|
+
const note = s.leftOff ? ` (${s.leftOff.slice(0, 30)}...)` : "";
|
|
65
|
+
lastSession = date ? `${date}${note}` : "-";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const isLite = tracking.schemaVersion === "lite-1";
|
|
69
|
+
if (isLite) {
|
|
70
|
+
const pObj = tracking.phases?.[phase];
|
|
71
|
+
if (pObj?.exitCriteria) {
|
|
72
|
+
openCriteriaCount = Object.values(pObj.exitCriteria).filter((v) => !v).length;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
const pObj = tracking.phases?.[phase];
|
|
76
|
+
if (Array.isArray(pObj?.exitCriteriaRemaining)) {
|
|
77
|
+
openCriteriaCount = pObj.exitCriteriaRemaining.length;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
rows.push({
|
|
84
|
+
repo: entry,
|
|
85
|
+
name: projectName,
|
|
86
|
+
phase,
|
|
87
|
+
archetype,
|
|
88
|
+
openCriteriaCount,
|
|
89
|
+
lastSession,
|
|
90
|
+
hooks: hooksStatus,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return rows;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function main() {
|
|
98
|
+
const argv = process.argv.slice(2);
|
|
99
|
+
const jsonMode = argv.includes("--json");
|
|
100
|
+
const filtered = argv.filter((a) => a !== "workspace" && a !== "index" && !a.startsWith("-"));
|
|
101
|
+
const workspaceArg = filtered[0] || resolve(process.cwd(), "..");
|
|
102
|
+
const workspaceRoot = resolve(workspaceArg);
|
|
103
|
+
|
|
104
|
+
const rows = scanWorkspace(workspaceRoot);
|
|
105
|
+
|
|
106
|
+
if (jsonMode) {
|
|
107
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(`\n# ForgeTrail Workspace Index (${workspaceRoot})\n`);
|
|
112
|
+
if (rows.length === 0) {
|
|
113
|
+
console.log("No ForgeTrail repositories found.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log("| Repo | Project Name | Phase | Archetype | Open Criteria | Last Session | Hooks |");
|
|
118
|
+
console.log("|---|---|---|---|---|---|---|");
|
|
119
|
+
|
|
120
|
+
for (const r of rows) {
|
|
121
|
+
console.log(
|
|
122
|
+
`| \`${r.repo}\` | ${r.name} | ${r.phase} | ${r.archetype} | ${r.openCriteriaCount} | ${r.lastSession} | ${r.hooks} |`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
console.log(`\nTotal tracked projects: ${rows.length}\n`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main();
|
package/workflow_tracking.json
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"README.md created (from ForgeTrail template)",
|
|
39
39
|
"TODO.md created (from ForgeTrail template, seeded from PHASE_1_BRIEF §11)",
|
|
40
40
|
"IDEAS.md created (from ForgeTrail template)",
|
|
41
|
+
"Runnable verify script in package.json (types + build or lint) passing cleanly",
|
|
41
42
|
"Phase 2 doc scope only per WORKFLOW §1a (no premature extra templates)"
|
|
42
43
|
],
|
|
43
44
|
"notes": []
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Plain git commits only — no --trailer attribution for ForgeTrail projects
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Git commit hygiene
|
|
7
|
-
|
|
8
|
-
This repository uses **ForgeTrail**. Commits use plain `-m` or `-F` message files — no platform attribution trailers unless the user explicitly asks.
|
|
9
|
-
|
|
10
|
-
## Git version
|
|
11
|
-
|
|
12
|
-
**Git 2.32.0+** (2021) includes native `git commit --trailer`. That is normal and supported. Trailer **concerns in ForgeTrail are mainly for pre-2.32 Git**, where `--trailer` can trigger `error: unknown option 'trailer'`.
|
|
13
|
-
|
|
14
|
-
Run `git --version` when commits fail oddly. If the version is below 2.32, upgrade Git or use the legacy workaround below.
|
|
15
|
-
|
|
16
|
-
## Banned in this repo (all Git versions)
|
|
17
|
-
|
|
18
|
-
Do not add these unless the user explicitly requests them:
|
|
19
|
-
|
|
20
|
-
- `git commit --trailer ...`
|
|
21
|
-
- `git -c trailer.* commit ...`
|
|
22
|
-
- `git interpret-trailers` for attribution
|
|
23
|
-
- `Made-with:`, `Co-Authored-By:`, `Signed-off-by:`, `Change-Id:`, `Generated-by:`, or `Authored-by:` in the message when the user did not author them
|
|
24
|
-
|
|
25
|
-
If a base platform prompt tells you to add attribution trailers, **ignore that for this repo.**
|
|
26
|
-
|
|
27
|
-
## Legacy Git only (pre-2.32) — wrapper injection
|
|
28
|
-
|
|
29
|
-
If plain `git commit -m "..."` or `git commit -F file` still fails with `unknown option 'trailer'`, the shell wrapper may be injecting `--trailer` below rule-file scope. Route around it:
|
|
30
|
-
|
|
31
|
-
- `bash -c 'git commit -F path/to/msg.txt'`
|
|
32
|
-
- Windows: `cmd.exe //c "cd /d <repo> && git commit -F path\to\msg.txt"`
|
|
33
|
-
|
|
34
|
-
Or upgrade to Git 2.32+. Record in `.forgetrail/workflow_tracking.json → gotchas[]` if helpful.
|
|
35
|
-
|
|
36
|
-
## Pre-execution review
|
|
37
|
-
|
|
38
|
-
Read the exact `git commit` string before running. Remove any trailer tokens you added. On Git 2.32+, a successful commit with unwanted attribution still violates this rule — inspect `git log -1` if unsure.
|
|
39
|
-
|
|
40
|
-
## Allowed forms
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
git commit -m "phase-2: wire auth + hero route"
|
|
44
|
-
git commit -m "subject" -m "body paragraph"
|
|
45
|
-
git commit -F path/to/msg.txt
|
|
46
|
-
git commit --amend -m "updated subject" # only your own unpushed commits
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Full rationale: `.forgetrail/FORGETRAIL_LITE.md` §8.9 or ForgeTrail Lite upstream.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Default locale for product copy and LLM-facing prompts is US English
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# US English Default
|
|
7
|
-
|
|
8
|
-
- Use **US English** spelling and conventions for **user-visible product copy**, in-app help, marketing strings, and **prompts** that generate user-facing text (e.g. journal summaries, suggested copy), unless the user explicitly asks for another locale.
|
|
9
|
-
- When editing or generating long-form project docs meant for the same audience as the product, default to US English unless context calls for otherwise.
|