waypoint-codex 0.8.0 → 0.9.0
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 +17 -24
- package/dist/src/cli.js +15 -36
- package/dist/src/core.js +16 -323
- package/dist/src/templates.js +1 -4
- package/dist/src/upgrade.js +98 -8
- package/package.json +1 -1
- package/templates/.agents/skills/backend-context-interview/SKILL.md +70 -0
- package/templates/.agents/skills/backend-ship-audit/SKILL.md +221 -0
- package/templates/.agents/skills/backend-ship-audit/agents/openai.yaml +3 -0
- package/templates/.agents/skills/backend-ship-audit/references/audit-framework.md +228 -0
- package/templates/.agents/skills/backend-ship-audit/references/report-template.md +92 -0
- package/templates/.agents/skills/frontend-context-interview/SKILL.md +60 -0
- package/templates/.agents/skills/frontend-ship-audit/SKILL.md +87 -0
- package/templates/.agents/skills/frontend-ship-audit/agents/openai.yaml +3 -0
- package/templates/.agents/skills/frontend-ship-audit/references/guidance-file-updates.md +57 -0
- package/templates/.agents/skills/frontend-ship-audit/references/report-template.md +51 -0
- package/templates/.agents/skills/frontend-ship-audit/references/review-framework.md +83 -0
- package/templates/.agents/skills/frontend-ship-audit/scripts/create_frontend_audit.py +81 -0
- package/templates/.codex/agents/plan-reviewer.toml +1 -2
- package/templates/.waypoint/README.md +2 -5
- package/templates/.waypoint/SOUL.md +1 -1
- package/templates/.waypoint/agent-operating-manual.md +12 -9
- package/templates/.waypoint/agents/code-health-reviewer.md +10 -1
- package/templates/.waypoint/agents/plan-reviewer.md +7 -2
- package/templates/.waypoint/config.toml +0 -3
- package/templates/managed-agents-block.md +38 -2
- package/templates/.waypoint/automations/README.md +0 -18
- package/templates/.waypoint/automations/docs-garden.toml +0 -7
- package/templates/.waypoint/automations/repo-health.toml +0 -8
- package/templates/.waypoint/rules/README.md +0 -6
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Waypoint scaffolds a Codex-friendly repo structure built around a few core piece
|
|
|
28
28
|
- `.waypoint/TRACKS_INDEX.md` for tracker routing
|
|
29
29
|
- `.waypoint/context/` for generated startup context
|
|
30
30
|
- `.agents/skills/` for repo-local workflows like planning, tracking, audits, and QA
|
|
31
|
+
- `.codex/` for the default reviewer-agent pack
|
|
31
32
|
|
|
32
33
|
The philosophy is simple:
|
|
33
34
|
|
|
@@ -66,7 +67,7 @@ npx waypoint-codex@latest --help
|
|
|
66
67
|
Inside the repo you want to prepare for Codex:
|
|
67
68
|
|
|
68
69
|
```bash
|
|
69
|
-
waypoint init
|
|
70
|
+
waypoint init
|
|
70
71
|
waypoint doctor
|
|
71
72
|
```
|
|
72
73
|
|
|
@@ -75,6 +76,9 @@ That gives you a repo that looks roughly like this:
|
|
|
75
76
|
```text
|
|
76
77
|
repo/
|
|
77
78
|
├── AGENTS.md
|
|
79
|
+
├── .codex/
|
|
80
|
+
│ ├── agents/
|
|
81
|
+
│ └── config.toml
|
|
78
82
|
├── .agents/
|
|
79
83
|
│ └── skills/
|
|
80
84
|
└── .waypoint/
|
|
@@ -98,32 +102,29 @@ From there, start your Codex session in the repo and follow the generated bootst
|
|
|
98
102
|
waypoint init
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
By default, `waypoint init` updates the global CLI to the latest published `waypoint-codex` first, then scaffolds with that fresh version. It also installs the reviewer-agent pack by default. If you want to scaffold with the currently installed binary instead, use:
|
|
102
106
|
|
|
103
107
|
```bash
|
|
104
|
-
waypoint init --
|
|
108
|
+
waypoint init --skip-cli-update
|
|
105
109
|
```
|
|
106
110
|
|
|
107
111
|
### App-friendly profile
|
|
108
112
|
|
|
109
113
|
```bash
|
|
110
|
-
waypoint init --app-friendly
|
|
114
|
+
waypoint init --app-friendly
|
|
111
115
|
```
|
|
112
116
|
|
|
113
117
|
Flags you can combine:
|
|
114
118
|
|
|
115
119
|
- `--app-friendly`
|
|
116
|
-
- `--
|
|
117
|
-
- `--with-rules`
|
|
118
|
-
- `--with-automations`
|
|
120
|
+
- `--skip-cli-update`
|
|
119
121
|
|
|
120
122
|
## Main commands
|
|
121
123
|
|
|
122
|
-
- `waypoint init` — scaffold or refresh the repo
|
|
124
|
+
- `waypoint init` — update the CLI to latest by default, then scaffold or refresh the repo
|
|
123
125
|
- `waypoint doctor` — validate health and report drift
|
|
124
|
-
- `waypoint sync` — rebuild the docs
|
|
126
|
+
- `waypoint sync` — rebuild the docs and tracker indexes
|
|
125
127
|
- `waypoint upgrade` — update the CLI and refresh the current repo using its saved config
|
|
126
|
-
- `waypoint import-legacy` — analyze an older repo layout and produce an adoption report
|
|
127
128
|
|
|
128
129
|
## Built-in skills
|
|
129
130
|
|
|
@@ -134,21 +135,24 @@ Waypoint ships a strong default skill pack for real coding work:
|
|
|
134
135
|
- `docs-sync`
|
|
135
136
|
- `code-guide-audit`
|
|
136
137
|
- `break-it-qa`
|
|
138
|
+
- `frontend-ship-audit`
|
|
139
|
+
- `backend-ship-audit`
|
|
137
140
|
- `workspace-compress`
|
|
138
141
|
- `pre-pr-hygiene`
|
|
139
142
|
- `pr-review`
|
|
140
143
|
|
|
141
144
|
These are repo-local, so the workflow travels with the project.
|
|
145
|
+
`break-it-qa`, `frontend-ship-audit`, and `backend-ship-audit` are user-invoked audit skills, not default autonomous agent steps.
|
|
142
146
|
|
|
143
|
-
##
|
|
147
|
+
## Reviewer agents
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
Waypoint scaffolds these reviewer agents by default:
|
|
146
150
|
|
|
147
151
|
- `code-health-reviewer`
|
|
148
152
|
- `code-reviewer`
|
|
149
153
|
- `plan-reviewer`
|
|
150
154
|
|
|
151
|
-
The intended workflow is
|
|
155
|
+
The intended workflow is closeout-based: run `code-reviewer` before considering any non-trivial implementation slice complete, and run `code-health-reviewer` before considering medium or large changes complete, especially when they add structure, duplicate logic, or introduce new abstractions. If both apply, run them in parallel. A recent self-authored commit is the preferred scope anchor when one cleanly represents the slice, but it is not the only valid trigger.
|
|
152
156
|
|
|
153
157
|
## What makes it different
|
|
154
158
|
|
|
@@ -178,22 +182,11 @@ If you only want to update the CLI:
|
|
|
178
182
|
waypoint upgrade --skip-repo-refresh
|
|
179
183
|
```
|
|
180
184
|
|
|
181
|
-
## Importing an existing repo
|
|
182
|
-
|
|
183
|
-
If you already have an older assistant setup or repo-memory system:
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
waypoint import-legacy /path/to/source-repo /path/to/new-repo --init-target
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
This generates an adoption report and helps separate durable docs from old runtime-specific scaffolding.
|
|
190
|
-
|
|
191
185
|
## Learn more
|
|
192
186
|
|
|
193
187
|
- [Overview](docs/overview.md)
|
|
194
188
|
- [Architecture](docs/architecture.md)
|
|
195
189
|
- [Upgrading](docs/upgrading.md)
|
|
196
|
-
- [Importing Existing Repositories](docs/importing-existing-repos.md)
|
|
197
190
|
- [Releasing](docs/releasing.md)
|
|
198
191
|
|
|
199
192
|
## License
|
package/dist/src/cli.js
CHANGED
|
@@ -4,8 +4,8 @@ import { readFileSync } from "node:fs";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
|
-
import { doctorRepository,
|
|
8
|
-
import { upgradeWaypoint } from "./upgrade.js";
|
|
7
|
+
import { doctorRepository, initRepository, loadWaypointConfig, syncRepository } from "./core.js";
|
|
8
|
+
import { maybeUpgradeWaypointBeforeInit, upgradeWaypoint } from "./upgrade.js";
|
|
9
9
|
const VERSION = JSON.parse(readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json"), "utf8")).version;
|
|
10
10
|
function resolveRepo(input) {
|
|
11
11
|
return path.resolve(input ?? ".");
|
|
@@ -27,11 +27,10 @@ function printHelp() {
|
|
|
27
27
|
console.log(`usage: waypoint [--version] <command> [options]
|
|
28
28
|
|
|
29
29
|
Commands:
|
|
30
|
-
init Initialize a repository with Waypoint scaffolding
|
|
30
|
+
init Initialize a repository with Waypoint scaffolding (auto-updates CLI unless skipped)
|
|
31
31
|
doctor Validate repository health and report drift
|
|
32
|
-
sync Rebuild docs
|
|
32
|
+
sync Rebuild docs and tracker indexes
|
|
33
33
|
upgrade Update the global Waypoint CLI and refresh this repo using existing config
|
|
34
|
-
import-legacy Analyze a legacy repository layout and produce a Waypoint adoption report
|
|
35
34
|
`);
|
|
36
35
|
}
|
|
37
36
|
async function main() {
|
|
@@ -50,18 +49,23 @@ async function main() {
|
|
|
50
49
|
args: argv.slice(1),
|
|
51
50
|
options: {
|
|
52
51
|
"app-friendly": { type: "boolean", default: false },
|
|
53
|
-
"
|
|
54
|
-
"with-rules": { type: "boolean", default: false },
|
|
55
|
-
"with-automations": { type: "boolean", default: false }
|
|
52
|
+
"skip-cli-update": { type: "boolean", default: false }
|
|
56
53
|
},
|
|
57
54
|
allowPositionals: true
|
|
58
55
|
});
|
|
59
56
|
const projectRoot = resolveRepo(positionals[0]);
|
|
57
|
+
if (!values["skip-cli-update"]) {
|
|
58
|
+
const status = maybeUpgradeWaypointBeforeInit({
|
|
59
|
+
currentVersion: VERSION,
|
|
60
|
+
cliEntry: process.argv[1] ? path.resolve(process.argv[1]) : fileURLToPath(import.meta.url),
|
|
61
|
+
initArgs: argv.slice(1),
|
|
62
|
+
});
|
|
63
|
+
if (status !== null) {
|
|
64
|
+
return status;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
60
67
|
const results = initRepository(projectRoot, {
|
|
61
68
|
profile: values["app-friendly"] ? "app-friendly" : "universal",
|
|
62
|
-
withRoles: values["with-roles"],
|
|
63
|
-
withRules: values["with-rules"],
|
|
64
|
-
withAutomations: values["with-automations"]
|
|
65
69
|
});
|
|
66
70
|
for (const line of results) {
|
|
67
71
|
console.log(`- ${line}`);
|
|
@@ -105,31 +109,6 @@ async function main() {
|
|
|
105
109
|
}
|
|
106
110
|
return 0;
|
|
107
111
|
}
|
|
108
|
-
if (command === "import-legacy") {
|
|
109
|
-
const { values, positionals } = parseArgs({
|
|
110
|
-
args: argv.slice(1),
|
|
111
|
-
options: {
|
|
112
|
-
"init-target": { type: "boolean", default: false }
|
|
113
|
-
},
|
|
114
|
-
allowPositionals: true
|
|
115
|
-
});
|
|
116
|
-
if (positionals.length === 0) {
|
|
117
|
-
console.error("import-legacy requires a source repository path.");
|
|
118
|
-
return 2;
|
|
119
|
-
}
|
|
120
|
-
const sourceRepo = resolveRepo(positionals[0]);
|
|
121
|
-
const targetRepo = positionals[1] ? resolveRepo(positionals[1]) : undefined;
|
|
122
|
-
const result = importLegacyRepo(sourceRepo, targetRepo, {
|
|
123
|
-
initTarget: values["init-target"]
|
|
124
|
-
});
|
|
125
|
-
for (const line of result.actions) {
|
|
126
|
-
console.log(`- ${line}`);
|
|
127
|
-
}
|
|
128
|
-
if (!targetRepo) {
|
|
129
|
-
console.log(result.report);
|
|
130
|
-
}
|
|
131
|
-
return 0;
|
|
132
|
-
}
|
|
133
112
|
if (command === "upgrade") {
|
|
134
113
|
const { values, positionals } = parseArgs({
|
|
135
114
|
args: argv.slice(1),
|
package/dist/src/core.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
4
2
|
import path from "node:path";
|
|
5
3
|
import * as TOML from "@iarna/toml";
|
|
6
4
|
import { renderDocsIndex } from "./docs-index.js";
|
|
@@ -12,8 +10,6 @@ const DEFAULT_DOCS_INDEX = ".waypoint/DOCS_INDEX.md";
|
|
|
12
10
|
const DEFAULT_TRACK_DIR = ".waypoint/track";
|
|
13
11
|
const DEFAULT_TRACKS_INDEX = ".waypoint/TRACKS_INDEX.md";
|
|
14
12
|
const DEFAULT_WORKSPACE = ".waypoint/WORKSPACE.md";
|
|
15
|
-
const STATE_DIR = ".waypoint/state";
|
|
16
|
-
const SYNC_RECORDS_FILE = ".waypoint/state/sync-records.json";
|
|
17
13
|
const TIMESTAMPED_WORKSPACE_SECTIONS = new Set([
|
|
18
14
|
"## Active Trackers",
|
|
19
15
|
"## Current State",
|
|
@@ -102,8 +98,6 @@ function scaffoldSkills(projectRoot) {
|
|
|
102
98
|
}
|
|
103
99
|
function scaffoldWaypointOptionalTemplates(projectRoot) {
|
|
104
100
|
copyTemplateTree(templatePath(".waypoint/agents"), path.join(projectRoot, ".waypoint/agents"));
|
|
105
|
-
copyTemplateTree(templatePath(".waypoint/automations"), path.join(projectRoot, ".waypoint/automations"));
|
|
106
|
-
copyTemplateTree(templatePath(".waypoint/rules"), path.join(projectRoot, ".waypoint/rules"));
|
|
107
101
|
copyTemplateTree(templatePath(".waypoint/scripts"), path.join(projectRoot, ".waypoint/scripts"));
|
|
108
102
|
copyTemplateTree(templatePath(".waypoint/track"), path.join(projectRoot, ".waypoint/track"));
|
|
109
103
|
}
|
|
@@ -135,21 +129,18 @@ export function initRepository(projectRoot, options) {
|
|
|
135
129
|
".codex/agents/reviewer.toml",
|
|
136
130
|
".codex/agents/architect.toml",
|
|
137
131
|
".codex/agents/implementer.toml",
|
|
132
|
+
".waypoint/automations",
|
|
133
|
+
".waypoint/rules",
|
|
134
|
+
".waypoint/state",
|
|
138
135
|
]) {
|
|
139
136
|
removePathIfExists(path.join(projectRoot, deprecatedPath));
|
|
140
137
|
}
|
|
141
|
-
ensureDir(path.join(projectRoot, ".waypoint/automations"));
|
|
142
|
-
ensureDir(path.join(projectRoot, ".waypoint/rules"));
|
|
143
|
-
ensureDir(path.join(projectRoot, STATE_DIR));
|
|
144
138
|
writeText(path.join(projectRoot, ".waypoint/README.md"), readTemplate(".waypoint/README.md"));
|
|
145
139
|
writeText(path.join(projectRoot, ".waypoint/SOUL.md"), readTemplate(".waypoint/SOUL.md"));
|
|
146
140
|
writeText(path.join(projectRoot, ".waypoint/agent-operating-manual.md"), readTemplate(".waypoint/agent-operating-manual.md"));
|
|
147
141
|
scaffoldWaypointOptionalTemplates(projectRoot);
|
|
148
142
|
writeText(path.join(projectRoot, DEFAULT_CONFIG_PATH), renderWaypointConfig({
|
|
149
143
|
profile: options.profile,
|
|
150
|
-
roles: options.withRoles,
|
|
151
|
-
rules: options.withRules,
|
|
152
|
-
automations: options.withAutomations,
|
|
153
144
|
}));
|
|
154
145
|
writeIfMissing(path.join(projectRoot, DEFAULT_WORKSPACE), readTemplate("WORKSPACE.md"));
|
|
155
146
|
ensureDir(path.join(projectRoot, DEFAULT_DOCS_DIR));
|
|
@@ -158,9 +149,7 @@ export function initRepository(projectRoot, options) {
|
|
|
158
149
|
writeIfMissing(path.join(projectRoot, ".waypoint/docs/code-guide.md"), readTemplate(".waypoint/docs/code-guide.md"));
|
|
159
150
|
upsertManagedBlock(path.join(projectRoot, "AGENTS.md"), readTemplate("managed-agents-block.md"));
|
|
160
151
|
scaffoldSkills(projectRoot);
|
|
161
|
-
|
|
162
|
-
scaffoldOptionalCodex(projectRoot);
|
|
163
|
-
}
|
|
152
|
+
scaffoldOptionalCodex(projectRoot);
|
|
164
153
|
appendGitignoreSnippet(projectRoot);
|
|
165
154
|
const docsIndex = renderDocsIndex(projectRoot, path.join(projectRoot, DEFAULT_DOCS_DIR));
|
|
166
155
|
const tracksIndex = renderTracksIndex(projectRoot, path.join(projectRoot, DEFAULT_TRACK_DIR));
|
|
@@ -171,6 +160,7 @@ export function initRepository(projectRoot, options) {
|
|
|
171
160
|
"Installed managed AGENTS block",
|
|
172
161
|
"Created .waypoint/WORKSPACE.md, .waypoint/docs/, and .waypoint/track/ scaffold",
|
|
173
162
|
"Installed repo-local Waypoint skills",
|
|
163
|
+
"Installed reviewer agents and project Codex config",
|
|
174
164
|
"Generated .waypoint/DOCS_INDEX.md and .waypoint/TRACKS_INDEX.md",
|
|
175
165
|
];
|
|
176
166
|
}
|
|
@@ -181,27 +171,6 @@ export function loadWaypointConfig(projectRoot) {
|
|
|
181
171
|
}
|
|
182
172
|
return TOML.parse(readFileSync(configPath, "utf8"));
|
|
183
173
|
}
|
|
184
|
-
function loadSyncRecords(projectRoot) {
|
|
185
|
-
const syncPath = path.join(projectRoot, SYNC_RECORDS_FILE);
|
|
186
|
-
if (!existsSync(syncPath)) {
|
|
187
|
-
return {};
|
|
188
|
-
}
|
|
189
|
-
try {
|
|
190
|
-
return JSON.parse(readFileSync(syncPath, "utf8"));
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
return {};
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function saveSyncRecords(projectRoot, records) {
|
|
197
|
-
writeText(path.join(projectRoot, SYNC_RECORDS_FILE), `${JSON.stringify(records, null, 2)}\n`);
|
|
198
|
-
}
|
|
199
|
-
function hashFile(filePath) {
|
|
200
|
-
return createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
|
201
|
-
}
|
|
202
|
-
function codexHome() {
|
|
203
|
-
return process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
|
|
204
|
-
}
|
|
205
174
|
function findWorkspaceTimestampViolations(workspaceText) {
|
|
206
175
|
let currentSection = "";
|
|
207
176
|
const violations = new Set();
|
|
@@ -223,111 +192,6 @@ function findWorkspaceTimestampViolations(workspaceText) {
|
|
|
223
192
|
}
|
|
224
193
|
return [...violations];
|
|
225
194
|
}
|
|
226
|
-
function renderCodexAutomation(spec, cwd) {
|
|
227
|
-
const now = Date.now();
|
|
228
|
-
const rrule = spec.rrule?.startsWith("RRULE:") ? spec.rrule : `RRULE:${spec.rrule}`;
|
|
229
|
-
return [
|
|
230
|
-
"version = 1",
|
|
231
|
-
`id = ${JSON.stringify(spec.id)}`,
|
|
232
|
-
`name = ${JSON.stringify(spec.name)}`,
|
|
233
|
-
`prompt = ${JSON.stringify(spec.prompt)}`,
|
|
234
|
-
`status = ${JSON.stringify(spec.status ?? "ACTIVE")}`,
|
|
235
|
-
`rrule = ${JSON.stringify(rrule)}`,
|
|
236
|
-
`execution_environment = ${JSON.stringify(spec.execution_environment ?? "worktree")}`,
|
|
237
|
-
`cwds = ${JSON.stringify(spec.cwds ?? [cwd])}`,
|
|
238
|
-
`created_at = ${now}`,
|
|
239
|
-
`updated_at = ${now}`,
|
|
240
|
-
].join("\n") + "\n";
|
|
241
|
-
}
|
|
242
|
-
function isKebabCase(value) {
|
|
243
|
-
return /^[a-z0-9-]+$/.test(value);
|
|
244
|
-
}
|
|
245
|
-
function validateAutomationSpecFile(filePath) {
|
|
246
|
-
const errors = [];
|
|
247
|
-
let parsed;
|
|
248
|
-
try {
|
|
249
|
-
parsed = TOML.parse(readFileSync(filePath, "utf8"));
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
return [`invalid TOML: ${error instanceof Error ? error.message : String(error)}`];
|
|
253
|
-
}
|
|
254
|
-
for (const key of ["id", "name", "prompt", "rrule"]) {
|
|
255
|
-
if (!parsed[key]) {
|
|
256
|
-
errors.push(`missing required key \`${key}\``);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (parsed.id && !isKebabCase(parsed.id)) {
|
|
260
|
-
errors.push("automation id must use lowercase kebab-case");
|
|
261
|
-
}
|
|
262
|
-
return errors;
|
|
263
|
-
}
|
|
264
|
-
function syncAutomations(projectRoot) {
|
|
265
|
-
const sourceDir = path.join(projectRoot, ".waypoint/automations");
|
|
266
|
-
if (!existsSync(sourceDir)) {
|
|
267
|
-
return [];
|
|
268
|
-
}
|
|
269
|
-
const targetRoot = path.join(codexHome(), "automations");
|
|
270
|
-
ensureDir(targetRoot);
|
|
271
|
-
const records = loadSyncRecords(projectRoot);
|
|
272
|
-
const results = [];
|
|
273
|
-
for (const entry of readdirSync(sourceDir)) {
|
|
274
|
-
if (!entry.endsWith(".toml")) {
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
const sourcePath = path.join(sourceDir, entry);
|
|
278
|
-
const errors = validateAutomationSpecFile(sourcePath);
|
|
279
|
-
if (errors.length > 0) {
|
|
280
|
-
results.push(`Skipped ${entry}: ${errors.join("; ")}`);
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
const spec = TOML.parse(readFileSync(sourcePath, "utf8"));
|
|
284
|
-
if (spec.enabled === false) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
const targetDir = path.join(targetRoot, spec.id);
|
|
288
|
-
const targetPath = path.join(targetDir, "automation.toml");
|
|
289
|
-
ensureDir(targetDir);
|
|
290
|
-
writeText(targetPath, renderCodexAutomation(spec, projectRoot));
|
|
291
|
-
records[sourcePath] = {
|
|
292
|
-
artifact_type: "automation",
|
|
293
|
-
source_path: sourcePath,
|
|
294
|
-
target_path: targetPath,
|
|
295
|
-
source_hash: hashFile(sourcePath),
|
|
296
|
-
target_hash: hashFile(targetPath),
|
|
297
|
-
};
|
|
298
|
-
results.push(`Synced automation \`${spec.id}\``);
|
|
299
|
-
}
|
|
300
|
-
saveSyncRecords(projectRoot, records);
|
|
301
|
-
return results;
|
|
302
|
-
}
|
|
303
|
-
function syncRules(projectRoot) {
|
|
304
|
-
const sourceDir = path.join(projectRoot, ".waypoint/rules");
|
|
305
|
-
if (!existsSync(sourceDir)) {
|
|
306
|
-
return [];
|
|
307
|
-
}
|
|
308
|
-
const targetRoot = path.join(codexHome(), "rules");
|
|
309
|
-
ensureDir(targetRoot);
|
|
310
|
-
const records = loadSyncRecords(projectRoot);
|
|
311
|
-
const results = [];
|
|
312
|
-
for (const entry of readdirSync(sourceDir)) {
|
|
313
|
-
if (!entry.endsWith(".rules")) {
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
const sourcePath = path.join(sourceDir, entry);
|
|
317
|
-
const targetPath = path.join(targetRoot, `waypoint-${entry}`);
|
|
318
|
-
copyFileSync(sourcePath, targetPath);
|
|
319
|
-
records[sourcePath] = {
|
|
320
|
-
artifact_type: "rules",
|
|
321
|
-
source_path: sourcePath,
|
|
322
|
-
target_path: targetPath,
|
|
323
|
-
source_hash: hashFile(sourcePath),
|
|
324
|
-
target_hash: hashFile(targetPath),
|
|
325
|
-
};
|
|
326
|
-
results.push(`Synced rules \`${entry}\``);
|
|
327
|
-
}
|
|
328
|
-
saveSyncRecords(projectRoot, records);
|
|
329
|
-
return results;
|
|
330
|
-
}
|
|
331
195
|
export function doctorRepository(projectRoot) {
|
|
332
196
|
const findings = [];
|
|
333
197
|
const config = loadWaypointConfig(projectRoot);
|
|
@@ -532,55 +396,15 @@ export function doctorRepository(projectRoot) {
|
|
|
532
396
|
});
|
|
533
397
|
}
|
|
534
398
|
}
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const filePath = path.join(automationDir, entry);
|
|
545
|
-
const errors = validateAutomationSpecFile(filePath);
|
|
546
|
-
for (const error of errors) {
|
|
547
|
-
findings.push({
|
|
548
|
-
severity: "error",
|
|
549
|
-
category: "automations",
|
|
550
|
-
message: `${path.relative(projectRoot, filePath)}: ${error}`,
|
|
551
|
-
remediation: "Fix the automation spec and rerun `waypoint sync`.",
|
|
552
|
-
paths: [filePath],
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
if (errors.length === 0) {
|
|
556
|
-
const spec = TOML.parse(readFileSync(filePath, "utf8"));
|
|
557
|
-
if (spec.enabled === false) {
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (errors.length === 0 && !records[filePath]) {
|
|
562
|
-
findings.push({
|
|
563
|
-
severity: "info",
|
|
564
|
-
category: "automations",
|
|
565
|
-
message: `Automation \`${path.basename(entry, ".toml")}\` has not been synced.`,
|
|
566
|
-
remediation: "Run `waypoint sync` to install it into Codex home.",
|
|
567
|
-
paths: [filePath],
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
if (featureMap.roles) {
|
|
574
|
-
const codexConfigPath = path.join(projectRoot, ".codex/config.toml");
|
|
575
|
-
if (!existsSync(codexConfigPath)) {
|
|
576
|
-
findings.push({
|
|
577
|
-
severity: "warn",
|
|
578
|
-
category: "roles",
|
|
579
|
-
message: "Role support is enabled but .codex/config.toml is missing.",
|
|
580
|
-
remediation: "Run `waypoint init --with-roles` or create the project Codex config files.",
|
|
581
|
-
paths: [codexConfigPath],
|
|
582
|
-
});
|
|
583
|
-
}
|
|
399
|
+
const codexConfigPath = path.join(projectRoot, ".codex/config.toml");
|
|
400
|
+
if (!existsSync(codexConfigPath)) {
|
|
401
|
+
findings.push({
|
|
402
|
+
severity: "warn",
|
|
403
|
+
category: "roles",
|
|
404
|
+
message: "Reviewer agent config is missing from .codex/config.toml.",
|
|
405
|
+
remediation: "Run `waypoint init` or create the project Codex config files.",
|
|
406
|
+
paths: [codexConfigPath],
|
|
407
|
+
});
|
|
584
408
|
}
|
|
585
409
|
return findings;
|
|
586
410
|
}
|
|
@@ -594,136 +418,5 @@ export function syncRepository(projectRoot) {
|
|
|
594
418
|
const tracksIndex = renderTracksIndex(projectRoot, trackDir);
|
|
595
419
|
writeText(docsIndexPath, `${docsIndex.content}\n`);
|
|
596
420
|
writeText(tracksIndexPath, `${tracksIndex.content}\n`);
|
|
597
|
-
|
|
598
|
-
const featureMap = config.features ?? {};
|
|
599
|
-
if (featureMap.rules) {
|
|
600
|
-
results.push(...syncRules(projectRoot));
|
|
601
|
-
}
|
|
602
|
-
if (featureMap.automations) {
|
|
603
|
-
results.push(...syncAutomations(projectRoot));
|
|
604
|
-
}
|
|
605
|
-
return results;
|
|
606
|
-
}
|
|
607
|
-
export function importLegacyRepo(sourceRepo, targetRepo, options = {}) {
|
|
608
|
-
const sourceDocsDir = path.join(sourceRepo, ".meridian/docs");
|
|
609
|
-
const sourceSkillsDir = path.join(sourceRepo, "skills");
|
|
610
|
-
const sourceCommandsDir = path.join(sourceRepo, "commands");
|
|
611
|
-
const sourceAgentsDir = path.join(sourceRepo, "agents");
|
|
612
|
-
const sourceHooksPath = path.join(sourceRepo, "hooks/hooks.json");
|
|
613
|
-
const sourceScriptsDir = path.join(sourceRepo, "scripts");
|
|
614
|
-
const portableDocs = existsSync(sourceDocsDir)
|
|
615
|
-
? readdirSync(sourceDocsDir).filter((entry) => entry.endsWith(".md")).sort()
|
|
616
|
-
: [];
|
|
617
|
-
const portableSkills = existsSync(sourceSkillsDir)
|
|
618
|
-
? readdirSync(sourceSkillsDir)
|
|
619
|
-
.map((entry) => path.join("skills", entry, "SKILL.md"))
|
|
620
|
-
.filter((relPath) => existsSync(path.join(sourceRepo, relPath)))
|
|
621
|
-
.sort()
|
|
622
|
-
: [];
|
|
623
|
-
const portableCommands = existsSync(sourceCommandsDir)
|
|
624
|
-
? readdirSync(sourceCommandsDir)
|
|
625
|
-
.filter((entry) => entry.endsWith(".md"))
|
|
626
|
-
.map((entry) => path.join("commands", entry))
|
|
627
|
-
.sort()
|
|
628
|
-
: [];
|
|
629
|
-
const agentFiles = existsSync(sourceAgentsDir)
|
|
630
|
-
? readdirSync(sourceAgentsDir)
|
|
631
|
-
.filter((entry) => entry.endsWith(".md"))
|
|
632
|
-
.map((entry) => path.join("agents", entry))
|
|
633
|
-
.sort()
|
|
634
|
-
: [];
|
|
635
|
-
const hookFiles = existsSync(sourceHooksPath) ? [path.join("hooks", "hooks.json")] : [];
|
|
636
|
-
const scriptFiles = existsSync(sourceScriptsDir)
|
|
637
|
-
? collectFiles(sourceScriptsDir)
|
|
638
|
-
.filter((filePath) => filePath.endsWith(".py"))
|
|
639
|
-
.map((filePath) => path.relative(sourceRepo, filePath))
|
|
640
|
-
.sort()
|
|
641
|
-
: [];
|
|
642
|
-
const actions = [];
|
|
643
|
-
if (targetRepo && options.initTarget) {
|
|
644
|
-
actions.push(...initRepository(targetRepo, {
|
|
645
|
-
profile: "universal",
|
|
646
|
-
withRoles: false,
|
|
647
|
-
withRules: false,
|
|
648
|
-
withAutomations: false,
|
|
649
|
-
}));
|
|
650
|
-
}
|
|
651
|
-
if (targetRepo) {
|
|
652
|
-
const importDir = path.join(targetRepo, ".waypoint/docs/legacy-import");
|
|
653
|
-
ensureDir(importDir);
|
|
654
|
-
for (const docName of portableDocs) {
|
|
655
|
-
copyFileSync(path.join(sourceDocsDir, docName), path.join(importDir, docName));
|
|
656
|
-
}
|
|
657
|
-
if (portableDocs.length > 0) {
|
|
658
|
-
actions.push(`Copied ${portableDocs.length} legacy docs into ${importDir}`);
|
|
659
|
-
}
|
|
660
|
-
const docsIndex = renderDocsIndex(targetRepo, path.join(targetRepo, DEFAULT_DOCS_DIR));
|
|
661
|
-
writeText(path.join(targetRepo, DEFAULT_DOCS_INDEX), `${docsIndex.content}\n`);
|
|
662
|
-
}
|
|
663
|
-
const report = [
|
|
664
|
-
"# Legacy Repository Adoption Report",
|
|
665
|
-
"",
|
|
666
|
-
`Source: \`${sourceRepo}\``,
|
|
667
|
-
"",
|
|
668
|
-
"## Portable as-is or with light rewriting",
|
|
669
|
-
"",
|
|
670
|
-
`- Docs: ${portableDocs.length}`,
|
|
671
|
-
`- Skills: ${portableSkills.length}`,
|
|
672
|
-
`- Commands/prompts worth reviewing for skill conversion: ${portableCommands.length}`,
|
|
673
|
-
"",
|
|
674
|
-
"### Docs",
|
|
675
|
-
...(portableDocs.length > 0
|
|
676
|
-
? portableDocs.map((entry) => `- \`.meridian/docs/${entry}\``)
|
|
677
|
-
: ["- None"]),
|
|
678
|
-
"",
|
|
679
|
-
"### Skills",
|
|
680
|
-
...(portableSkills.length > 0 ? portableSkills.map((entry) => `- \`${entry}\``) : ["- None"]),
|
|
681
|
-
"",
|
|
682
|
-
"## Replace with explicit Waypoint patterns",
|
|
683
|
-
"",
|
|
684
|
-
"- hook-based session injection -> AGENTS routing, context generation, repo-local skills, doctor/sync",
|
|
685
|
-
"- hidden stop-hook policing -> advisory workflow skills and visible repo state",
|
|
686
|
-
"- transcript learners -> maintenance skills and optional app automations",
|
|
687
|
-
"- opaque reviewer plumbing -> optional Codex roles where actually useful",
|
|
688
|
-
"",
|
|
689
|
-
"## Legacy machinery to drop",
|
|
690
|
-
"",
|
|
691
|
-
`- Hook registration files: ${hookFiles.length}`,
|
|
692
|
-
`- Hook/runtime scripts: ${scriptFiles.length}`,
|
|
693
|
-
"",
|
|
694
|
-
"### Hook/runtime files",
|
|
695
|
-
...((hookFiles.length + scriptFiles.length) > 0
|
|
696
|
-
? [...hookFiles, ...scriptFiles].map((entry) => `- \`${entry}\``)
|
|
697
|
-
: ["- None"]),
|
|
698
|
-
"",
|
|
699
|
-
"## Agent files to reinterpret, not port literally",
|
|
700
|
-
"",
|
|
701
|
-
...(agentFiles.length > 0 ? agentFiles.map((entry) => `- \`${entry}\``) : ["- None"]),
|
|
702
|
-
"",
|
|
703
|
-
"## Notes",
|
|
704
|
-
"",
|
|
705
|
-
"- The strongest reusable assets are methodology, docs patterns, and review/planning discipline.",
|
|
706
|
-
"- The weakest portability surface is hook-dependent session machinery and transcript-coupled automation.",
|
|
707
|
-
"",
|
|
708
|
-
].join("\n");
|
|
709
|
-
if (targetRepo) {
|
|
710
|
-
const reportPath = path.join(targetRepo, ".waypoint/IMPORT_LEGACY.md");
|
|
711
|
-
writeText(reportPath, report);
|
|
712
|
-
actions.push(`Wrote migration report to ${reportPath}`);
|
|
713
|
-
}
|
|
714
|
-
return { report, actions };
|
|
715
|
-
}
|
|
716
|
-
function collectFiles(rootDir) {
|
|
717
|
-
const output = [];
|
|
718
|
-
for (const entry of readdirSync(rootDir)) {
|
|
719
|
-
const fullPath = path.join(rootDir, entry);
|
|
720
|
-
const stat = statSync(fullPath);
|
|
721
|
-
if (stat.isDirectory()) {
|
|
722
|
-
output.push(...collectFiles(fullPath));
|
|
723
|
-
}
|
|
724
|
-
else {
|
|
725
|
-
output.push(fullPath);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
return output;
|
|
421
|
+
return ["Rebuilt .waypoint/DOCS_INDEX.md", "Rebuilt .waypoint/TRACKS_INDEX.md"];
|
|
729
422
|
}
|
package/dist/src/templates.js
CHANGED
|
@@ -27,8 +27,5 @@ export function readTemplate(relativePath) {
|
|
|
27
27
|
}
|
|
28
28
|
export function renderWaypointConfig(options) {
|
|
29
29
|
return readTemplate(".waypoint/config.toml")
|
|
30
|
-
.replace("__PROFILE__", options.profile)
|
|
31
|
-
.replace("__ROLES__", String(options.roles))
|
|
32
|
-
.replace("__RULES__", String(options.rules))
|
|
33
|
-
.replace("__AUTOMATIONS__", String(options.automations));
|
|
30
|
+
.replace("__PROFILE__", options.profile);
|
|
34
31
|
}
|