@waycraft/waypoint-mcp 0.1.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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/context.js +79 -0
- package/dist/index.js +46 -0
- package/dist/tools/audit.js +311 -0
- package/dist/tools/build.js +112 -0
- package/dist/tools/compare.js +92 -0
- package/dist/tools/debug.js +148 -0
- package/dist/tools/design.js +276 -0
- package/dist/tools/document.js +107 -0
- package/dist/tools/fix.js +103 -0
- package/dist/tools/goal.js +94 -0
- package/dist/tools/improve.js +99 -0
- package/dist/tools/measure.js +101 -0
- package/dist/tools/plan.js +107 -0
- package/dist/tools/research.js +104 -0
- package/dist/tools/review.js +123 -0
- package/dist/tools/test.js +107 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Geli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Waypoint
|
|
2
|
+
|
|
3
|
+
A structured development protocol as an MCP server. 14 tools that guide any project from goal to ship — for engineers and non-technical stakeholders alike.
|
|
4
|
+
|
|
5
|
+
Each tool reads your workspace automatically. No setup or briefing required. Tools write structured artifacts to a `.waypoint/` folder, building a shared record of decisions, designs, plans, and reviews as you work.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx @waycraft/waypoint-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or add permanently via Claude Code:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
claude mcp add waypoint npx @waycraft/waypoint-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Connect manually
|
|
24
|
+
|
|
25
|
+
Add to `~/.claude/mcp.json` (global) or `.claude/mcp.json` (project-level):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"waypoint": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["@waycraft/waypoint-mcp"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Restart Claude Code after editing `mcp.json`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## The 14 tools
|
|
43
|
+
|
|
44
|
+
| Tool | What it does |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `waypoint_goal` | Define what you're building and why |
|
|
47
|
+
| `waypoint_research` | Surface what you need to know first |
|
|
48
|
+
| `waypoint_compare` | Weigh your options and tradeoffs |
|
|
49
|
+
| `waypoint_plan` | Map out how and when you'll build it |
|
|
50
|
+
| `waypoint_design` | Set the structural contract before building |
|
|
51
|
+
| `waypoint_build` | Scaffold the implementation |
|
|
52
|
+
| `waypoint_test` | Verify it works |
|
|
53
|
+
| `waypoint_fix` | Fix what's broken |
|
|
54
|
+
| `waypoint_debug` | Find out why it's broken |
|
|
55
|
+
| `waypoint_audit` | Check design health — tiered Must Fix / Should Fix findings |
|
|
56
|
+
| `waypoint_measure` | Evaluate whether you hit the goal |
|
|
57
|
+
| `waypoint_improve` | Identify what to make better |
|
|
58
|
+
| `waypoint_document` | Write it up for others |
|
|
59
|
+
| `waypoint_review` | Final check before you ship |
|
|
60
|
+
|
|
61
|
+
Every tool accepts `workspacePath` (required) — the absolute path to the project you're working on.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## The journey
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
waypoint_goal → waypoint_research → waypoint_compare → waypoint_plan → waypoint_design
|
|
69
|
+
→ waypoint_build → waypoint_test → waypoint_fix → waypoint_debug → waypoint_audit
|
|
70
|
+
→ waypoint_measure → waypoint_improve → waypoint_document → waypoint_review
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Tools are independent — call any one at any time. The order above is the natural progression, not a requirement.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## How artifacts work
|
|
78
|
+
|
|
79
|
+
Each tool writes a markdown file to `.waypoint/` in your workspace:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
your-project/
|
|
83
|
+
└── .waypoint/
|
|
84
|
+
├── goal.md
|
|
85
|
+
├── research.md
|
|
86
|
+
├── compare.md
|
|
87
|
+
├── plan.md
|
|
88
|
+
├── design.md
|
|
89
|
+
├── build.md
|
|
90
|
+
├── test.md
|
|
91
|
+
├── fix.md
|
|
92
|
+
├── debug.md
|
|
93
|
+
├── audit.md
|
|
94
|
+
├── measure.md
|
|
95
|
+
├── improve.md
|
|
96
|
+
├── docs.md
|
|
97
|
+
└── review.md
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Artifacts are plain markdown — edit them directly. Later tools read earlier ones to stay in context. Commit `.waypoint/` to version control to preserve the record.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Running tests
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
npm test
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each tool has its own test file in `tests/`. Tests spin up the MCP server over stdio, send real JSON-RPC calls, and assert on output and artifacts written to disk.
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFile, writeFile, readdir, mkdir, stat } from "fs/promises";
|
|
2
|
+
import { join, relative } from "path";
|
|
3
|
+
const SKIP_DIRS = new Set([
|
|
4
|
+
"node_modules", ".git", "dist", "build", ".next", ".nuxt",
|
|
5
|
+
"coverage", ".cache", ".turbo", "out",
|
|
6
|
+
]);
|
|
7
|
+
async function listTree(dir, depth, root) {
|
|
8
|
+
if (depth === 0)
|
|
9
|
+
return [];
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = await readdir(dir);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const lines = [];
|
|
18
|
+
for (const entry of entries.sort()) {
|
|
19
|
+
if (entry.startsWith(".") && entry !== ".waypoint")
|
|
20
|
+
continue;
|
|
21
|
+
if (SKIP_DIRS.has(entry))
|
|
22
|
+
continue;
|
|
23
|
+
const full = join(dir, entry);
|
|
24
|
+
const rel = relative(root, full);
|
|
25
|
+
let isDir = false;
|
|
26
|
+
try {
|
|
27
|
+
isDir = (await stat(full)).isDirectory();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
lines.push(isDir ? `${rel}/` : rel);
|
|
33
|
+
if (isDir) {
|
|
34
|
+
lines.push(...await listTree(full, depth - 1, root));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return lines;
|
|
38
|
+
}
|
|
39
|
+
export async function getBaseContext(workspacePath) {
|
|
40
|
+
const treeLines = await listTree(workspacePath, 2, workspacePath);
|
|
41
|
+
const fileTree = treeLines.length > 0 ? treeLines.join("\n") : "(empty workspace)";
|
|
42
|
+
let packageJson = null;
|
|
43
|
+
try {
|
|
44
|
+
packageJson = await readFile(join(workspacePath, "package.json"), "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// not a Node project — fine
|
|
48
|
+
}
|
|
49
|
+
const waypointArtifacts = {};
|
|
50
|
+
const waypointDir = join(workspacePath, ".waypoint");
|
|
51
|
+
try {
|
|
52
|
+
const files = await readdir(waypointDir);
|
|
53
|
+
for (const file of files.sort()) {
|
|
54
|
+
try {
|
|
55
|
+
waypointArtifacts[file] = await readFile(join(waypointDir, file), "utf8");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// skip unreadable files
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// no .waypoint directory yet — fine
|
|
64
|
+
}
|
|
65
|
+
return { workspacePath, fileTree, packageJson, waypointArtifacts };
|
|
66
|
+
}
|
|
67
|
+
export async function getArtifact(workspacePath, filename) {
|
|
68
|
+
try {
|
|
69
|
+
return await readFile(join(workspacePath, ".waypoint", filename), "utf8");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export async function saveArtifact(workspacePath, filename, content) {
|
|
76
|
+
const waypointDir = join(workspacePath, ".waypoint");
|
|
77
|
+
await mkdir(waypointDir, { recursive: true });
|
|
78
|
+
await writeFile(join(waypointDir, filename), content, "utf8");
|
|
79
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import * as goal from "./tools/goal.js";
|
|
5
|
+
import * as research from "./tools/research.js";
|
|
6
|
+
import * as compare from "./tools/compare.js";
|
|
7
|
+
import * as plan from "./tools/plan.js";
|
|
8
|
+
import * as design from "./tools/design.js";
|
|
9
|
+
import * as build from "./tools/build.js";
|
|
10
|
+
import * as test from "./tools/test.js";
|
|
11
|
+
import * as fix from "./tools/fix.js";
|
|
12
|
+
import * as debug from "./tools/debug.js";
|
|
13
|
+
import * as audit from "./tools/audit.js";
|
|
14
|
+
import * as measure from "./tools/measure.js";
|
|
15
|
+
import * as improve from "./tools/improve.js";
|
|
16
|
+
import * as document from "./tools/document.js";
|
|
17
|
+
import * as review from "./tools/review.js";
|
|
18
|
+
const tools = [goal, research, compare, plan, design, build, test, fix, debug, audit, measure, improve, document, review];
|
|
19
|
+
const server = new Server({ name: "waypoint-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
20
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
21
|
+
tools: tools.map((t) => t.definition),
|
|
22
|
+
}));
|
|
23
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
24
|
+
const { name, arguments: args } = request.params;
|
|
25
|
+
const tool = tools.find((t) => t.definition.name === name);
|
|
26
|
+
if (!tool) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
const text = await tool.run(args);
|
|
35
|
+
return { content: [{ type: "text", text }] };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: `Tool error: ${message}` }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const transport = new StdioServerTransport();
|
|
46
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { getBaseContext, getArtifact, saveArtifact } from "../context.js";
|
|
2
|
+
export const definition = {
|
|
3
|
+
name: "waypoint_audit",
|
|
4
|
+
description: "Diagnostic design health check on existing code. Infers project tier, confirms with one question, then produces tiered findings: Must Fix, Should Fix, Consider Later. Runs after Build or Fix, or standalone on any existing codebase.",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
workspacePath: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "Absolute path to the workspace root.",
|
|
11
|
+
},
|
|
12
|
+
tier: {
|
|
13
|
+
type: "string",
|
|
14
|
+
enum: ["prototype", "product", "platform"],
|
|
15
|
+
description: "Explicitly set the project tier (optional). Omit to let waypoint_audit infer it and ask for confirmation.",
|
|
16
|
+
},
|
|
17
|
+
focus: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "Specific file, folder, or concern to audit (optional). E.g. 'auth/', 'routes/user.js', 'error handling'. Omit to audit the full codebase.",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
required: ["workspacePath"],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
// ─── Tier inference ───────────────────────────────────────────────────────────
|
|
26
|
+
function inferTier(ctx) {
|
|
27
|
+
let score = 0;
|
|
28
|
+
const fileTree = ctx.fileTree ?? "";
|
|
29
|
+
const pkg = (() => {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(ctx.packageJson ?? "{}");
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
|
|
38
|
+
const allText = [fileTree, ctx.packageJson ?? ""].join("\n").toLowerCase();
|
|
39
|
+
const fileCount = (fileTree.match(/\n/g) ?? []).length;
|
|
40
|
+
if (fileCount > 40)
|
|
41
|
+
score += 2;
|
|
42
|
+
else if (fileCount > 15)
|
|
43
|
+
score += 1;
|
|
44
|
+
if (allText.includes("dockerfile") || allText.includes("docker-compose"))
|
|
45
|
+
score += 2;
|
|
46
|
+
if (allText.includes(".github/workflows") || allText.includes("ci.yml"))
|
|
47
|
+
score += 1;
|
|
48
|
+
if (allText.includes(".env.example") || allText.includes("config/"))
|
|
49
|
+
score += 1;
|
|
50
|
+
if (deps.some(d => ["prisma", "typeorm", "sequelize", "drizzle", "mongoose"].includes(d)))
|
|
51
|
+
score += 2;
|
|
52
|
+
if (deps.some(d => ["passport", "jsonwebtoken", "next-auth", "clerk"].includes(d)))
|
|
53
|
+
score += 1;
|
|
54
|
+
if (deps.some(d => ["jest", "vitest", "mocha", "playwright", "cypress"].includes(d)))
|
|
55
|
+
score += 1;
|
|
56
|
+
if (deps.some(d => ["@anthropic-ai/sdk", "openai", "langchain"].includes(d)))
|
|
57
|
+
score += 1;
|
|
58
|
+
if (allText.includes("console.log") && fileCount < 10)
|
|
59
|
+
score -= 1;
|
|
60
|
+
if (!ctx.packageJson)
|
|
61
|
+
score -= 1;
|
|
62
|
+
if (score >= 6)
|
|
63
|
+
return { tier: "platform", confidence: "high" };
|
|
64
|
+
if (score >= 3)
|
|
65
|
+
return { tier: "product", confidence: score >= 4 ? "high" : "medium" };
|
|
66
|
+
return { tier: "prototype", confidence: score <= 1 ? "high" : "medium" };
|
|
67
|
+
}
|
|
68
|
+
const FINDINGS = [
|
|
69
|
+
{
|
|
70
|
+
id: "secrets",
|
|
71
|
+
label: "Hardcoded secrets or credentials in code",
|
|
72
|
+
detail: "Any API key, password, or token literal in source code is a credential leak risk. Move to environment variables and add to .env.example.",
|
|
73
|
+
severity: { prototype: "must", product: "must", platform: "must" },
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "no-error-handling",
|
|
77
|
+
label: "Unhandled promise rejections or missing try/catch on I/O",
|
|
78
|
+
detail: "Silent failures corrupt state and are hard to debug in production. Every async I/O path needs explicit error handling.",
|
|
79
|
+
severity: { prototype: "must", product: "must", platform: "must" },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "god-file",
|
|
83
|
+
label: "God file or module (single file doing too much)",
|
|
84
|
+
detail: "Files over ~200 lines that mix concerns (routing, logic, data access) become hard to test and change. Split by responsibility.",
|
|
85
|
+
severity: { prototype: "should", product: "must", platform: "must" },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "auth-inline",
|
|
89
|
+
label: "Auth logic inside route handlers",
|
|
90
|
+
detail: "Auth mixed into handlers can't be tested in isolation and is easy to omit on new routes. Extract to middleware.",
|
|
91
|
+
severity: { prototype: "should", product: "must", platform: "must" },
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "no-input-validation",
|
|
95
|
+
label: "No input validation at system boundaries",
|
|
96
|
+
detail: "Unvalidated input leads to silent data corruption and security risks. Add schema validation (Zod, joi) before handler logic.",
|
|
97
|
+
severity: { prototype: "should", product: "must", platform: "must" },
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "no-service-layer",
|
|
101
|
+
label: "Direct DB or external API calls in route handlers",
|
|
102
|
+
detail: "Bypassing a service layer makes testing require real infrastructure and makes the DB/API harder to swap. Add a services/ or repositories/ layer.",
|
|
103
|
+
severity: { prototype: "consider", product: "should", platform: "must" },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "scattered-env",
|
|
107
|
+
label: "Scattered process.env access throughout the codebase",
|
|
108
|
+
detail: "Direct process.env reads in multiple files make config hard to audit and validate. Centralise in a config module.",
|
|
109
|
+
severity: { prototype: "consider", product: "should", platform: "must" },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "no-tests",
|
|
113
|
+
label: "No automated tests",
|
|
114
|
+
detail: "Without tests, changes regress silently. Add at minimum unit tests for core logic and one integration test per entry point.",
|
|
115
|
+
severity: { prototype: "consider", product: "should", platform: "must" },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "magic-values",
|
|
119
|
+
label: "Magic numbers or strings in logic",
|
|
120
|
+
detail: "Unexplained literals (timeouts, limits, status codes) are hard to understand and change safely. Use named constants.",
|
|
121
|
+
severity: { prototype: "should", product: "should", platform: "must" },
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "no-logging",
|
|
125
|
+
label: "No structured logging",
|
|
126
|
+
detail: "console.log is fine early on, but structured logging (with levels and context) is needed before onboarding a team or going to production.",
|
|
127
|
+
severity: { prototype: "consider", product: "consider", platform: "must" },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "inconsistent-naming",
|
|
131
|
+
label: "Inconsistent naming conventions",
|
|
132
|
+
detail: "Mixed camelCase/snake_case, unclear abbreviations, or generic names (data, result, obj) slow down reading. Align on a convention and apply it.",
|
|
133
|
+
severity: { prototype: "consider", product: "should", platform: "should" },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "inline-prompts",
|
|
137
|
+
label: "Prompt strings hardcoded inline in business logic",
|
|
138
|
+
detail: "Inline prompt strings mix concerns and make prompt iteration require code changes. Extract to a dedicated prompts/ module.",
|
|
139
|
+
severity: { prototype: "consider", product: "should", platform: "must" },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "no-llm-resilience",
|
|
143
|
+
label: "LLM calls without retry or error handling",
|
|
144
|
+
detail: "LLM APIs are unreliable — timeouts, rate limits, and transient errors are common. Wrap every call in retry logic with a fallback.",
|
|
145
|
+
severity: { prototype: "should", product: "must", platform: "must" },
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "fat-agent-tools",
|
|
149
|
+
label: "Agent tool functions with multiple responsibilities",
|
|
150
|
+
detail: "Tool functions that do too many things degrade model tool-use accuracy and are hard to test. Each tool should do exactly one thing.",
|
|
151
|
+
severity: { prototype: "consider", product: "should", platform: "must" },
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "no-llm-observability",
|
|
155
|
+
label: "No token usage or cost tracking on LLM calls",
|
|
156
|
+
detail: "Without observability on LLM usage, costs are invisible and runaway spending is easy to miss. Log token counts at the call site or service layer.",
|
|
157
|
+
severity: { prototype: "consider", product: "consider", platform: "must" },
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
// ─── Tier label map ───────────────────────────────────────────────────────────
|
|
161
|
+
const TIER_LABELS = {
|
|
162
|
+
prototype: "Prototype",
|
|
163
|
+
product: "Product",
|
|
164
|
+
platform: "Platform",
|
|
165
|
+
};
|
|
166
|
+
// ─── Confirmation note ────────────────────────────────────────────────────────
|
|
167
|
+
function confirmationNote(tier, confidence, explicit) {
|
|
168
|
+
if (explicit) {
|
|
169
|
+
return `> **Tier set explicitly: ${TIER_LABELS[tier]}**`;
|
|
170
|
+
}
|
|
171
|
+
if (confidence === "high") {
|
|
172
|
+
return `> **Tier inferred: ${TIER_LABELS[tier]}** (high confidence)\n> If this is wrong, re-run \`waypoint_audit\` with an explicit \`tier\` parameter.`;
|
|
173
|
+
}
|
|
174
|
+
const alternates = ["prototype", "product", "platform"].filter(t => t !== tier);
|
|
175
|
+
return [
|
|
176
|
+
`> **Tier inferred: ${TIER_LABELS[tier]}** (medium confidence — mixed signals)`,
|
|
177
|
+
`> Re-run with \`tier: "${alternates[0]}"\` or \`"${alternates[1]}"\` if this doesn't match your intent.`,
|
|
178
|
+
].join("\n");
|
|
179
|
+
}
|
|
180
|
+
// ─── Run ──────────────────────────────────────────────────────────────────────
|
|
181
|
+
export async function run(args) {
|
|
182
|
+
const { workspacePath, tier: explicitTier, focus } = args;
|
|
183
|
+
const ctx = await getBaseContext(workspacePath);
|
|
184
|
+
const buildArtifact = await getArtifact(workspacePath, "build.md");
|
|
185
|
+
const goalArtifact = await getArtifact(workspacePath, "goal.md");
|
|
186
|
+
const designArtifact = await getArtifact(workspacePath, "design.md");
|
|
187
|
+
const fixArtifact = await getArtifact(workspacePath, "fix.md");
|
|
188
|
+
const goalLine = goalArtifact?.match(/^# Goal\n+(.+)/m)?.[1] ?? "(no goal defined)";
|
|
189
|
+
let tierKey;
|
|
190
|
+
let confidence;
|
|
191
|
+
if (explicitTier) {
|
|
192
|
+
tierKey = explicitTier;
|
|
193
|
+
confidence = "explicit";
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const inferred = inferTier(ctx);
|
|
197
|
+
tierKey = inferred.tier;
|
|
198
|
+
confidence = inferred.confidence;
|
|
199
|
+
}
|
|
200
|
+
const tierLabel = TIER_LABELS[tierKey];
|
|
201
|
+
const tierNote = confirmationNote(tierKey, confidence, !!explicitTier);
|
|
202
|
+
const contextNotes = [
|
|
203
|
+
!buildArtifact && !goalArtifact && "> ℹ️ No EDP context found — running as standalone audit on existing codebase.",
|
|
204
|
+
!buildArtifact && goalArtifact && "> ⚠️ No build.md found — audit has no build baseline.",
|
|
205
|
+
designArtifact && "> ✅ design.md found — checking compliance with design contract.",
|
|
206
|
+
fixArtifact && "> ℹ️ fix.md present — checking that recent fixes haven't introduced structural drift.",
|
|
207
|
+
].filter(Boolean);
|
|
208
|
+
const must = FINDINGS.filter(f => f.severity[tierKey] === "must");
|
|
209
|
+
const should = FINDINGS.filter(f => f.severity[tierKey] === "should");
|
|
210
|
+
const consider = FINDINGS.filter(f => f.severity[tierKey] === "consider");
|
|
211
|
+
const pkg = (() => {
|
|
212
|
+
try {
|
|
213
|
+
return JSON.parse(ctx.packageJson ?? "{}");
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
})();
|
|
219
|
+
const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
|
|
220
|
+
const isAiNative = deps.some(d => ["@anthropic-ai/sdk", "openai", "langchain", "@langchain"].includes(d));
|
|
221
|
+
const aiFindings = ["inline-prompts", "no-llm-resilience", "fat-agent-tools", "no-llm-observability"];
|
|
222
|
+
function renderFindings(findings) {
|
|
223
|
+
if (findings.length === 0)
|
|
224
|
+
return ["_None at this tier._"];
|
|
225
|
+
return findings.flatMap((f, i) => {
|
|
226
|
+
const isAi = aiFindings.includes(f.id);
|
|
227
|
+
const tag = isAiNative && isAi ? " _(AI-native)_" : "";
|
|
228
|
+
const focusNote = focus ? ` _(check in: ${focus})_` : "";
|
|
229
|
+
return [
|
|
230
|
+
`### ${i + 1}. ${f.label}${tag}`,
|
|
231
|
+
`${f.detail}${focusNote}`,
|
|
232
|
+
"- [ ] Investigated",
|
|
233
|
+
"- [ ] Action taken or explicitly deferred",
|
|
234
|
+
"",
|
|
235
|
+
];
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const artifact = [
|
|
239
|
+
"# Audit",
|
|
240
|
+
"",
|
|
241
|
+
`**Goal:** ${goalLine}`,
|
|
242
|
+
focus ? `**Focus:** ${focus}` : "",
|
|
243
|
+
`**Tier:** ${tierLabel}`,
|
|
244
|
+
...contextNotes,
|
|
245
|
+
"",
|
|
246
|
+
tierNote,
|
|
247
|
+
"",
|
|
248
|
+
"## Must Fix",
|
|
249
|
+
"<!-- Address these before shipping or sharing this code -->",
|
|
250
|
+
"",
|
|
251
|
+
...renderFindings(must),
|
|
252
|
+
"## Should Fix",
|
|
253
|
+
"<!-- Address before next release or when touching this area -->",
|
|
254
|
+
"",
|
|
255
|
+
...renderFindings(should),
|
|
256
|
+
"## Consider Later",
|
|
257
|
+
"<!-- Worth doing at scale — not urgent at current tier -->",
|
|
258
|
+
"",
|
|
259
|
+
...renderFindings(consider),
|
|
260
|
+
"## Design contract compliance",
|
|
261
|
+
designArtifact
|
|
262
|
+
? "<!-- Check findings against the contract in design.md -->"
|
|
263
|
+
: "<!-- No design.md found — run `waypoint_design` to establish a contract, then re-audit -->",
|
|
264
|
+
"- [ ] All Must Fix items from the design contract are addressed",
|
|
265
|
+
"- [ ] Structure matches design.md recommendations",
|
|
266
|
+
"",
|
|
267
|
+
"## Audit summary",
|
|
268
|
+
`| Severity | Count |`,
|
|
269
|
+
`|----------|-------|`,
|
|
270
|
+
`| Must Fix | ${must.length} |`,
|
|
271
|
+
`| Should Fix | ${should.length} |`,
|
|
272
|
+
`| Consider Later | ${consider.length} |`,
|
|
273
|
+
"",
|
|
274
|
+
"**Overall verdict:**",
|
|
275
|
+
"<!-- ✅ Clean | ⚠️ Needs attention | ❌ Significant issues -->",
|
|
276
|
+
"",
|
|
277
|
+
`_Generated by waypoint_audit (${tierLabel}) — ${new Date().toISOString()}_`,
|
|
278
|
+
]
|
|
279
|
+
.filter(l => l !== undefined)
|
|
280
|
+
.join("\n");
|
|
281
|
+
await saveArtifact(workspacePath, "audit.md", artifact);
|
|
282
|
+
const hasMust = must.length > 0;
|
|
283
|
+
const hasShould = should.length > 0;
|
|
284
|
+
const nextStep = hasMust
|
|
285
|
+
? "Run `waypoint_fix` for Must Fix items — minimal targeted patches. Then re-run `waypoint_audit` to confirm."
|
|
286
|
+
: hasShould
|
|
287
|
+
? "No Must Fix items. Run `waypoint_improve` to work through Should Fix items as structured refactors."
|
|
288
|
+
: "No Must Fix or Should Fix items. Run `waypoint_review` for a final pre-ship quality check.";
|
|
289
|
+
return [
|
|
290
|
+
"## waypoint_audit — Audit complete",
|
|
291
|
+
"",
|
|
292
|
+
`**Tier:** ${tierLabel}`,
|
|
293
|
+
`**Goal:** ${goalLine}`,
|
|
294
|
+
focus ? `**Focus:** ${focus}` : "",
|
|
295
|
+
isAiNative ? "**AI-native patterns:** checked" : "",
|
|
296
|
+
"",
|
|
297
|
+
"### Findings summary",
|
|
298
|
+
`- **Must Fix:** ${must.length} — address before shipping`,
|
|
299
|
+
`- **Should Fix:** ${should.length} — address before next release`,
|
|
300
|
+
`- **Consider Later:** ${consider.length} — low urgency at current tier`,
|
|
301
|
+
"",
|
|
302
|
+
"### Artifact saved",
|
|
303
|
+
"`audit.md` written to `.waypoint/audit.md`.",
|
|
304
|
+
"Work through each checklist item — check off as you investigate and act.",
|
|
305
|
+
"",
|
|
306
|
+
"### Suggested next step",
|
|
307
|
+
nextStep,
|
|
308
|
+
]
|
|
309
|
+
.filter(l => l !== undefined)
|
|
310
|
+
.join("\n");
|
|
311
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { getBaseContext, getArtifact, saveArtifact } from "../context.js";
|
|
2
|
+
export const definition = {
|
|
3
|
+
name: "waypoint_build",
|
|
4
|
+
description: "Scaffold and implement. Generate prompts and guidance for AI coding tools.",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
workspacePath: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "Absolute path to the workspace root.",
|
|
11
|
+
},
|
|
12
|
+
task: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Specific build task or milestone to implement (optional). Omit to build from the full plan.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
required: ["workspacePath"],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
export async function run(args) {
|
|
21
|
+
const { workspacePath, task } = args;
|
|
22
|
+
const ctx = await getBaseContext(workspacePath);
|
|
23
|
+
const planArtifact = await getArtifact(workspacePath, "plan.md");
|
|
24
|
+
const goalArtifact = await getArtifact(workspacePath, "goal.md");
|
|
25
|
+
if (!planArtifact && !goalArtifact) {
|
|
26
|
+
return [
|
|
27
|
+
"## waypoint_build — No plan or goal found",
|
|
28
|
+
"",
|
|
29
|
+
"Run `waypoint_goal` → `waypoint_plan` before building.",
|
|
30
|
+
].join("\n");
|
|
31
|
+
}
|
|
32
|
+
const goalLine = goalArtifact?.match(/^# Goal\n+(.+)/m)?.[1] ?? "(goal not parsed)";
|
|
33
|
+
const hasPlan = !!planArtifact;
|
|
34
|
+
const pkgName = (() => {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(ctx.packageJson ?? "{}").name ?? "this project";
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return "this project";
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
const artifact = [
|
|
43
|
+
"# Build",
|
|
44
|
+
"",
|
|
45
|
+
`**Goal:** ${goalLine}`,
|
|
46
|
+
task ? `**Task:** ${task}` : "",
|
|
47
|
+
!hasPlan ? "\n> ⚠️ No plan.md found — consider running `waypoint_plan` first." : "",
|
|
48
|
+
"",
|
|
49
|
+
"## Implementation checklist",
|
|
50
|
+
"<!-- Track each implementation unit -->",
|
|
51
|
+
"- [ ] Set up project structure / scaffold",
|
|
52
|
+
"- [ ] Implement core logic",
|
|
53
|
+
"- [ ] Wire up integrations",
|
|
54
|
+
"- [ ] Handle edge cases and errors",
|
|
55
|
+
"- [ ] Self-review before handoff to waypoint_test",
|
|
56
|
+
"",
|
|
57
|
+
"## AI coding prompts",
|
|
58
|
+
"<!-- Paste these into your AI coding tool (Claude Code, Cursor, etc.) -->",
|
|
59
|
+
"",
|
|
60
|
+
"### Scaffold prompt",
|
|
61
|
+
"```",
|
|
62
|
+
`Scaffold the implementation for: ${task ?? goalLine}`,
|
|
63
|
+
`Project: ${pkgName}`,
|
|
64
|
+
"Requirements:",
|
|
65
|
+
"- [fill from plan milestones]",
|
|
66
|
+
"- Keep changes minimal and focused",
|
|
67
|
+
"- No speculative features",
|
|
68
|
+
"```",
|
|
69
|
+
"",
|
|
70
|
+
"### Implementation prompt",
|
|
71
|
+
"```",
|
|
72
|
+
`Implement: ${task ?? goalLine}`,
|
|
73
|
+
"Constraints:",
|
|
74
|
+
"- Match existing code style",
|
|
75
|
+
"- No new dependencies unless necessary",
|
|
76
|
+
"- Write no comments unless the why is non-obvious",
|
|
77
|
+
"```",
|
|
78
|
+
"",
|
|
79
|
+
"## Implementation notes",
|
|
80
|
+
"<!-- Record decisions made during build, gotchas, deviations from plan -->",
|
|
81
|
+
"- ",
|
|
82
|
+
"",
|
|
83
|
+
"## Files changed",
|
|
84
|
+
"<!-- List key files created or modified -->",
|
|
85
|
+
"- ",
|
|
86
|
+
"",
|
|
87
|
+
`_Generated by waypoint_build — ${new Date().toISOString()}_`,
|
|
88
|
+
]
|
|
89
|
+
.filter((l) => l !== undefined)
|
|
90
|
+
.join("\n");
|
|
91
|
+
await saveArtifact(workspacePath, "build.md", artifact);
|
|
92
|
+
return [
|
|
93
|
+
"## waypoint_build — Build guide generated",
|
|
94
|
+
"",
|
|
95
|
+
`**Goal:** ${goalLine}`,
|
|
96
|
+
task ? `**Task:** ${task}` : "",
|
|
97
|
+
!hasPlan ? "\n> No plan.md found — output may be generic. Run `waypoint_plan` first." : "",
|
|
98
|
+
"",
|
|
99
|
+
"### AI coding prompts included",
|
|
100
|
+
"- **Scaffold prompt** — use to create initial structure",
|
|
101
|
+
"- **Implementation prompt** — use for each milestone or task",
|
|
102
|
+
"",
|
|
103
|
+
"### Artifact saved",
|
|
104
|
+
"`build.md` written to `.waypoint/build.md`.",
|
|
105
|
+
"Fill in the implementation checklist as you work.",
|
|
106
|
+
"",
|
|
107
|
+
"### Suggested next step",
|
|
108
|
+
"Run `waypoint_test` to verify feature-level requirements once implementation is ready.",
|
|
109
|
+
]
|
|
110
|
+
.filter((l) => l !== undefined)
|
|
111
|
+
.join("\n");
|
|
112
|
+
}
|