waypoint-codex 0.1.1 → 0.1.3
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 +5 -5
- package/dist/src/cli.js +3 -1
- package/dist/src/core.js +23 -11
- package/package.json +1 -1
- package/templates/.gitignore.snippet +1 -0
- package/templates/.waypoint/README.md +2 -0
- package/templates/.waypoint/agent-operating-manual.md +4 -4
- package/templates/.waypoint/config.toml +2 -2
- package/templates/.waypoint/scripts/build-docs-index.mjs +1 -1
- package/templates/.waypoint/scripts/prepare-context.mjs +230 -3
- package/templates/managed-agents-block.md +2 -2
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@ Waypoint is a docs-first repository operating system for Codex.
|
|
|
5
5
|
It helps the next agent pick up your repo with full context by keeping the important things in markdown files inside the repo:
|
|
6
6
|
|
|
7
7
|
- `AGENTS.md` for startup instructions
|
|
8
|
-
-
|
|
8
|
+
- `.waypoint/WORKSPACE.md` for live state
|
|
9
9
|
- `.waypoint/docs/` for durable project memory
|
|
10
|
-
-
|
|
10
|
+
- `.waypoint/DOCS_INDEX.md` for docs routing
|
|
11
11
|
- repo-local skills for planning and audits
|
|
12
12
|
|
|
13
13
|
## Install
|
|
@@ -36,10 +36,10 @@ That scaffolds:
|
|
|
36
36
|
```text
|
|
37
37
|
repo/
|
|
38
38
|
├── AGENTS.md
|
|
39
|
-
├── WORKSPACE.md
|
|
40
|
-
├── DOCS_INDEX.md
|
|
41
39
|
├── .agents/skills/
|
|
42
40
|
└── .waypoint/
|
|
41
|
+
├── WORKSPACE.md
|
|
42
|
+
├── DOCS_INDEX.md
|
|
43
43
|
├── docs/
|
|
44
44
|
├── context/
|
|
45
45
|
└── ...
|
|
@@ -49,7 +49,7 @@ repo/
|
|
|
49
49
|
|
|
50
50
|
- `waypoint init` — scaffold or refresh the repo
|
|
51
51
|
- `waypoint doctor` — check for drift and missing pieces
|
|
52
|
-
- `waypoint sync` — rebuild
|
|
52
|
+
- `waypoint sync` — rebuild `.waypoint/DOCS_INDEX.md` and sync optional automations/rules
|
|
53
53
|
- `waypoint import-legacy` — import from an older repo layout
|
|
54
54
|
|
|
55
55
|
## Shipped skills
|
package/dist/src/cli.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
3
5
|
import path from "node:path";
|
|
4
6
|
import process from "node:process";
|
|
5
7
|
import { doctorRepository, importLegacyRepo, initRepository, syncRepository } from "./core.js";
|
|
6
|
-
const VERSION = "
|
|
8
|
+
const VERSION = JSON.parse(readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../package.json"), "utf8")).version;
|
|
7
9
|
function resolveRepo(input) {
|
|
8
10
|
return path.resolve(input ?? ".");
|
|
9
11
|
}
|
package/dist/src/core.js
CHANGED
|
@@ -7,8 +7,8 @@ import { renderDocsIndex } from "./docs-index.js";
|
|
|
7
7
|
import { readTemplate, renderWaypointConfig, MANAGED_BLOCK_END, MANAGED_BLOCK_START, templatePath } from "./templates.js";
|
|
8
8
|
const DEFAULT_CONFIG_PATH = ".waypoint/config.toml";
|
|
9
9
|
const DEFAULT_DOCS_DIR = ".waypoint/docs";
|
|
10
|
-
const DEFAULT_DOCS_INDEX = "DOCS_INDEX.md";
|
|
11
|
-
const DEFAULT_WORKSPACE = "WORKSPACE.md";
|
|
10
|
+
const DEFAULT_DOCS_INDEX = ".waypoint/DOCS_INDEX.md";
|
|
11
|
+
const DEFAULT_WORKSPACE = ".waypoint/WORKSPACE.md";
|
|
12
12
|
const STATE_DIR = ".waypoint/state";
|
|
13
13
|
const SYNC_RECORDS_FILE = ".waypoint/state/sync-records.json";
|
|
14
14
|
function ensureDir(dirPath) {
|
|
@@ -29,6 +29,15 @@ function removePathIfExists(targetPath) {
|
|
|
29
29
|
rmSync(targetPath, { recursive: true, force: true });
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
function migrateLegacyRootFiles(projectRoot) {
|
|
33
|
+
const legacyWorkspace = path.join(projectRoot, "WORKSPACE.md");
|
|
34
|
+
const newWorkspace = path.join(projectRoot, DEFAULT_WORKSPACE);
|
|
35
|
+
if (existsSync(legacyWorkspace) && !existsSync(newWorkspace)) {
|
|
36
|
+
writeText(newWorkspace, readFileSync(legacyWorkspace, "utf8"));
|
|
37
|
+
}
|
|
38
|
+
removePathIfExists(legacyWorkspace);
|
|
39
|
+
removePathIfExists(path.join(projectRoot, "DOCS_INDEX.md"));
|
|
40
|
+
}
|
|
32
41
|
function appendGitignoreSnippet(projectRoot) {
|
|
33
42
|
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
34
43
|
const snippet = readTemplate(".gitignore.snippet").trim();
|
|
@@ -90,9 +99,12 @@ function scaffoldOptionalCodex(projectRoot) {
|
|
|
90
99
|
}
|
|
91
100
|
export function initRepository(projectRoot, options) {
|
|
92
101
|
ensureDir(projectRoot);
|
|
102
|
+
migrateLegacyRootFiles(projectRoot);
|
|
93
103
|
for (const deprecatedPath of [
|
|
94
104
|
"docs/README.md",
|
|
95
105
|
"docs/code-guide.md",
|
|
106
|
+
"docs/legacy-import",
|
|
107
|
+
"WAYPOINT_MIGRATION.md",
|
|
96
108
|
".agents/skills/waypoint-planning",
|
|
97
109
|
".agents/skills/waypoint-docs",
|
|
98
110
|
".agents/skills/waypoint-review",
|
|
@@ -138,9 +150,9 @@ export function initRepository(projectRoot, options) {
|
|
|
138
150
|
return [
|
|
139
151
|
"Initialized Waypoint scaffold",
|
|
140
152
|
"Installed managed AGENTS block",
|
|
141
|
-
"Created WORKSPACE.md and .waypoint/docs/ scaffold",
|
|
153
|
+
"Created .waypoint/WORKSPACE.md and .waypoint/docs/ scaffold",
|
|
142
154
|
"Installed repo-local Waypoint skills",
|
|
143
|
-
"Generated DOCS_INDEX.md",
|
|
155
|
+
"Generated .waypoint/DOCS_INDEX.md",
|
|
144
156
|
];
|
|
145
157
|
}
|
|
146
158
|
export function loadWaypointConfig(projectRoot) {
|
|
@@ -316,7 +328,7 @@ export function doctorRepository(projectRoot) {
|
|
|
316
328
|
findings.push({
|
|
317
329
|
severity: "error",
|
|
318
330
|
category: "workspace",
|
|
319
|
-
message: "WORKSPACE.md is missing.",
|
|
331
|
+
message: ".waypoint/WORKSPACE.md is missing.",
|
|
320
332
|
remediation: "Run `waypoint init` to scaffold the workspace file.",
|
|
321
333
|
paths: [workspacePath],
|
|
322
334
|
});
|
|
@@ -365,7 +377,7 @@ export function doctorRepository(projectRoot) {
|
|
|
365
377
|
findings.push({
|
|
366
378
|
severity: "error",
|
|
367
379
|
category: "docs",
|
|
368
|
-
message: "docs/ directory is missing.",
|
|
380
|
+
message: ".waypoint/docs/ directory is missing.",
|
|
369
381
|
remediation: "Run `waypoint init` to scaffold docs.",
|
|
370
382
|
paths: [docsDir],
|
|
371
383
|
});
|
|
@@ -383,7 +395,7 @@ export function doctorRepository(projectRoot) {
|
|
|
383
395
|
findings.push({
|
|
384
396
|
severity: "warn",
|
|
385
397
|
category: "docs",
|
|
386
|
-
message: "DOCS_INDEX.md is missing.",
|
|
398
|
+
message: ".waypoint/DOCS_INDEX.md is missing.",
|
|
387
399
|
remediation: "Run `waypoint sync` to generate the docs index.",
|
|
388
400
|
paths: [docsIndexPath],
|
|
389
401
|
});
|
|
@@ -392,7 +404,7 @@ export function doctorRepository(projectRoot) {
|
|
|
392
404
|
findings.push({
|
|
393
405
|
severity: "warn",
|
|
394
406
|
category: "docs",
|
|
395
|
-
message: "DOCS_INDEX.md is stale.",
|
|
407
|
+
message: ".waypoint/DOCS_INDEX.md is stale.",
|
|
396
408
|
remediation: "Run `waypoint sync` to rebuild the docs index.",
|
|
397
409
|
paths: [docsIndexPath],
|
|
398
410
|
});
|
|
@@ -472,7 +484,7 @@ export function syncRepository(projectRoot) {
|
|
|
472
484
|
const docsIndexPath = path.join(projectRoot, config.docs_index_file ?? DEFAULT_DOCS_INDEX);
|
|
473
485
|
const docsIndex = renderDocsIndex(projectRoot, docsDir);
|
|
474
486
|
writeText(docsIndexPath, `${docsIndex.content}\n`);
|
|
475
|
-
const results = ["Rebuilt DOCS_INDEX.md"];
|
|
487
|
+
const results = ["Rebuilt .waypoint/DOCS_INDEX.md"];
|
|
476
488
|
const featureMap = config.features ?? {};
|
|
477
489
|
if (featureMap.rules) {
|
|
478
490
|
results.push(...syncRules(projectRoot));
|
|
@@ -527,7 +539,7 @@ export function importLegacyRepo(sourceRepo, targetRepo, options = {}) {
|
|
|
527
539
|
}));
|
|
528
540
|
}
|
|
529
541
|
if (targetRepo) {
|
|
530
|
-
const importDir = path.join(targetRepo, "docs/legacy-import");
|
|
542
|
+
const importDir = path.join(targetRepo, ".waypoint/docs/legacy-import");
|
|
531
543
|
ensureDir(importDir);
|
|
532
544
|
for (const docName of portableDocs) {
|
|
533
545
|
copyFileSync(path.join(sourceDocsDir, docName), path.join(importDir, docName));
|
|
@@ -585,7 +597,7 @@ export function importLegacyRepo(sourceRepo, targetRepo, options = {}) {
|
|
|
585
597
|
"",
|
|
586
598
|
].join("\n");
|
|
587
599
|
if (targetRepo) {
|
|
588
|
-
const reportPath = path.join(targetRepo, "
|
|
600
|
+
const reportPath = path.join(targetRepo, ".waypoint/IMPORT_LEGACY.md");
|
|
589
601
|
writeText(reportPath, report);
|
|
590
602
|
actions.push(`Wrote migration report to ${reportPath}`);
|
|
591
603
|
}
|
package/package.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Repo-local Waypoint configuration and optional integration sources.
|
|
4
4
|
|
|
5
5
|
- `config.toml` — Waypoint feature toggles and file locations
|
|
6
|
+
- `WORKSPACE.md` — live operational state
|
|
7
|
+
- `DOCS_INDEX.md` — generated docs routing map
|
|
6
8
|
- `SOUL.md` — agent identity and working values
|
|
7
9
|
- `agent-operating-manual.md` — required session workflow
|
|
8
10
|
- `docs/` — Waypoint-managed project memory (architecture, decisions, debugging knowledge, durable plans)
|
|
@@ -9,7 +9,7 @@ At the start of every session:
|
|
|
9
9
|
1. Run `node .waypoint/scripts/prepare-context.mjs`
|
|
10
10
|
2. Read `.waypoint/SOUL.md`
|
|
11
11
|
3. Read this file
|
|
12
|
-
4. Read
|
|
12
|
+
4. Read `.waypoint/WORKSPACE.md`
|
|
13
13
|
5. Read `.waypoint/context/MANIFEST.md`
|
|
14
14
|
6. Read every file listed in that manifest
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ Do not skip this sequence.
|
|
|
19
19
|
|
|
20
20
|
The repository should contain the context the next agent needs.
|
|
21
21
|
|
|
22
|
-
-
|
|
22
|
+
- `.waypoint/WORKSPACE.md` is the live operational record: in progress, current state, next steps
|
|
23
23
|
- `.waypoint/docs/` is the durable project memory: architecture, decisions, integration notes, debugging knowledge, and durable plans
|
|
24
24
|
- `.waypoint/context/` is the generated session context bundle: current git/PR/doc index state
|
|
25
25
|
|
|
@@ -29,9 +29,9 @@ If something important lives only in your head or in the chat transcript, the re
|
|
|
29
29
|
|
|
30
30
|
- Read code before editing it.
|
|
31
31
|
- Follow the repo's documented patterns when they are healthy.
|
|
32
|
-
- Update
|
|
32
|
+
- Update `.waypoint/WORKSPACE.md` as live execution state when progress meaningfully changes.
|
|
33
33
|
- Update `.waypoint/docs/` when durable knowledge changes.
|
|
34
|
-
- Rebuild
|
|
34
|
+
- Rebuild `.waypoint/DOCS_INDEX.md` whenever routable docs change.
|
|
35
35
|
- Use the repo-local skills and optional reviewer agents instead of improvising from scratch.
|
|
36
36
|
|
|
37
37
|
## Documentation expectations
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
version = 1
|
|
2
2
|
profile = "__PROFILE__"
|
|
3
|
-
workspace_file = "WORKSPACE.md"
|
|
3
|
+
workspace_file = ".waypoint/WORKSPACE.md"
|
|
4
4
|
docs_dir = ".waypoint/docs"
|
|
5
|
-
docs_index_file = "DOCS_INDEX.md"
|
|
5
|
+
docs_index_file = ".waypoint/DOCS_INDEX.md"
|
|
6
6
|
|
|
7
7
|
[features]
|
|
8
8
|
repo_skills = true
|
|
@@ -139,7 +139,7 @@ export function renderDocsIndex(projectRoot) {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
export function writeDocsIndex(projectRoot) {
|
|
142
|
-
const outputPath = path.join(projectRoot, "DOCS_INDEX.md");
|
|
142
|
+
const outputPath = path.join(projectRoot, ".waypoint", "DOCS_INDEX.md");
|
|
143
143
|
writeFileSync(outputPath, renderDocsIndex(projectRoot), "utf8");
|
|
144
144
|
return outputPath;
|
|
145
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
@@ -35,6 +35,232 @@ function safeExec(command, cwd) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const SESSION_DIR_NAMES = ["sessions", "archived_sessions"];
|
|
39
|
+
const SECRET_PATTERNS = [
|
|
40
|
+
/npm_[A-Za-z0-9]+/g,
|
|
41
|
+
/github_pat_[A-Za-z0-9_]+/g,
|
|
42
|
+
/gh[pousr]_[A-Za-z0-9]+/g,
|
|
43
|
+
/sk-[A-Za-z0-9]+/g,
|
|
44
|
+
/sk_[A-Za-z0-9]+/g,
|
|
45
|
+
/fc-[A-Za-z0-9]+/g,
|
|
46
|
+
/AIza[0-9A-Za-z\-_]{20,}/g,
|
|
47
|
+
];
|
|
48
|
+
const MAX_RECENT_TURNS = 25;
|
|
49
|
+
|
|
50
|
+
function codexHome() {
|
|
51
|
+
return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function redactSecrets(text) {
|
|
55
|
+
return SECRET_PATTERNS.reduce((current, pattern) => current.replace(pattern, "[REDACTED]"), text);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isWithinPath(childPath, parentPath) {
|
|
59
|
+
const rel = path.relative(realpathSync(parentPath), realpathSync(childPath));
|
|
60
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function collectSessionFiles(rootDir) {
|
|
64
|
+
const files = [];
|
|
65
|
+
|
|
66
|
+
function walk(currentDir) {
|
|
67
|
+
if (!existsSync(currentDir)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const entry of readdirSync(currentDir)) {
|
|
71
|
+
const fullPath = path.join(currentDir, entry);
|
|
72
|
+
let stats;
|
|
73
|
+
try {
|
|
74
|
+
stats = statSync(fullPath);
|
|
75
|
+
} catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (stats.isDirectory()) {
|
|
79
|
+
walk(fullPath);
|
|
80
|
+
} else if (entry.endsWith(".jsonl")) {
|
|
81
|
+
files.push(fullPath);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
walk(rootDir);
|
|
87
|
+
return files;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function extractMessageText(content) {
|
|
91
|
+
if (!Array.isArray(content)) {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
return content
|
|
95
|
+
.filter((block) => block?.type === "input_text" || block?.type === "output_text")
|
|
96
|
+
.map((block) => (typeof block?.text === "string" ? block.text : ""))
|
|
97
|
+
.join("")
|
|
98
|
+
.trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isBootstrapNoise(role, text) {
|
|
102
|
+
return role === "user" && text.startsWith("# AGENTS.md instructions for ");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function mergeConsecutiveTurns(turns) {
|
|
106
|
+
const merged = [];
|
|
107
|
+
for (const turn of turns) {
|
|
108
|
+
const previous = merged.at(-1);
|
|
109
|
+
if (previous && previous.role === turn.role) {
|
|
110
|
+
if (previous.text !== turn.text) {
|
|
111
|
+
previous.text = `${previous.text}\n\n${turn.text}`;
|
|
112
|
+
}
|
|
113
|
+
previous.timestamp = turn.timestamp || previous.timestamp;
|
|
114
|
+
previous.messageCount += 1;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
merged.push({ ...turn });
|
|
118
|
+
}
|
|
119
|
+
return merged;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseSession(sessionFile, projectRoot) {
|
|
123
|
+
let sessionCwd = null;
|
|
124
|
+
let compactionCount = 0;
|
|
125
|
+
const rawTurns = [];
|
|
126
|
+
const compactionBoundaries = [];
|
|
127
|
+
|
|
128
|
+
for (const line of readFileSync(sessionFile, "utf8").split("\n")) {
|
|
129
|
+
if (!line.trim()) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let parsed;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(line);
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (parsed.type === "session_meta") {
|
|
141
|
+
const cwd = parsed.payload?.cwd;
|
|
142
|
+
if (typeof cwd === "string") {
|
|
143
|
+
sessionCwd = cwd;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (parsed.type === "compacted") {
|
|
149
|
+
compactionCount += 1;
|
|
150
|
+
compactionBoundaries.push(rawTurns.length);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (parsed.type !== "response_item" || parsed.payload?.type !== "message") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const role = parsed.payload?.role;
|
|
159
|
+
if (role !== "user" && role !== "assistant") {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const text = redactSecrets(extractMessageText(parsed.payload?.content));
|
|
164
|
+
if (!text || isBootstrapNoise(role, text)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
rawTurns.push({
|
|
169
|
+
role,
|
|
170
|
+
text,
|
|
171
|
+
timestamp: parsed.timestamp || null,
|
|
172
|
+
messageCount: 1,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!sessionCwd || !isWithinPath(sessionCwd, projectRoot)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const selectedFromPreCompaction = compactionBoundaries.length > 0;
|
|
181
|
+
const relevantTurns = selectedFromPreCompaction ? rawTurns.slice(0, compactionBoundaries.at(-1)) : rawTurns;
|
|
182
|
+
const turns = mergeConsecutiveTurns(relevantTurns);
|
|
183
|
+
if (turns.length === 0) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
path: sessionFile,
|
|
189
|
+
sessionCwd,
|
|
190
|
+
turns,
|
|
191
|
+
compactionCount,
|
|
192
|
+
selectedFromPreCompaction,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function latestMatchingSession(projectRoot) {
|
|
197
|
+
const matches = [];
|
|
198
|
+
for (const dirName of SESSION_DIR_NAMES) {
|
|
199
|
+
for (const sessionFile of collectSessionFiles(path.join(codexHome(), dirName))) {
|
|
200
|
+
const parsed = parseSession(sessionFile, projectRoot);
|
|
201
|
+
if (parsed) {
|
|
202
|
+
matches.push(parsed);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
matches.sort((a, b) => statSync(b.path).mtimeMs - statSync(a.path).mtimeMs);
|
|
208
|
+
return matches[0] || null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function writeRecentThread(contextDir, projectRoot) {
|
|
212
|
+
const filePath = path.join(contextDir, "RECENT_THREAD.md");
|
|
213
|
+
const snapshot = latestMatchingSession(projectRoot);
|
|
214
|
+
const generatedAt = new Date().toString();
|
|
215
|
+
|
|
216
|
+
if (!snapshot) {
|
|
217
|
+
writeFileSync(
|
|
218
|
+
filePath,
|
|
219
|
+
[
|
|
220
|
+
"# Recent Thread",
|
|
221
|
+
"",
|
|
222
|
+
`Generated by \`${path.relative(projectRoot, fileURLToPath(import.meta.url))}\` on ${generatedAt}.`,
|
|
223
|
+
"",
|
|
224
|
+
"No matching local Codex session was found for this repo yet.",
|
|
225
|
+
"",
|
|
226
|
+
].join("\n"),
|
|
227
|
+
"utf8"
|
|
228
|
+
);
|
|
229
|
+
return filePath;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const selectedTurns = snapshot.turns.slice(-MAX_RECENT_TURNS);
|
|
233
|
+
const relSessionPath = path.relative(codexHome(), snapshot.path);
|
|
234
|
+
const lines = [
|
|
235
|
+
"# Recent Thread",
|
|
236
|
+
"",
|
|
237
|
+
`Generated by \`${path.relative(projectRoot, fileURLToPath(import.meta.url))}\` on ${generatedAt}.`,
|
|
238
|
+
"",
|
|
239
|
+
`- Source session: \`${relSessionPath}\``,
|
|
240
|
+
`- Session cwd: \`${snapshot.sessionCwd}\``,
|
|
241
|
+
`- Included turns: ${selectedTurns.length} of ${snapshot.turns.length} meaningful turns`,
|
|
242
|
+
`- Compactions in source session: ${snapshot.compactionCount}`,
|
|
243
|
+
snapshot.selectedFromPreCompaction
|
|
244
|
+
? "- Selection rule: take the 25 meaningful turns immediately before the last compaction."
|
|
245
|
+
: "- Selection rule: no compaction found, so take the latest meaningful turns from the local transcript.",
|
|
246
|
+
"- Noise filter: bootstrap AGENTS payloads are excluded.",
|
|
247
|
+
"- Secret handling: obvious token formats are redacted before writing this file.",
|
|
248
|
+
"",
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
selectedTurns.forEach((turn, index) => {
|
|
252
|
+
const mergedSuffix = turn.messageCount > 1 ? ` (merged ${turn.messageCount} messages)` : "";
|
|
253
|
+
const timestamp = turn.timestamp ? ` - ${turn.timestamp}` : "";
|
|
254
|
+
lines.push(`## ${index + 1}. ${turn.role[0].toUpperCase()}${turn.role.slice(1)}${mergedSuffix}${timestamp}`);
|
|
255
|
+
lines.push("");
|
|
256
|
+
lines.push(turn.text);
|
|
257
|
+
lines.push("");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
writeFileSync(filePath, `${lines.join("\n").trimEnd()}\n`, "utf8");
|
|
261
|
+
return filePath;
|
|
262
|
+
}
|
|
263
|
+
|
|
38
264
|
function collectNestedGitRepos(projectRoot) {
|
|
39
265
|
const repos = [];
|
|
40
266
|
|
|
@@ -140,6 +366,7 @@ function main() {
|
|
|
140
366
|
"```",
|
|
141
367
|
].join("\n")
|
|
142
368
|
);
|
|
369
|
+
const recentThreadPath = writeRecentThread(contextDir, projectRoot);
|
|
143
370
|
|
|
144
371
|
const manifestPath = path.join(contextDir, "MANIFEST.md");
|
|
145
372
|
const manifestLines = [
|
|
@@ -154,13 +381,14 @@ function main() {
|
|
|
154
381
|
`- \`${path.relative(projectRoot, recentCommitsPath)}\` — recent commits`,
|
|
155
382
|
`- \`${path.relative(projectRoot, nestedReposPath)}\` — recent commits in nested repositories`,
|
|
156
383
|
`- \`${path.relative(projectRoot, prsPath)}\` — open and recently merged pull requests`,
|
|
384
|
+
`- \`${path.relative(projectRoot, recentThreadPath)}\` — latest meaningful turns from the local Codex session for this repo`,
|
|
157
385
|
`- \`${path.relative(projectRoot, docsIndexPath)}\` — current docs index`,
|
|
158
386
|
"",
|
|
159
387
|
"## Stable source-of-truth files to read before this manifest",
|
|
160
388
|
"",
|
|
161
389
|
"- `.waypoint/SOUL.md`",
|
|
162
390
|
"- `.waypoint/agent-operating-manual.md`",
|
|
163
|
-
"-
|
|
391
|
+
"- `.waypoint/WORKSPACE.md`",
|
|
164
392
|
"",
|
|
165
393
|
`Generated by: \`${path.relative(projectRoot, fileURLToPath(import.meta.url))}\``,
|
|
166
394
|
"",
|
|
@@ -171,4 +399,3 @@ function main() {
|
|
|
171
399
|
}
|
|
172
400
|
|
|
173
401
|
main();
|
|
174
|
-
|
|
@@ -7,14 +7,14 @@ Before doing substantial work:
|
|
|
7
7
|
1. Run `node .waypoint/scripts/prepare-context.mjs`
|
|
8
8
|
2. Read `.waypoint/SOUL.md`
|
|
9
9
|
3. Read `.waypoint/agent-operating-manual.md`
|
|
10
|
-
4. Read
|
|
10
|
+
4. Read `.waypoint/WORKSPACE.md`
|
|
11
11
|
5. Read `.waypoint/context/MANIFEST.md`
|
|
12
12
|
6. Read every file listed in the manifest
|
|
13
13
|
|
|
14
14
|
This is mandatory, not optional. Do not skip the context refresh or skip files in the manifest.
|
|
15
15
|
|
|
16
16
|
Working rules:
|
|
17
|
-
- Keep
|
|
17
|
+
- Keep `.waypoint/WORKSPACE.md` current as the live execution state
|
|
18
18
|
- Update `.waypoint/docs/` when behavior or durable project knowledge changes
|
|
19
19
|
- Use the repo-local skills Waypoint ships for structured workflows when relevant
|
|
20
20
|
- Treat the generated context bundle as required session bootstrap, not optional reference material
|