waypoint-codex 0.1.0 → 0.1.2
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 +35 -107
- package/dist/src/core.js +6 -4
- package/dist/src/docs-index.js +1 -1
- package/package.json +1 -1
- package/templates/.waypoint/README.md +1 -0
- package/templates/.waypoint/agent-operating-manual.md +2 -2
- package/templates/.waypoint/config.toml +1 -2
- package/templates/.waypoint/docs/README.md +27 -0
- package/templates/.waypoint/docs/code-guide.md +95 -0
- package/templates/.waypoint/scripts/build-docs-index.mjs +2 -2
- package/templates/.waypoint/scripts/prepare-context.mjs +229 -2
- package/templates/managed-agents-block.md +1 -1
- package/templates/docs/README.md +0 -16
- package/templates/docs/code-guide.md +0 -31
package/README.md
CHANGED
|
@@ -1,156 +1,84 @@
|
|
|
1
1
|
# Waypoint
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Waypoint is a docs-first repository operating system for Codex.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- a live `WORKSPACE.md`
|
|
7
|
-
- indexed `docs/`
|
|
8
|
-
- repo-local skills under `.agents/skills/`
|
|
9
|
-
- optional sync for Codex App automations and user-home rules
|
|
5
|
+
It helps the next agent pick up your repo with full context by keeping the important things in markdown files inside the repo:
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
- `AGENTS.md` for startup instructions
|
|
8
|
+
- `WORKSPACE.md` for live state
|
|
9
|
+
- `.waypoint/docs/` for durable project memory
|
|
10
|
+
- `DOCS_INDEX.md` for docs routing
|
|
11
|
+
- repo-local skills for planning and audits
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
The next agent should be able to pick up a repository with full context by reading the repository itself.
|
|
16
|
-
|
|
17
|
-
Waypoint pushes projects toward:
|
|
18
|
-
|
|
19
|
-
- docs-first project memory
|
|
20
|
-
- visible operational state
|
|
21
|
-
- explicit session bootstrap
|
|
22
|
-
- battle-tested reusable workflows
|
|
23
|
-
- less hidden magic and less reliance on chat history
|
|
24
|
-
|
|
25
|
-
## What ships today
|
|
26
|
-
|
|
27
|
-
- `waypoint init` — scaffold the core repository contract
|
|
28
|
-
- `waypoint doctor` — validate repo health and detect drift
|
|
29
|
-
- `waypoint sync` — rebuild `DOCS_INDEX.md` and optionally sync automations/rules into Codex home
|
|
30
|
-
- `waypoint import-legacy` — analyze a legacy repository layout and write an adoption report into a target Waypoint repo
|
|
31
|
-
|
|
32
|
-
## Install for development
|
|
33
|
-
|
|
34
|
-
From the `projects/waypoint/` directory:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm install
|
|
38
|
-
npm run build
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Run directly in development:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm run dev -- --help
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Run the compiled CLI after building:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
node dist/src/cli.js --help
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Run the tests:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
npm test
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Install for usage
|
|
60
|
-
|
|
61
|
-
Global install:
|
|
13
|
+
## Install
|
|
62
14
|
|
|
63
15
|
```bash
|
|
64
16
|
npm install -g waypoint-codex
|
|
65
17
|
```
|
|
66
18
|
|
|
67
|
-
|
|
19
|
+
Or use it without installing globally:
|
|
68
20
|
|
|
69
21
|
```bash
|
|
70
22
|
npx waypoint-codex@latest --help
|
|
71
23
|
```
|
|
72
24
|
|
|
73
|
-
|
|
25
|
+
## Start using it
|
|
26
|
+
|
|
27
|
+
Inside your Codex project:
|
|
74
28
|
|
|
75
29
|
```bash
|
|
76
30
|
waypoint init --with-automations --with-roles
|
|
77
31
|
waypoint doctor
|
|
78
32
|
```
|
|
79
33
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Initialize a repository:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
npx tsx src/cli.ts init /path/to/repo
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Check repo health:
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
npx tsx src/cli.ts doctor /path/to/repo
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Sync optional user-home artifacts:
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
npx tsx src/cli.ts sync /path/to/repo
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Analyze a legacy repository layout and scaffold a target Waypoint repo:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
npx tsx src/cli.ts import-legacy /path/to/source-repo /path/to/new-repo --init-target
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Repo contract
|
|
107
|
-
|
|
108
|
-
Waypoint creates and manages:
|
|
34
|
+
That scaffolds:
|
|
109
35
|
|
|
110
36
|
```text
|
|
111
37
|
repo/
|
|
112
38
|
├── AGENTS.md
|
|
113
39
|
├── WORKSPACE.md
|
|
114
40
|
├── DOCS_INDEX.md
|
|
115
|
-
├── docs/
|
|
116
41
|
├── .agents/skills/
|
|
117
42
|
└── .waypoint/
|
|
43
|
+
├── docs/
|
|
44
|
+
├── context/
|
|
45
|
+
└── ...
|
|
118
46
|
```
|
|
119
47
|
|
|
120
|
-
|
|
48
|
+
## Main commands
|
|
121
49
|
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
50
|
+
- `waypoint init` — scaffold or refresh the repo
|
|
51
|
+
- `waypoint doctor` — check for drift and missing pieces
|
|
52
|
+
- `waypoint sync` — rebuild `DOCS_INDEX.md` and sync optional automations/rules
|
|
53
|
+
- `waypoint import-legacy` — import from an older repo layout
|
|
126
54
|
|
|
127
|
-
## Shipped
|
|
55
|
+
## Shipped skills
|
|
128
56
|
|
|
129
57
|
- `planning`
|
|
130
58
|
- `error-audit`
|
|
131
59
|
- `observability-audit`
|
|
132
60
|
- `ux-states-audit`
|
|
133
61
|
|
|
134
|
-
## Optional
|
|
62
|
+
## Optional reviewer roles
|
|
135
63
|
|
|
136
|
-
If you initialize with `--with-roles`, Waypoint
|
|
64
|
+
If you initialize with `--with-roles`, Waypoint scaffolds:
|
|
137
65
|
|
|
138
66
|
- `code-health-reviewer`
|
|
139
67
|
- `code-reviewer`
|
|
140
68
|
- `docs-researcher`
|
|
141
69
|
- `plan-reviewer`
|
|
142
70
|
|
|
143
|
-
##
|
|
144
|
-
|
|
145
|
-
- `waypoint init`
|
|
146
|
-
- `waypoint doctor`
|
|
147
|
-
- `waypoint sync` with real Codex automation TOML output
|
|
148
|
-
- `waypoint import-legacy`
|
|
71
|
+
## Update
|
|
149
72
|
|
|
150
|
-
|
|
73
|
+
```bash
|
|
74
|
+
npm install -g waypoint-codex@latest
|
|
75
|
+
waypoint init --with-automations --with-roles
|
|
76
|
+
waypoint doctor
|
|
77
|
+
```
|
|
151
78
|
|
|
152
|
-
|
|
153
|
-
- App automations are supported as an optional declarative sync target.
|
|
154
|
-
- Rules are supported as an optional sync target.
|
|
79
|
+
## Learn more
|
|
155
80
|
|
|
156
|
-
|
|
81
|
+
- [Overview](docs/overview.md)
|
|
82
|
+
- [Architecture](docs/architecture.md)
|
|
83
|
+
- [Upgrading](docs/upgrading.md)
|
|
84
|
+
- [Importing Existing Repositories](docs/importing-existing-repos.md)
|
package/dist/src/core.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as TOML from "@iarna/toml";
|
|
|
6
6
|
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
|
-
const DEFAULT_DOCS_DIR = "docs";
|
|
9
|
+
const DEFAULT_DOCS_DIR = ".waypoint/docs";
|
|
10
10
|
const DEFAULT_DOCS_INDEX = "DOCS_INDEX.md";
|
|
11
11
|
const DEFAULT_WORKSPACE = "WORKSPACE.md";
|
|
12
12
|
const STATE_DIR = ".waypoint/state";
|
|
@@ -91,6 +91,8 @@ function scaffoldOptionalCodex(projectRoot) {
|
|
|
91
91
|
export function initRepository(projectRoot, options) {
|
|
92
92
|
ensureDir(projectRoot);
|
|
93
93
|
for (const deprecatedPath of [
|
|
94
|
+
"docs/README.md",
|
|
95
|
+
"docs/code-guide.md",
|
|
94
96
|
".agents/skills/waypoint-planning",
|
|
95
97
|
".agents/skills/waypoint-docs",
|
|
96
98
|
".agents/skills/waypoint-review",
|
|
@@ -123,8 +125,8 @@ export function initRepository(projectRoot, options) {
|
|
|
123
125
|
}));
|
|
124
126
|
writeIfMissing(path.join(projectRoot, DEFAULT_WORKSPACE), readTemplate("WORKSPACE.md"));
|
|
125
127
|
ensureDir(path.join(projectRoot, DEFAULT_DOCS_DIR));
|
|
126
|
-
writeIfMissing(path.join(projectRoot, "docs/README.md"), readTemplate("docs/README.md"));
|
|
127
|
-
writeIfMissing(path.join(projectRoot, "docs/code-guide.md"), readTemplate("docs/code-guide.md"));
|
|
128
|
+
writeIfMissing(path.join(projectRoot, ".waypoint/docs/README.md"), readTemplate(".waypoint/docs/README.md"));
|
|
129
|
+
writeIfMissing(path.join(projectRoot, ".waypoint/docs/code-guide.md"), readTemplate(".waypoint/docs/code-guide.md"));
|
|
128
130
|
upsertManagedBlock(path.join(projectRoot, "AGENTS.md"), readTemplate("managed-agents-block.md"));
|
|
129
131
|
scaffoldSkills(projectRoot);
|
|
130
132
|
if (options.withRoles) {
|
|
@@ -136,7 +138,7 @@ export function initRepository(projectRoot, options) {
|
|
|
136
138
|
return [
|
|
137
139
|
"Initialized Waypoint scaffold",
|
|
138
140
|
"Installed managed AGENTS block",
|
|
139
|
-
"Created WORKSPACE.md and docs/ scaffold",
|
|
141
|
+
"Created WORKSPACE.md and .waypoint/docs/ scaffold",
|
|
140
142
|
"Installed repo-local Waypoint skills",
|
|
141
143
|
"Generated DOCS_INDEX.md",
|
|
142
144
|
];
|
package/dist/src/docs-index.js
CHANGED
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ Repo-local Waypoint configuration and optional integration sources.
|
|
|
5
5
|
- `config.toml` — Waypoint feature toggles and file locations
|
|
6
6
|
- `SOUL.md` — agent identity and working values
|
|
7
7
|
- `agent-operating-manual.md` — required session workflow
|
|
8
|
+
- `docs/` — Waypoint-managed project memory (architecture, decisions, debugging knowledge, durable plans)
|
|
8
9
|
- `agents/` — agent prompt files that optional Codex roles can read and follow
|
|
9
10
|
- `automations/` — optional automation source specs
|
|
10
11
|
- `context/` — generated session context bundle
|
|
@@ -20,7 +20,7 @@ Do not skip this sequence.
|
|
|
20
20
|
The repository should contain the context the next agent needs.
|
|
21
21
|
|
|
22
22
|
- `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
|
|
|
26
26
|
If something important lives only in your head or in the chat transcript, the repo is under-documented.
|
|
@@ -30,7 +30,7 @@ If something important lives only in your head or in the chat transcript, the re
|
|
|
30
30
|
- Read code before editing it.
|
|
31
31
|
- Follow the repo's documented patterns when they are healthy.
|
|
32
32
|
- Update `WORKSPACE.md` as live execution state when progress meaningfully changes.
|
|
33
|
-
- Update
|
|
33
|
+
- Update `.waypoint/docs/` when durable knowledge changes.
|
|
34
34
|
- Rebuild `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
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
version = 1
|
|
2
2
|
profile = "__PROFILE__"
|
|
3
3
|
workspace_file = "WORKSPACE.md"
|
|
4
|
-
docs_dir = "docs"
|
|
4
|
+
docs_dir = ".waypoint/docs"
|
|
5
5
|
docs_index_file = "DOCS_INDEX.md"
|
|
6
6
|
|
|
7
7
|
[features]
|
|
@@ -10,4 +10,3 @@ docs_index = true
|
|
|
10
10
|
roles = __ROLES__
|
|
11
11
|
rules = __RULES__
|
|
12
12
|
automations = __AUTOMATIONS__
|
|
13
|
-
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Waypoint Docs
|
|
2
|
+
|
|
3
|
+
This directory is Waypoint-managed project memory.
|
|
4
|
+
|
|
5
|
+
Put the durable context here that the next agent will need to continue the work:
|
|
6
|
+
|
|
7
|
+
- architecture
|
|
8
|
+
- decisions and tradeoffs
|
|
9
|
+
- integration notes
|
|
10
|
+
- invariants and constraints
|
|
11
|
+
- debugging knowledge
|
|
12
|
+
- active plans that should survive beyond one session
|
|
13
|
+
|
|
14
|
+
These are **project docs**, not Waypoint internals.
|
|
15
|
+
|
|
16
|
+
Every routable doc needs YAML frontmatter:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
---
|
|
20
|
+
summary: One-line description
|
|
21
|
+
read_when:
|
|
22
|
+
- task cue
|
|
23
|
+
---
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`DOCS_INDEX.md` is generated from the docs here.
|
|
27
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Opinionated rules for writing and changing Waypoint code so behavior stays explicit, strict, observable, and easy to evolve.
|
|
3
|
+
read_when:
|
|
4
|
+
- writing new code
|
|
5
|
+
- changing existing behavior
|
|
6
|
+
- introducing or removing configuration
|
|
7
|
+
- handling external input or external systems
|
|
8
|
+
- adding tests, logging, or state transitions
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Code Guide
|
|
12
|
+
|
|
13
|
+
Waypoint favors explicitness over convenience, correctness over compatibility theater, and deletion over accumulation. Code should make the system easier to reason about after the change, not merely pass today.
|
|
14
|
+
|
|
15
|
+
## 1. Compatibility is opt-in, not ambient
|
|
16
|
+
|
|
17
|
+
Do not preserve old behavior unless a user-facing requirement explicitly asks for it.
|
|
18
|
+
|
|
19
|
+
When replacing a path, remove the old one instead of leaving a shim, alias, translation layer, or silent compatibility branch. If compatibility must be preserved, document the exact contract being preserved and the planned removal condition.
|
|
20
|
+
|
|
21
|
+
Do not keep dead fields, dead parameters, dual formats, or migration-only logic "just in case". Every compatibility layer becomes part of the design whether intended or not.
|
|
22
|
+
|
|
23
|
+
## 2. Fail clearly, never quietly
|
|
24
|
+
|
|
25
|
+
Errors are part of the contract. Surface them early and with enough context to diagnose the cause.
|
|
26
|
+
|
|
27
|
+
Do not swallow errors, downgrade them to logs, or return partial success unless partial success is the explicit API. If an operation can fail in distinct ways, preserve those distinctions. Do not replace a specific failure with a generic one that destroys meaning.
|
|
28
|
+
|
|
29
|
+
Error messages should identify what failed, at which boundary, and why the system refused to proceed. They should not force readers to infer missing state from generic text.
|
|
30
|
+
|
|
31
|
+
## 3. No silent fallback paths
|
|
32
|
+
|
|
33
|
+
Fallbacks are allowed only when they are deliberate product behavior, not a coding reflex.
|
|
34
|
+
|
|
35
|
+
Do not silently retry with weaker behavior, alternate providers, cached data, inferred defaults, empty values, or best-effort modes unless the user asked for degraded operation and the degraded result remains truthful.
|
|
36
|
+
|
|
37
|
+
If degradation exists, it must be explicit in code, testable, and observable. Hidden fallback logic makes the system look healthy while it is already off-contract.
|
|
38
|
+
|
|
39
|
+
## 4. Validate at boundaries, not deep inside
|
|
40
|
+
|
|
41
|
+
Anything that crosses a boundary must be treated as untrusted: user input, config, environment, files, network responses, database reads, queue payloads, generated content, and data from other modules.
|
|
42
|
+
|
|
43
|
+
Validate structure, required fields, allowed values, and invariants at the boundary. Convert external data into a trusted internal shape once. Do not pass loosely-typed or half-validated data deeper into the system and hope downstream code copes.
|
|
44
|
+
|
|
45
|
+
Boundary validation should reject invalid data, not "normalize" it into something ambiguous.
|
|
46
|
+
|
|
47
|
+
## 5. Configuration must be strict
|
|
48
|
+
|
|
49
|
+
Missing or invalid configuration should stop the system at startup or at the feature boundary where it becomes required.
|
|
50
|
+
|
|
51
|
+
Do not hide absent configuration behind guessed defaults, environment-dependent behavior, or implicit no-op modes. Defaults are acceptable only when they are safe, intentional, and documented as part of the product contract.
|
|
52
|
+
|
|
53
|
+
Configuration should be centralized enough to audit. A reader should be able to tell which settings matter, what values are valid, and what happens when they are wrong.
|
|
54
|
+
|
|
55
|
+
## 6. Prefer direct code over speculative abstraction
|
|
56
|
+
|
|
57
|
+
Do not introduce abstractions for imagined future use. Add them only when multiple concrete cases already demand the same shape.
|
|
58
|
+
|
|
59
|
+
A small amount of duplication is cheaper than the wrong abstraction. Prefer code that exposes the current domain plainly over generic layers, plugin systems, factories, wrappers, or strategy trees created before the need is real.
|
|
60
|
+
|
|
61
|
+
Abstractions must remove real complexity, not relocate it. If a helper hides critical behavior, state changes, validation, or failure modes, it is making the system harder to read.
|
|
62
|
+
|
|
63
|
+
## 7. Make state and invariants explicit
|
|
64
|
+
|
|
65
|
+
State transitions should be visible in code. Readers should be able to answer: what states exist, what moves the system between them, and what must always be true.
|
|
66
|
+
|
|
67
|
+
Do not encode important transitions as scattered flag mutations, ordering assumptions, optional fields, or side effects hidden in utility calls. Avoid representations where invalid states are easy to create and hard to detect.
|
|
68
|
+
|
|
69
|
+
When a function changes state, make the transition obvious. When a module depends on an invariant, assert it at the boundary of the operation instead of relying on folklore.
|
|
70
|
+
|
|
71
|
+
## 8. Tests define behavior changes, not just regressions
|
|
72
|
+
|
|
73
|
+
Any behavior change must update tests to describe the new contract. If the old behavior mattered, remove or rewrite the old tests instead of making them weaker.
|
|
74
|
+
|
|
75
|
+
Test observable behavior and boundary cases, not implementation trivia. Cover failure modes, validation rules, configuration strictness, and any intentional degradation path. If a bug fix closes a previously possible bad state, add a test that proves the bad state is now rejected.
|
|
76
|
+
|
|
77
|
+
Do not merge code whose behavior changed without leaving behind executable evidence of the new rules.
|
|
78
|
+
|
|
79
|
+
## 9. Observability is part of correctness
|
|
80
|
+
|
|
81
|
+
Code is not complete if production failures cannot be understood from its signals.
|
|
82
|
+
|
|
83
|
+
Emit structured logs, metrics, or events at important boundaries and state transitions, especially around input rejection, external calls, retries, and degraded modes. Observability should explain which path executed and why.
|
|
84
|
+
|
|
85
|
+
Do not log noise to compensate for poor design. Prefer a small number of high-value signals tied to decisions, failures, and contract edges.
|
|
86
|
+
|
|
87
|
+
## 10. Optimize for future legibility
|
|
88
|
+
|
|
89
|
+
Write code for the next person who must change it under pressure.
|
|
90
|
+
|
|
91
|
+
Keep modules narrow in responsibility. Keep data flow obvious. Keep control flow boring. Prefer designs where the main path is easy to follow and unusual behavior is explicitly named.
|
|
92
|
+
|
|
93
|
+
When changing code, improve the shape around the change if needed. Do not leave behind half-migrated designs, obsolete branches, commented-out code, or placeholders for imagined follow-ups.
|
|
94
|
+
|
|
95
|
+
The best code is not code that can handle every possible future. It is code whose current truth is obvious, whose failures are visible, and whose wrong parts can be deleted without fear.
|
|
@@ -108,7 +108,7 @@ function walkDocs(projectRoot, currentDir, output) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
export function renderDocsIndex(projectRoot) {
|
|
111
|
-
const docsDir = path.join(projectRoot, "docs");
|
|
111
|
+
const docsDir = path.join(projectRoot, ".waypoint", "docs");
|
|
112
112
|
const entries = [];
|
|
113
113
|
|
|
114
114
|
if (existsSync(docsDir)) {
|
|
@@ -120,7 +120,7 @@ export function renderDocsIndex(projectRoot) {
|
|
|
120
120
|
"",
|
|
121
121
|
"Auto-generated by `.waypoint/scripts/build-docs-index.mjs`. Read matching docs before working on a task.",
|
|
122
122
|
"",
|
|
123
|
-
"## docs/",
|
|
123
|
+
"## .waypoint/docs/",
|
|
124
124
|
"",
|
|
125
125
|
];
|
|
126
126
|
|
|
@@ -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,6 +381,7 @@ 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",
|
|
@@ -171,4 +399,3 @@ function main() {
|
|
|
171
399
|
}
|
|
172
400
|
|
|
173
401
|
main();
|
|
174
|
-
|
|
@@ -15,7 +15,7 @@ This is mandatory, not optional. Do not skip the context refresh or skip files i
|
|
|
15
15
|
|
|
16
16
|
Working rules:
|
|
17
17
|
- Keep `WORKSPACE.md` current as the live execution state
|
|
18
|
-
- Update
|
|
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
|
|
21
21
|
<!-- waypoint:end -->
|
package/templates/docs/README.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Docs
|
|
2
|
-
|
|
3
|
-
Store durable project knowledge here.
|
|
4
|
-
|
|
5
|
-
Each document that should be routable by Codex needs YAML frontmatter:
|
|
6
|
-
|
|
7
|
-
```yaml
|
|
8
|
-
---
|
|
9
|
-
summary: One-line description
|
|
10
|
-
read_when:
|
|
11
|
-
- keyword or task cue
|
|
12
|
-
---
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
`DOCS_INDEX.md` is generated from the docs that include this frontmatter.
|
|
16
|
-
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
summary: Repository coding conventions — correctness, explicit error handling, maintainability, and testing discipline
|
|
3
|
-
read_when:
|
|
4
|
-
- writing code
|
|
5
|
-
- implementing a feature
|
|
6
|
-
- fixing a bug
|
|
7
|
-
- updating tests
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Code Guide
|
|
11
|
-
|
|
12
|
-
## Core rules
|
|
13
|
-
|
|
14
|
-
- Prefer explicit behavior over clever shortcuts.
|
|
15
|
-
- Keep changes scoped and reviewable.
|
|
16
|
-
- Update docs when durable behavior changes.
|
|
17
|
-
- Validate external data at boundaries.
|
|
18
|
-
- Do not swallow errors silently.
|
|
19
|
-
|
|
20
|
-
## Testing
|
|
21
|
-
|
|
22
|
-
- Run the smallest relevant verification first.
|
|
23
|
-
- Add or update tests when behavior changes.
|
|
24
|
-
- If tests cannot be run, say exactly why.
|
|
25
|
-
|
|
26
|
-
## Maintainability
|
|
27
|
-
|
|
28
|
-
- Reuse existing patterns where they are healthy.
|
|
29
|
-
- Do not introduce hidden fallback behavior without making it visible.
|
|
30
|
-
- Prefer clear names and clear module boundaries over compact code.
|
|
31
|
-
|