agent-method 1.5.1 → 1.5.5
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 +245 -143
- package/bin/{agent-method.js → wwa.js} +12 -4
- package/lib/cli/check.js +71 -71
- package/lib/cli/init.js +107 -17
- package/lib/cli/pipeline.js +1 -1
- package/lib/cli/refine.js +202 -202
- package/lib/cli/route.js +1 -1
- package/lib/cli/scan.js +28 -28
- package/lib/cli/serve.js +23 -0
- package/lib/cli/status.js +61 -61
- package/lib/cli/upgrade.js +149 -146
- package/lib/cli/watch.js +32 -0
- package/lib/init.js +296 -240
- package/lib/mcp-server.js +524 -0
- package/lib/pipeline.js +1 -1
- package/lib/registry.js +1 -1
- package/lib/watcher.js +165 -0
- package/package.json +8 -5
- package/templates/README.md +13 -9
- package/templates/entry-points/.cursorrules +3 -3
- package/templates/entry-points/AGENT.md +3 -3
- package/templates/entry-points/CLAUDE.md +3 -3
- package/templates/full/.cursorrules +3 -3
- package/templates/full/AGENT.md +3 -3
- package/templates/full/CLAUDE.md +3 -3
- package/templates/full/SESSION-LOG.md +66 -5
- package/templates/starter/.cursorrules +3 -3
- package/templates/starter/AGENT.md +3 -3
- package/templates/starter/CLAUDE.md +3 -3
- package/templates/starter/SESSION-LOG.md +66 -5
package/lib/cli/refine.js
CHANGED
|
@@ -1,202 +1,202 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { findSessionLog } from "./helpers.js";
|
|
5
|
-
|
|
6
|
-
export function register(program) {
|
|
7
|
-
program
|
|
8
|
-
.command("refine [session-log]")
|
|
9
|
-
.description("Extract a refinement report from session history")
|
|
10
|
-
.option("-o, --output <path>", "Output file (default: stdout)")
|
|
11
|
-
.option("--json", "Output as JSON")
|
|
12
|
-
.action(async (sessionLog, opts) => {
|
|
13
|
-
if (!sessionLog) {
|
|
14
|
-
sessionLog = findSessionLog(".");
|
|
15
|
-
if (!sessionLog) {
|
|
16
|
-
console.error(
|
|
17
|
-
"No SESSION-LOG.md found in current directory.\n" +
|
|
18
|
-
"Specify a path: npx agent-method refine path/to/SESSION-LOG.md"
|
|
19
|
-
);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (!existsSync(sessionLog)) {
|
|
25
|
-
console.error(`File not found: ${sessionLog}`);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const content = readFileSync(sessionLog, "utf-8");
|
|
30
|
-
const parsed = parseSessionLog(content);
|
|
31
|
-
|
|
32
|
-
let result;
|
|
33
|
-
if (opts.json) {
|
|
34
|
-
result = JSON.stringify(parsed, null, 2);
|
|
35
|
-
} else {
|
|
36
|
-
result = generateRefinementReport(parsed);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (opts.output) {
|
|
40
|
-
writeFileSync(opts.output, result, "utf-8");
|
|
41
|
-
console.log(
|
|
42
|
-
`Refinement report written to ${opts.output} (${parsed.entries.length} sessions)`
|
|
43
|
-
);
|
|
44
|
-
} else {
|
|
45
|
-
console.log(result);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
// Session log parsing
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
function parseSessionLog(content) {
|
|
55
|
-
const context = {};
|
|
56
|
-
const ctxMatch = content.match(
|
|
57
|
-
/## Project context\s*\n\s*\|[^\n]+\n\s*\|[-| ]+\n((?:\|[^\n]+\n)*)/
|
|
58
|
-
);
|
|
59
|
-
if (ctxMatch) {
|
|
60
|
-
for (const row of ctxMatch[1].trim().split("\n")) {
|
|
61
|
-
const cols = row
|
|
62
|
-
.split("|")
|
|
63
|
-
.map((c) => c.trim())
|
|
64
|
-
.filter((c) => c);
|
|
65
|
-
if (cols.length >= 2) {
|
|
66
|
-
context[cols[0].toLowerCase()] = cols[1];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const entries = [];
|
|
72
|
-
const entryPattern =
|
|
73
|
-
/###\s+S(\d+)\s*(?:\u2014|--)\s*(\S+)\s*(?:\u2014|--)\s*(.+?)$(.*?)(?=###\s+S\d+|$)/gms;
|
|
74
|
-
let m;
|
|
75
|
-
while ((m = entryPattern.exec(content)) !== null) {
|
|
76
|
-
const [, num, date, title, body] = m;
|
|
77
|
-
const entry = { session: parseInt(num, 10), date, title: title.trim() };
|
|
78
|
-
|
|
79
|
-
for (const line of body.split("\n")) {
|
|
80
|
-
const trimmed = line.trim();
|
|
81
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
82
|
-
const segments = trimmed.split("|").map((s) => s.trim());
|
|
83
|
-
for (const seg of segments) {
|
|
84
|
-
if (seg.includes(":")) {
|
|
85
|
-
const idx = seg.indexOf(":");
|
|
86
|
-
const key = seg.slice(0, idx).trim().toLowerCase();
|
|
87
|
-
const val = seg.slice(idx + 1).trim();
|
|
88
|
-
if (
|
|
89
|
-
[
|
|
90
|
-
"model", "profile", "workflow", "queries",
|
|
91
|
-
"features", "cascades", "decisions", "friction", "finding",
|
|
92
|
-
].includes(key)
|
|
93
|
-
) {
|
|
94
|
-
entry[key] = val;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
entries.push(entry);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return { context, entries };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function generateRefinementReport(parsed) {
|
|
106
|
-
const ctx = parsed.context;
|
|
107
|
-
const entries = parsed.entries;
|
|
108
|
-
|
|
109
|
-
const project = ctx["project name"] || "Unknown";
|
|
110
|
-
const ptype = ctx["project type"] || "unknown";
|
|
111
|
-
const profile = ctx["integration profile"] || "unknown";
|
|
112
|
-
const extensions = ctx["extension(s)"] || "none";
|
|
113
|
-
|
|
114
|
-
const workflows = new Set();
|
|
115
|
-
let totalFriction = 0;
|
|
116
|
-
let totalFindings = 0;
|
|
117
|
-
const findingsList = [];
|
|
118
|
-
const frictionList = [];
|
|
119
|
-
|
|
120
|
-
for (const e of entries) {
|
|
121
|
-
if (e.workflow) workflows.add(e.workflow);
|
|
122
|
-
const friction = e.friction || "none";
|
|
123
|
-
if (friction.toLowerCase() !== "none" && friction) {
|
|
124
|
-
totalFriction++;
|
|
125
|
-
frictionList.push(`S${e.session} (${e.date}): ${friction}`);
|
|
126
|
-
}
|
|
127
|
-
const finding = e.finding || "none";
|
|
128
|
-
if (finding.toLowerCase() !== "none" && finding) {
|
|
129
|
-
totalFindings++;
|
|
130
|
-
findingsList.push(`S${e.session} (${e.date}): ${finding}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const wfSorted = [...workflows].sort().join(", ") || "none";
|
|
135
|
-
const lines = [
|
|
136
|
-
`# Refinement Report: ${project}`,
|
|
137
|
-
"",
|
|
138
|
-
"Auto-generated from SESSION-LOG.md by `
|
|
139
|
-
"",
|
|
140
|
-
"## Source",
|
|
141
|
-
"",
|
|
142
|
-
"| Field | Value |",
|
|
143
|
-
"|-------|-------|",
|
|
144
|
-
`| Project type | ${ptype} |`,
|
|
145
|
-
`| Extension(s) | ${extensions} |`,
|
|
146
|
-
`| Workflow(s) exercised | ${wfSorted} |`,
|
|
147
|
-
`| Integration profile | ${profile} |`,
|
|
148
|
-
`| Sessions | ${entries.length} |`,
|
|
149
|
-
"",
|
|
150
|
-
"## Session summary",
|
|
151
|
-
"",
|
|
152
|
-
"| # | Date | Title | Workflow | Friction | Finding |",
|
|
153
|
-
"|---|------|-------|----------|----------|---------|",
|
|
154
|
-
];
|
|
155
|
-
|
|
156
|
-
for (const e of entries) {
|
|
157
|
-
const friction = e.friction || "none";
|
|
158
|
-
const finding = e.finding || "none";
|
|
159
|
-
const wf = e.workflow || "\u2014";
|
|
160
|
-
lines.push(
|
|
161
|
-
`| S${e.session} | ${e.date} | ${e.title} | ${wf} | ${friction} | ${finding} |`
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
lines.push(
|
|
166
|
-
"", "## Statistics", "",
|
|
167
|
-
`- **Total sessions**: ${entries.length}`,
|
|
168
|
-
`- **Workflows used**: ${wfSorted}`,
|
|
169
|
-
`- **Sessions with friction**: ${totalFriction}`,
|
|
170
|
-
`- **Sessions with findings**: ${totalFindings}`,
|
|
171
|
-
""
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
if (frictionList.length > 0) {
|
|
175
|
-
lines.push("## Friction points", "");
|
|
176
|
-
for (const f of frictionList) lines.push(`- ${f}`);
|
|
177
|
-
lines.push("");
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (findingsList.length > 0) {
|
|
181
|
-
lines.push("## Findings", "");
|
|
182
|
-
for (const f of findingsList) lines.push(`- ${f}`);
|
|
183
|
-
lines.push("");
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
lines.push(
|
|
187
|
-
"## Dimension scores", "",
|
|
188
|
-
"Fill in after reviewing the session data above:", "",
|
|
189
|
-
"| Dimension | Score | One-line summary |",
|
|
190
|
-
"|-----------|:-----:|-----------------|",
|
|
191
|
-
"| Context quality | \u2014 | |",
|
|
192
|
-
"| Decision preservation | \u2014 | |",
|
|
193
|
-
"| Scope discipline | \u2014 | |",
|
|
194
|
-
"| Cascade coverage | \u2014 | |",
|
|
195
|
-
"| Audit completeness | \u2014 | |",
|
|
196
|
-
"| Bootstrap speed | \u2014 | |",
|
|
197
|
-
"| Lifecycle fit | \u2014 | |",
|
|
198
|
-
"| Model adequacy | \u2014 | |"
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
return lines.join("\n") + "\n";
|
|
202
|
-
}
|
|
1
|
+
/** wwa refine — extract refinement report from session history. */
|
|
2
|
+
|
|
3
|
+
import { readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { findSessionLog } from "./helpers.js";
|
|
5
|
+
|
|
6
|
+
export function register(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("refine [session-log]")
|
|
9
|
+
.description("Extract a refinement report from session history")
|
|
10
|
+
.option("-o, --output <path>", "Output file (default: stdout)")
|
|
11
|
+
.option("--json", "Output as JSON")
|
|
12
|
+
.action(async (sessionLog, opts) => {
|
|
13
|
+
if (!sessionLog) {
|
|
14
|
+
sessionLog = findSessionLog(".");
|
|
15
|
+
if (!sessionLog) {
|
|
16
|
+
console.error(
|
|
17
|
+
"No SESSION-LOG.md found in current directory.\n" +
|
|
18
|
+
"Specify a path: npx agent-method refine path/to/SESSION-LOG.md"
|
|
19
|
+
);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!existsSync(sessionLog)) {
|
|
25
|
+
console.error(`File not found: ${sessionLog}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = readFileSync(sessionLog, "utf-8");
|
|
30
|
+
const parsed = parseSessionLog(content);
|
|
31
|
+
|
|
32
|
+
let result;
|
|
33
|
+
if (opts.json) {
|
|
34
|
+
result = JSON.stringify(parsed, null, 2);
|
|
35
|
+
} else {
|
|
36
|
+
result = generateRefinementReport(parsed);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (opts.output) {
|
|
40
|
+
writeFileSync(opts.output, result, "utf-8");
|
|
41
|
+
console.log(
|
|
42
|
+
`Refinement report written to ${opts.output} (${parsed.entries.length} sessions)`
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
console.log(result);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Session log parsing
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
function parseSessionLog(content) {
|
|
55
|
+
const context = {};
|
|
56
|
+
const ctxMatch = content.match(
|
|
57
|
+
/## Project context\s*\n\s*\|[^\n]+\n\s*\|[-| ]+\n((?:\|[^\n]+\n)*)/
|
|
58
|
+
);
|
|
59
|
+
if (ctxMatch) {
|
|
60
|
+
for (const row of ctxMatch[1].trim().split("\n")) {
|
|
61
|
+
const cols = row
|
|
62
|
+
.split("|")
|
|
63
|
+
.map((c) => c.trim())
|
|
64
|
+
.filter((c) => c);
|
|
65
|
+
if (cols.length >= 2) {
|
|
66
|
+
context[cols[0].toLowerCase()] = cols[1];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const entries = [];
|
|
72
|
+
const entryPattern =
|
|
73
|
+
/###\s+S(\d+)\s*(?:\u2014|--)\s*(\S+)\s*(?:\u2014|--)\s*(.+?)$(.*?)(?=###\s+S\d+|$)/gms;
|
|
74
|
+
let m;
|
|
75
|
+
while ((m = entryPattern.exec(content)) !== null) {
|
|
76
|
+
const [, num, date, title, body] = m;
|
|
77
|
+
const entry = { session: parseInt(num, 10), date, title: title.trim() };
|
|
78
|
+
|
|
79
|
+
for (const line of body.split("\n")) {
|
|
80
|
+
const trimmed = line.trim();
|
|
81
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
82
|
+
const segments = trimmed.split("|").map((s) => s.trim());
|
|
83
|
+
for (const seg of segments) {
|
|
84
|
+
if (seg.includes(":")) {
|
|
85
|
+
const idx = seg.indexOf(":");
|
|
86
|
+
const key = seg.slice(0, idx).trim().toLowerCase();
|
|
87
|
+
const val = seg.slice(idx + 1).trim();
|
|
88
|
+
if (
|
|
89
|
+
[
|
|
90
|
+
"model", "profile", "workflow", "queries",
|
|
91
|
+
"features", "cascades", "decisions", "friction", "finding",
|
|
92
|
+
].includes(key)
|
|
93
|
+
) {
|
|
94
|
+
entry[key] = val;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
entries.push(entry);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { context, entries };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function generateRefinementReport(parsed) {
|
|
106
|
+
const ctx = parsed.context;
|
|
107
|
+
const entries = parsed.entries;
|
|
108
|
+
|
|
109
|
+
const project = ctx["project name"] || "Unknown";
|
|
110
|
+
const ptype = ctx["project type"] || "unknown";
|
|
111
|
+
const profile = ctx["integration profile"] || "unknown";
|
|
112
|
+
const extensions = ctx["extension(s)"] || "none";
|
|
113
|
+
|
|
114
|
+
const workflows = new Set();
|
|
115
|
+
let totalFriction = 0;
|
|
116
|
+
let totalFindings = 0;
|
|
117
|
+
const findingsList = [];
|
|
118
|
+
const frictionList = [];
|
|
119
|
+
|
|
120
|
+
for (const e of entries) {
|
|
121
|
+
if (e.workflow) workflows.add(e.workflow);
|
|
122
|
+
const friction = e.friction || "none";
|
|
123
|
+
if (friction.toLowerCase() !== "none" && friction) {
|
|
124
|
+
totalFriction++;
|
|
125
|
+
frictionList.push(`S${e.session} (${e.date}): ${friction}`);
|
|
126
|
+
}
|
|
127
|
+
const finding = e.finding || "none";
|
|
128
|
+
if (finding.toLowerCase() !== "none" && finding) {
|
|
129
|
+
totalFindings++;
|
|
130
|
+
findingsList.push(`S${e.session} (${e.date}): ${finding}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const wfSorted = [...workflows].sort().join(", ") || "none";
|
|
135
|
+
const lines = [
|
|
136
|
+
`# Refinement Report: ${project}`,
|
|
137
|
+
"",
|
|
138
|
+
"Auto-generated from SESSION-LOG.md by `wwa refine`.",
|
|
139
|
+
"",
|
|
140
|
+
"## Source",
|
|
141
|
+
"",
|
|
142
|
+
"| Field | Value |",
|
|
143
|
+
"|-------|-------|",
|
|
144
|
+
`| Project type | ${ptype} |`,
|
|
145
|
+
`| Extension(s) | ${extensions} |`,
|
|
146
|
+
`| Workflow(s) exercised | ${wfSorted} |`,
|
|
147
|
+
`| Integration profile | ${profile} |`,
|
|
148
|
+
`| Sessions | ${entries.length} |`,
|
|
149
|
+
"",
|
|
150
|
+
"## Session summary",
|
|
151
|
+
"",
|
|
152
|
+
"| # | Date | Title | Workflow | Friction | Finding |",
|
|
153
|
+
"|---|------|-------|----------|----------|---------|",
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
for (const e of entries) {
|
|
157
|
+
const friction = e.friction || "none";
|
|
158
|
+
const finding = e.finding || "none";
|
|
159
|
+
const wf = e.workflow || "\u2014";
|
|
160
|
+
lines.push(
|
|
161
|
+
`| S${e.session} | ${e.date} | ${e.title} | ${wf} | ${friction} | ${finding} |`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push(
|
|
166
|
+
"", "## Statistics", "",
|
|
167
|
+
`- **Total sessions**: ${entries.length}`,
|
|
168
|
+
`- **Workflows used**: ${wfSorted}`,
|
|
169
|
+
`- **Sessions with friction**: ${totalFriction}`,
|
|
170
|
+
`- **Sessions with findings**: ${totalFindings}`,
|
|
171
|
+
""
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
if (frictionList.length > 0) {
|
|
175
|
+
lines.push("## Friction points", "");
|
|
176
|
+
for (const f of frictionList) lines.push(`- ${f}`);
|
|
177
|
+
lines.push("");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (findingsList.length > 0) {
|
|
181
|
+
lines.push("## Findings", "");
|
|
182
|
+
for (const f of findingsList) lines.push(`- ${f}`);
|
|
183
|
+
lines.push("");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
lines.push(
|
|
187
|
+
"## Dimension scores", "",
|
|
188
|
+
"Fill in after reviewing the session data above:", "",
|
|
189
|
+
"| Dimension | Score | One-line summary |",
|
|
190
|
+
"|-----------|:-----:|-----------------|",
|
|
191
|
+
"| Context quality | \u2014 | |",
|
|
192
|
+
"| Decision preservation | \u2014 | |",
|
|
193
|
+
"| Scope discipline | \u2014 | |",
|
|
194
|
+
"| Cascade coverage | \u2014 | |",
|
|
195
|
+
"| Audit completeness | \u2014 | |",
|
|
196
|
+
"| Bootstrap speed | \u2014 | |",
|
|
197
|
+
"| Lifecycle fit | \u2014 | |",
|
|
198
|
+
"| Model adequacy | \u2014 | |"
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return lines.join("\n") + "\n";
|
|
202
|
+
}
|
package/lib/cli/route.js
CHANGED
package/lib/cli/scan.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
import { getPipeline, outputData } from "./helpers.js";
|
|
4
|
-
|
|
5
|
-
export function register(program) {
|
|
6
|
-
program
|
|
7
|
-
.command("scan [directory]")
|
|
8
|
-
.description("Detect project type from directory contents")
|
|
9
|
-
.option("--registry <path>", "Path to feature-registry.yaml")
|
|
10
|
-
.option("--json", "Output as JSON")
|
|
11
|
-
.action(async (directory, opts) => {
|
|
12
|
-
directory = directory || ".";
|
|
13
|
-
const { detectProjectType } = await getPipeline();
|
|
14
|
-
const result = detectProjectType(directory);
|
|
15
|
-
|
|
16
|
-
const ptype = result.project_type || "general";
|
|
17
|
-
const friendlyMap = { analytical: "context", mixed: "mix" };
|
|
18
|
-
if (ptype in friendlyMap) {
|
|
19
|
-
result.friendly_name = friendlyMap[ptype];
|
|
20
|
-
result.init_command = `npx agent-method init ${friendlyMap[ptype]}`;
|
|
21
|
-
} else {
|
|
22
|
-
result.init_command = `npx agent-method init ${ptype}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
console.log(`Scanning: ${directory}`);
|
|
26
|
-
outputData(result, opts.json);
|
|
27
|
-
});
|
|
28
|
-
}
|
|
1
|
+
/** wwa scan — detect project type from directory contents. */
|
|
2
|
+
|
|
3
|
+
import { getPipeline, outputData } from "./helpers.js";
|
|
4
|
+
|
|
5
|
+
export function register(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("scan [directory]")
|
|
8
|
+
.description("Detect project type from directory contents")
|
|
9
|
+
.option("--registry <path>", "Path to feature-registry.yaml")
|
|
10
|
+
.option("--json", "Output as JSON")
|
|
11
|
+
.action(async (directory, opts) => {
|
|
12
|
+
directory = directory || ".";
|
|
13
|
+
const { detectProjectType } = await getPipeline();
|
|
14
|
+
const result = detectProjectType(directory);
|
|
15
|
+
|
|
16
|
+
const ptype = result.project_type || "general";
|
|
17
|
+
const friendlyMap = { analytical: "context", mixed: "mix" };
|
|
18
|
+
if (ptype in friendlyMap) {
|
|
19
|
+
result.friendly_name = friendlyMap[ptype];
|
|
20
|
+
result.init_command = `npx agent-method init ${friendlyMap[ptype]}`;
|
|
21
|
+
} else {
|
|
22
|
+
result.init_command = `npx agent-method init ${ptype}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(`Scanning: ${directory}`);
|
|
26
|
+
outputData(result, opts.json);
|
|
27
|
+
});
|
|
28
|
+
}
|
package/lib/cli/serve.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** wwa serve — start MCP server on stdio transport. */
|
|
2
|
+
|
|
3
|
+
export function register(program) {
|
|
4
|
+
program
|
|
5
|
+
.command("serve")
|
|
6
|
+
.description("Start MCP server exposing methodology tools via stdio transport")
|
|
7
|
+
.option("--registry <path>", "Path to feature-registry.yaml")
|
|
8
|
+
.action(async (opts) => {
|
|
9
|
+
// Redirect all non-MCP output to stderr so stdout stays clean for JSON-RPC
|
|
10
|
+
const info = (msg) => process.stderr.write(msg + "\n");
|
|
11
|
+
|
|
12
|
+
info("wwa MCP server starting...");
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const { startServer } = await import("../mcp-server.js");
|
|
16
|
+
await startServer(opts.registry || undefined);
|
|
17
|
+
info("wwa MCP server running on stdio. Press Ctrl+C to stop.");
|
|
18
|
+
} catch (err) {
|
|
19
|
+
info(`Error: ${err.message}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
package/lib/cli/status.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
import { findEntryPoint, readMethodVersion, basename_of, pkg } from "./helpers.js";
|
|
4
|
-
|
|
5
|
-
export function register(program) {
|
|
6
|
-
program
|
|
7
|
-
.command("status [directory]")
|
|
8
|
-
.description("Check if your methodology version is current")
|
|
9
|
-
.option("--json", "Output as JSON")
|
|
10
|
-
.action(async (directory, opts) => {
|
|
11
|
-
directory = directory || ".";
|
|
12
|
-
const ep = findEntryPoint(directory);
|
|
13
|
-
if (!ep) {
|
|
14
|
-
console.error(
|
|
15
|
-
`No entry point found in ${directory} ` +
|
|
16
|
-
"(looked for CLAUDE.md, .cursorrules, AGENT.md)"
|
|
17
|
-
);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const epVersion = readMethodVersion(ep);
|
|
22
|
-
const installed = pkg.version;
|
|
23
|
-
|
|
24
|
-
let verStatus, message;
|
|
25
|
-
if (!epVersion) {
|
|
26
|
-
verStatus = "no_version";
|
|
27
|
-
message =
|
|
28
|
-
`Entry point ${basename_of(ep)} has no method_version setting. ` +
|
|
29
|
-
`Add 'method_version: ${installed.split(".").slice(0, 2).join(".")}' ` +
|
|
30
|
-
`to enable version tracking.`;
|
|
31
|
-
} else if (epVersion === installed.split(".").slice(0, 2).join(".")) {
|
|
32
|
-
verStatus = "current";
|
|
33
|
-
message =
|
|
34
|
-
`Entry point ${basename_of(ep)} is current ` +
|
|
35
|
-
`(method_version: ${epVersion}, installed: ${installed})`;
|
|
36
|
-
} else {
|
|
37
|
-
verStatus = "outdated";
|
|
38
|
-
message =
|
|
39
|
-
`Entry point ${basename_of(ep)} is outdated ` +
|
|
40
|
-
`(method_version: ${epVersion}, installed: ${installed}). ` +
|
|
41
|
-
`Run \`npx agent-method upgrade ${directory}\` to update.`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (opts.json) {
|
|
45
|
-
console.log(
|
|
46
|
-
JSON.stringify(
|
|
47
|
-
{
|
|
48
|
-
entry_point: ep,
|
|
49
|
-
entry_point_version: epVersion,
|
|
50
|
-
installed_version: installed,
|
|
51
|
-
status: verStatus,
|
|
52
|
-
},
|
|
53
|
-
null,
|
|
54
|
-
2
|
|
55
|
-
)
|
|
56
|
-
);
|
|
57
|
-
} else {
|
|
58
|
-
console.log(message);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
1
|
+
/** wwa status — check if methodology version is current. */
|
|
2
|
+
|
|
3
|
+
import { findEntryPoint, readMethodVersion, basename_of, pkg } from "./helpers.js";
|
|
4
|
+
|
|
5
|
+
export function register(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("status [directory]")
|
|
8
|
+
.description("Check if your methodology version is current")
|
|
9
|
+
.option("--json", "Output as JSON")
|
|
10
|
+
.action(async (directory, opts) => {
|
|
11
|
+
directory = directory || ".";
|
|
12
|
+
const ep = findEntryPoint(directory);
|
|
13
|
+
if (!ep) {
|
|
14
|
+
console.error(
|
|
15
|
+
`No entry point found in ${directory} ` +
|
|
16
|
+
"(looked for CLAUDE.md, .cursorrules, AGENT.md)"
|
|
17
|
+
);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const epVersion = readMethodVersion(ep);
|
|
22
|
+
const installed = pkg.version;
|
|
23
|
+
|
|
24
|
+
let verStatus, message;
|
|
25
|
+
if (!epVersion) {
|
|
26
|
+
verStatus = "no_version";
|
|
27
|
+
message =
|
|
28
|
+
`Entry point ${basename_of(ep)} has no method_version setting. ` +
|
|
29
|
+
`Add 'method_version: ${installed.split(".").slice(0, 2).join(".")}' ` +
|
|
30
|
+
`to enable version tracking.`;
|
|
31
|
+
} else if (epVersion === installed.split(".").slice(0, 2).join(".")) {
|
|
32
|
+
verStatus = "current";
|
|
33
|
+
message =
|
|
34
|
+
`Entry point ${basename_of(ep)} is current ` +
|
|
35
|
+
`(method_version: ${epVersion}, installed: ${installed})`;
|
|
36
|
+
} else {
|
|
37
|
+
verStatus = "outdated";
|
|
38
|
+
message =
|
|
39
|
+
`Entry point ${basename_of(ep)} is outdated ` +
|
|
40
|
+
`(method_version: ${epVersion}, installed: ${installed}). ` +
|
|
41
|
+
`Run \`npx agent-method upgrade ${directory}\` to update.`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (opts.json) {
|
|
45
|
+
console.log(
|
|
46
|
+
JSON.stringify(
|
|
47
|
+
{
|
|
48
|
+
entry_point: ep,
|
|
49
|
+
entry_point_version: epVersion,
|
|
50
|
+
installed_version: installed,
|
|
51
|
+
status: verStatus,
|
|
52
|
+
},
|
|
53
|
+
null,
|
|
54
|
+
2
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
} else {
|
|
58
|
+
console.log(message);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|