agent-method 1.5.5 → 1.5.6
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 +68 -40
- package/bin/wwa.js +35 -9
- package/docs/internal/doc-tokens.yaml +452 -0
- package/docs/internal/feature-registry.yaml +13 -1
- package/lib/cli/casestudy.js +691 -0
- package/lib/cli/check.js +1 -1
- package/lib/cli/close.js +446 -0
- package/lib/cli/completion.js +639 -0
- package/lib/cli/digest.js +66 -0
- package/lib/cli/docs.js +207 -0
- package/lib/cli/helpers.js +49 -2
- package/lib/cli/implement.js +159 -0
- package/lib/cli/init.js +25 -6
- package/lib/cli/plan.js +128 -0
- package/lib/cli/refine.js +4 -4
- package/lib/cli/review.js +68 -0
- package/lib/cli/scan.js +2 -2
- package/lib/cli/status.js +1 -1
- package/lib/cli/upgrade.js +8 -7
- package/lib/init.js +205 -23
- package/package.json +10 -3
- package/templates/README.md +70 -22
- package/templates/entry-points/.cursorrules +142 -13
- package/templates/entry-points/AGENT.md +142 -13
- package/templates/entry-points/CLAUDE.md +142 -13
- package/templates/extensions/analytical-system.md +1 -1
- package/templates/extensions/code-project.md +1 -1
- package/templates/extensions/data-exploration.md +1 -1
- package/templates/full/.context/BASE.md +33 -0
- package/templates/full/.context/METHODOLOGY.md +62 -5
- package/templates/full/.cursorrules +127 -17
- package/templates/full/AGENT.md +127 -17
- package/templates/full/CLAUDE.md +127 -17
- package/templates/full/Management/DIGEST.md +23 -0
- package/templates/full/Management/STATUS.md +46 -0
- package/templates/full/PROJECT.md +34 -0
- package/templates/full/Reviews/INDEX.md +41 -0
- package/templates/full/Reviews/backlog.md +52 -0
- package/templates/full/Reviews/plan.md +43 -0
- package/templates/full/Reviews/project.md +41 -0
- package/templates/full/Reviews/requirements.md +42 -0
- package/templates/full/Reviews/roadmap.md +41 -0
- package/templates/full/Reviews/state.md +56 -0
- package/templates/full/SUMMARY.md +7 -4
- package/templates/full/agentWorkflows/INDEX.md +42 -0
- package/templates/full/agentWorkflows/observations.md +65 -0
- package/templates/full/agentWorkflows/patterns.md +68 -0
- package/templates/full/agentWorkflows/sessions.md +92 -0
- package/templates/full/intro/README.md +39 -0
- package/templates/starter/.context/BASE.md +35 -0
- package/templates/starter/.context/METHODOLOGY.md +59 -5
- package/templates/starter/.cursorrules +134 -12
- package/templates/starter/AGENT.md +134 -12
- package/templates/starter/CLAUDE.md +134 -12
- package/templates/starter/Management/DIGEST.md +23 -0
- package/templates/starter/Management/STATUS.md +46 -0
- package/templates/starter/PROJECT.md +34 -0
- package/templates/starter/Reviews/INDEX.md +75 -0
- package/templates/starter/SUMMARY.md +27 -0
- package/templates/starter/agentWorkflows/INDEX.md +61 -0
- package/templates/starter/intro/README.md +37 -0
- package/templates/full/docs/index.md +0 -46
package/lib/cli/refine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** wwa refine — extract refinement report from session history. */
|
|
2
2
|
|
|
3
|
-
import { readFileSync, existsSync
|
|
4
|
-
import { findSessionLog } from "./helpers.js";
|
|
3
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
4
|
+
import { findSessionLog, safeWriteFile } from "./helpers.js";
|
|
5
5
|
|
|
6
6
|
export function register(program) {
|
|
7
7
|
program
|
|
@@ -15,7 +15,7 @@ export function register(program) {
|
|
|
15
15
|
if (!sessionLog) {
|
|
16
16
|
console.error(
|
|
17
17
|
"No SESSION-LOG.md found in current directory.\n" +
|
|
18
|
-
"Specify a path:
|
|
18
|
+
"Specify a path: wwa refine path/to/SESSION-LOG.md"
|
|
19
19
|
);
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
@@ -37,7 +37,7 @@ export function register(program) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (opts.output) {
|
|
40
|
-
|
|
40
|
+
safeWriteFile(opts.output, result, "utf-8");
|
|
41
41
|
console.log(
|
|
42
42
|
`Refinement report written to ${opts.output} (${parsed.entries.length} sessions)`
|
|
43
43
|
);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** wwa review — display project review dashboard. */
|
|
2
|
+
|
|
3
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
4
|
+
import { resolve, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export function register(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("review [directory]")
|
|
9
|
+
.description("Display project review dashboard from Reviews/INDEX.md")
|
|
10
|
+
.option("--json", "Output as JSON")
|
|
11
|
+
.action(async (directory, opts) => {
|
|
12
|
+
directory = directory || ".";
|
|
13
|
+
const d = resolve(directory);
|
|
14
|
+
|
|
15
|
+
const reviewIndex = join(d, "Reviews", "INDEX.md");
|
|
16
|
+
const hasReviews = existsSync(reviewIndex);
|
|
17
|
+
|
|
18
|
+
// Fallback: synthesize from intelligence layer files
|
|
19
|
+
const sources = {
|
|
20
|
+
roadmap: join(d, "ROADMAP.md"),
|
|
21
|
+
plan: join(d, "PLAN.md"),
|
|
22
|
+
state: join(d, "STATE.md"),
|
|
23
|
+
requirements: join(d, "REQUIREMENTS.md"),
|
|
24
|
+
backlog: join(d, "todos", "backlog.md"),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const available = {};
|
|
28
|
+
for (const [key, path] of Object.entries(sources)) {
|
|
29
|
+
available[key] = existsSync(path);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (opts.json) {
|
|
33
|
+
const data = { hasReviewsDir: hasReviews, available };
|
|
34
|
+
if (hasReviews) {
|
|
35
|
+
data.indexContent = readFileSync(reviewIndex, "utf-8");
|
|
36
|
+
}
|
|
37
|
+
console.log(JSON.stringify(data, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (hasReviews) {
|
|
42
|
+
// Display Reviews/INDEX.md content
|
|
43
|
+
const content = readFileSync(reviewIndex, "utf-8");
|
|
44
|
+
console.log(content);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Synthesize a quick review from available files
|
|
49
|
+
console.log("\n Project Review Dashboard\n");
|
|
50
|
+
console.log(" No Reviews/ directory found. Showing available sources:\n");
|
|
51
|
+
|
|
52
|
+
for (const [key, path] of Object.entries(sources)) {
|
|
53
|
+
if (available[key]) {
|
|
54
|
+
const content = readFileSync(path, "utf-8");
|
|
55
|
+
const lines = content.split("\n").length;
|
|
56
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] || key;
|
|
57
|
+
console.log(` [+] ${key.padEnd(14)} ${title} (${lines} lines)`);
|
|
58
|
+
} else {
|
|
59
|
+
console.log(` [ ] ${key.padEnd(14)} not found`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(
|
|
64
|
+
"\n To get full reviews, use the full template tier:" +
|
|
65
|
+
"\n wwa init <type> --tier full\n"
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
package/lib/cli/scan.js
CHANGED
|
@@ -17,9 +17,9 @@ export function register(program) {
|
|
|
17
17
|
const friendlyMap = { analytical: "context", mixed: "mix" };
|
|
18
18
|
if (ptype in friendlyMap) {
|
|
19
19
|
result.friendly_name = friendlyMap[ptype];
|
|
20
|
-
result.init_command = `
|
|
20
|
+
result.init_command = `wwa init ${friendlyMap[ptype]}`;
|
|
21
21
|
} else {
|
|
22
|
-
result.init_command = `
|
|
22
|
+
result.init_command = `wwa init ${ptype}`;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
console.log(`Scanning: ${directory}`);
|
package/lib/cli/status.js
CHANGED
|
@@ -38,7 +38,7 @@ export function register(program) {
|
|
|
38
38
|
message =
|
|
39
39
|
`Entry point ${basename_of(ep)} is outdated ` +
|
|
40
40
|
`(method_version: ${epVersion}, installed: ${installed}). ` +
|
|
41
|
-
`Run \`
|
|
41
|
+
`Run \`wwa upgrade ${directory}\` to update.`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if (opts.json) {
|
package/lib/cli/upgrade.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** wwa upgrade — brownfield-safe methodology update. */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
readFileSync, existsSync,
|
|
4
|
+
readFileSync, existsSync, mkdirSync,
|
|
5
5
|
} from "node:fs";
|
|
6
6
|
import { resolve, join, dirname } from "node:path";
|
|
7
7
|
import {
|
|
8
8
|
findEntryPoint, readMethodVersion, basename_of, pkg, packageRoot,
|
|
9
|
+
safeWriteFile, safeCopyFile,
|
|
9
10
|
} from "./helpers.js";
|
|
10
11
|
|
|
11
12
|
export function register(program) {
|
|
@@ -55,7 +56,7 @@ export function register(program) {
|
|
|
55
56
|
);
|
|
56
57
|
if (!opts.dryRun) {
|
|
57
58
|
content = content.replace(/(method_version:\s*)\S+/, `$1${newVer}`);
|
|
58
|
-
|
|
59
|
+
safeWriteFile(ep, content, "utf-8");
|
|
59
60
|
}
|
|
60
61
|
} else if (!currentVer) {
|
|
61
62
|
actions.push(`Add method_version: ${newVer} to ${basename_of(ep)}`);
|
|
@@ -65,9 +66,9 @@ export function register(program) {
|
|
|
65
66
|
`## Method version\n\n` +
|
|
66
67
|
`method_version: ${newVer}\n` +
|
|
67
68
|
`<!-- Tracks which methodology version generated this entry point -->\n` +
|
|
68
|
-
`<!-- Use \`
|
|
69
|
+
`<!-- Use \`wwa status\` to compare against latest -->\n\n`;
|
|
69
70
|
content = content.replace("## Conventions", insert + "## Conventions");
|
|
70
|
-
|
|
71
|
+
safeWriteFile(ep, content, "utf-8");
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -86,7 +87,7 @@ export function register(program) {
|
|
|
86
87
|
actions.push(`Create ${fname} (new)`);
|
|
87
88
|
if (!opts.dryRun) {
|
|
88
89
|
mkdirSync(dirname(dst), { recursive: true });
|
|
89
|
-
|
|
90
|
+
safeCopyFile(src, dst);
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -104,7 +105,7 @@ export function register(program) {
|
|
|
104
105
|
" \u2014 effort, ambiguity, context level, tokens, time, user response, refinement delta, workflow, features, cascades, friction, findings) " +
|
|
105
106
|
"|\n\n<!-- INSTRUCTION: Add project-specific cascade"
|
|
106
107
|
);
|
|
107
|
-
|
|
108
|
+
safeWriteFile(ep, content, "utf-8");
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
}
|
|
@@ -127,7 +128,7 @@ export function register(program) {
|
|
|
127
128
|
"previous entries during normal work\n\n" +
|
|
128
129
|
"## Do not"
|
|
129
130
|
);
|
|
130
|
-
|
|
131
|
+
safeWriteFile(ep, content, "utf-8");
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
}
|
package/lib/init.js
CHANGED
|
@@ -5,9 +5,23 @@
|
|
|
5
5
|
* prompts for project configuration.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync, mkdirSync,
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, copyFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { resolve, join, dirname, basename, relative } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { safeCopyFile, safeWriteFile } from "./cli/helpers.js";
|
|
12
|
+
|
|
13
|
+
/** Entry point filenames that bypass the extension-based safety check. */
|
|
14
|
+
const ENTRY_POINT_NAMES = new Set(["CLAUDE.md", ".cursorrules", "AGENT.md"]);
|
|
15
|
+
|
|
16
|
+
/** Write file — allows entry points (e.g. .cursorrules) plus safe extensions. */
|
|
17
|
+
function writeInitFile(filePath, content, encoding = "utf-8") {
|
|
18
|
+
const name = basename(filePath);
|
|
19
|
+
if (ENTRY_POINT_NAMES.has(name)) {
|
|
20
|
+
writeFileSync(filePath, content, encoding);
|
|
21
|
+
} else {
|
|
22
|
+
safeWriteFile(filePath, content, encoding);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
11
25
|
|
|
12
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
27
|
const __dirname = dirname(__filename);
|
|
@@ -36,7 +50,7 @@ const RUNTIME_ENTRY_POINTS = {
|
|
|
36
50
|
* Set up a new project with methodology templates.
|
|
37
51
|
*/
|
|
38
52
|
export async function initProject(projectType, directory, opts = {}) {
|
|
39
|
-
const { tier = "starter", runtime = "all", profile = "standard", registryPath } = opts;
|
|
53
|
+
const { tier = "starter", runtime = "all", profile = "standard", onboarding = "auto", registryPath } = opts;
|
|
40
54
|
const targetDir = resolve(directory);
|
|
41
55
|
const methodRoot = resolve(__dirname, "..");
|
|
42
56
|
|
|
@@ -47,16 +61,25 @@ export async function initProject(projectType, directory, opts = {}) {
|
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
console.log(`\nSetting up ${FRIENDLY_NAMES[projectType] || projectType} project`);
|
|
50
|
-
console.log(` Template:
|
|
51
|
-
console.log(` Profile:
|
|
52
|
-
console.log(` Runtime:
|
|
53
|
-
console.log(`
|
|
64
|
+
console.log(` Template: ${tier}`);
|
|
65
|
+
console.log(` Profile: ${profile}`);
|
|
66
|
+
console.log(` Runtime: ${runtime}`);
|
|
67
|
+
console.log(` Onboarding: ${onboarding}`);
|
|
68
|
+
console.log(` Target: ${targetDir}\n`);
|
|
69
|
+
|
|
70
|
+
// Determine brownfield mode from user selection or auto-detection
|
|
71
|
+
let isBrownfield;
|
|
72
|
+
if (onboarding === "brownfield") {
|
|
73
|
+
isBrownfield = true;
|
|
74
|
+
} else if (onboarding === "greenfield") {
|
|
75
|
+
isBrownfield = false;
|
|
76
|
+
} else {
|
|
77
|
+
// auto — detect from existing files
|
|
78
|
+
isBrownfield = detectBrownfield(targetDir);
|
|
79
|
+
}
|
|
54
80
|
|
|
55
|
-
// Detect brownfield (existing project with methodology files)
|
|
56
|
-
const isBrownfield = detectBrownfield(targetDir);
|
|
57
81
|
if (isBrownfield) {
|
|
58
|
-
console.log("
|
|
59
|
-
console.log(" Will append to existing entry points, not overwrite.\n");
|
|
82
|
+
console.log(" Brownfield mode — will append to existing entry points, not overwrite.\n");
|
|
60
83
|
}
|
|
61
84
|
|
|
62
85
|
// Create target directory
|
|
@@ -65,7 +88,7 @@ export async function initProject(projectType, directory, opts = {}) {
|
|
|
65
88
|
// Copy template files
|
|
66
89
|
const copied = [];
|
|
67
90
|
const skipped = [];
|
|
68
|
-
copyTemplateDir(templateDir, targetDir, copied, skipped, isBrownfield);
|
|
91
|
+
copyTemplateDir(templateDir, targetDir, copied, skipped, isBrownfield, runtime);
|
|
69
92
|
|
|
70
93
|
// Apply extensions based on project type
|
|
71
94
|
const extensions = getExtensions(projectType);
|
|
@@ -77,12 +100,21 @@ export async function initProject(projectType, directory, opts = {}) {
|
|
|
77
100
|
}
|
|
78
101
|
}
|
|
79
102
|
|
|
103
|
+
// Copy feature-registry.yaml to user project (populated, from package)
|
|
104
|
+
copyFeatureRegistry(methodRoot, targetDir, copied);
|
|
105
|
+
|
|
106
|
+
// Create project-specific doc-tokens.yaml with initial values
|
|
107
|
+
createProjectTokens(targetDir, projectType, tier, profile, onboarding, copied);
|
|
108
|
+
|
|
80
109
|
// Replace placeholders in entry points
|
|
81
110
|
replacePlaceholders(targetDir, projectType, tier);
|
|
82
111
|
|
|
83
112
|
// Set integration profile in entry points
|
|
84
113
|
setProfile(targetDir, profile);
|
|
85
114
|
|
|
115
|
+
// Set onboarding scenario in PROJECT-PROFILE.md
|
|
116
|
+
setOnboardingScenario(targetDir, onboarding, isBrownfield, projectType);
|
|
117
|
+
|
|
86
118
|
// Remove unused entry points based on runtime choice
|
|
87
119
|
const removed = removeUnusedEntryPoints(targetDir, runtime, isBrownfield);
|
|
88
120
|
|
|
@@ -104,14 +136,21 @@ export async function initProject(projectType, directory, opts = {}) {
|
|
|
104
136
|
: "CLAUDE.md / .cursorrules / AGENT.md";
|
|
105
137
|
|
|
106
138
|
console.log(`\nNext steps:`);
|
|
107
|
-
|
|
108
|
-
console.log(`
|
|
139
|
+
let step = 1;
|
|
140
|
+
console.log(` ${step++}. cd ${relative(process.cwd(), targetDir) || "."}`);
|
|
109
141
|
if (runtime === "all") {
|
|
110
|
-
console.log(`
|
|
142
|
+
console.log(` ${step++}. Delete the two entry point files you don't use`);
|
|
111
143
|
console.log(` (keep CLAUDE.md, .cursorrules, or AGENT.md)`);
|
|
112
144
|
}
|
|
113
|
-
console.log(` ${
|
|
114
|
-
|
|
145
|
+
console.log(` ${step++}. Start a conversation with your agent — it reads ${keptEntry} automatically`);
|
|
146
|
+
if (isBrownfield) {
|
|
147
|
+
console.log(` The agent will scan your codebase and walk you through 5 onboarding stages`);
|
|
148
|
+
console.log(` (scan → clarify → map → organize → starting state)`);
|
|
149
|
+
} else {
|
|
150
|
+
console.log(` The agent will walk you through 4 onboarding stages`);
|
|
151
|
+
console.log(` (vision → architecture → goals → starting state)`);
|
|
152
|
+
}
|
|
153
|
+
console.log(`\nVerify: wwa check`);
|
|
115
154
|
}
|
|
116
155
|
|
|
117
156
|
function detectBrownfield(dir) {
|
|
@@ -124,7 +163,7 @@ function detectBrownfield(dir) {
|
|
|
124
163
|
return count >= 2;
|
|
125
164
|
}
|
|
126
165
|
|
|
127
|
-
function copyTemplateDir(src, dst, copied, skipped, isBrownfield) {
|
|
166
|
+
function copyTemplateDir(src, dst, copied, skipped, isBrownfield, runtime) {
|
|
128
167
|
if (!existsSync(src)) return;
|
|
129
168
|
mkdirSync(dst, { recursive: true });
|
|
130
169
|
|
|
@@ -133,11 +172,24 @@ function copyTemplateDir(src, dst, copied, skipped, isBrownfield) {
|
|
|
133
172
|
const dstPath = join(dst, entry.name);
|
|
134
173
|
|
|
135
174
|
if (entry.isDirectory()) {
|
|
136
|
-
copyTemplateDir(srcPath, dstPath, copied, skipped, isBrownfield);
|
|
175
|
+
copyTemplateDir(srcPath, dstPath, copied, skipped, isBrownfield, runtime);
|
|
137
176
|
} else {
|
|
138
177
|
// Entry points: special handling in brownfield
|
|
139
178
|
const isEntryPoint = ["CLAUDE.md", ".cursorrules", "AGENT.md"].includes(entry.name);
|
|
140
179
|
|
|
180
|
+
// Skip entry points that won't be kept (avoids safety invariant issue with .cursorrules)
|
|
181
|
+
if (isEntryPoint && runtime !== "all" && !isBrownfield) {
|
|
182
|
+
const keep = RUNTIME_ENTRY_POINTS[runtime];
|
|
183
|
+
if (keep && entry.name !== keep) continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Defer heavy infrastructure files in brownfield to reduce first-session load
|
|
187
|
+
const isDeferredInBrownfield = isBrownfield && entry.name === "REGISTRY.md";
|
|
188
|
+
if (isDeferredInBrownfield) {
|
|
189
|
+
skipped.push(`${relative(dst, dstPath)} (deferred — created on first file split)`);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
141
193
|
if (existsSync(dstPath)) {
|
|
142
194
|
if (isEntryPoint && isBrownfield) {
|
|
143
195
|
// Append methodology sections to existing entry point
|
|
@@ -146,9 +198,13 @@ function copyTemplateDir(src, dst, copied, skipped, isBrownfield) {
|
|
|
146
198
|
} else {
|
|
147
199
|
skipped.push(relative(dst, dstPath));
|
|
148
200
|
}
|
|
149
|
-
} else {
|
|
201
|
+
} else if (isEntryPoint) {
|
|
202
|
+
// Entry points bypass safeCopyFile (e.g. .cursorrules has no .md extension)
|
|
150
203
|
copyFileSync(srcPath, dstPath);
|
|
151
204
|
copied.push(relative(dst, dstPath));
|
|
205
|
+
} else {
|
|
206
|
+
safeCopyFile(srcPath, dstPath);
|
|
207
|
+
copied.push(relative(dst, dstPath));
|
|
152
208
|
}
|
|
153
209
|
}
|
|
154
210
|
}
|
|
@@ -176,7 +232,7 @@ function appendToEntryPoint(srcPath, dstPath) {
|
|
|
176
232
|
}
|
|
177
233
|
}
|
|
178
234
|
|
|
179
|
-
|
|
235
|
+
writeInitFile(dstPath, dstContent, "utf-8");
|
|
180
236
|
}
|
|
181
237
|
|
|
182
238
|
function getExtensions(projectType) {
|
|
@@ -242,7 +298,7 @@ function mergeExtension(extFile, targetDir, isBrownfield) {
|
|
|
242
298
|
}
|
|
243
299
|
}
|
|
244
300
|
|
|
245
|
-
|
|
301
|
+
writeInitFile(epPath, epContent, "utf-8");
|
|
246
302
|
}
|
|
247
303
|
}
|
|
248
304
|
|
|
@@ -260,7 +316,7 @@ function replacePlaceholders(targetDir, projectType, tier) {
|
|
|
260
316
|
// Set template tier
|
|
261
317
|
content = content.replace(/template_tier:\s*\S+/, `template_tier: ${tier}`);
|
|
262
318
|
|
|
263
|
-
|
|
319
|
+
writeInitFile(epPath, content, "utf-8");
|
|
264
320
|
}
|
|
265
321
|
}
|
|
266
322
|
|
|
@@ -271,10 +327,136 @@ function setProfile(targetDir, profile) {
|
|
|
271
327
|
|
|
272
328
|
let content = readFileSync(epPath, "utf-8");
|
|
273
329
|
content = content.replace(/tier:\s*(lite|standard|full)/, `tier: ${profile}`);
|
|
274
|
-
|
|
330
|
+
writeInitFile(epPath, content, "utf-8");
|
|
275
331
|
}
|
|
276
332
|
}
|
|
277
333
|
|
|
334
|
+
function setOnboardingScenario(targetDir, onboarding, isBrownfield, projectType) {
|
|
335
|
+
const profilePath = join(targetDir, "PROJECT-PROFILE.md");
|
|
336
|
+
if (!existsSync(profilePath)) return;
|
|
337
|
+
|
|
338
|
+
let content = readFileSync(profilePath, "utf-8");
|
|
339
|
+
|
|
340
|
+
// Set lifecycle stage based on scenario
|
|
341
|
+
const stage = isBrownfield ? "discovery" : "design";
|
|
342
|
+
content = content.replace(/\*\*Stage\*\*:\s*\{stage\}/, `**Stage**: ${stage}`);
|
|
343
|
+
content = content.replace(/\*\*Maturity\*\*:\s*\{maturity\}/, `**Maturity**: new`);
|
|
344
|
+
|
|
345
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
346
|
+
content = content.replace(
|
|
347
|
+
/\*\*Last transition\*\*:\s*\{date\}.*$/m,
|
|
348
|
+
`**Last transition**: ${today} (setup -> ${stage})`
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
// Set project type
|
|
352
|
+
content = content.replace(/\[{project-type}\]/, `[${projectType}]`);
|
|
353
|
+
|
|
354
|
+
// Set onboarding hint so the agent knows which first-session flow
|
|
355
|
+
const scenario = onboarding === "auto"
|
|
356
|
+
? (isBrownfield ? "brownfield (auto-detected)" : "greenfield (auto-detected)")
|
|
357
|
+
: onboarding;
|
|
358
|
+
|
|
359
|
+
// Add onboarding scenario after lifecycle section
|
|
360
|
+
if (!content.includes("**Onboarding**")) {
|
|
361
|
+
content = content.replace(
|
|
362
|
+
/(\*\*Last transition\*\*:.*$)/m,
|
|
363
|
+
`$1\n- **Onboarding**: ${scenario}`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Set extension stack
|
|
368
|
+
const extensions = getExtensions(projectType);
|
|
369
|
+
const extNames = extensions.map(e => e.replace(".md", ""));
|
|
370
|
+
content = content.replace(/\[{extensions-applied}\]/, `[${extNames.join(", ") || "none"}]`);
|
|
371
|
+
|
|
372
|
+
for (const ext of ["code-project", "data-exploration", "analytical-system"]) {
|
|
373
|
+
const isActive = extNames.includes(ext) ? "yes" : "no";
|
|
374
|
+
const source = extNames.includes(ext) ? "wwa init" : "—";
|
|
375
|
+
content = content.replace(
|
|
376
|
+
new RegExp(`\\| ${ext} \\| \\{yes/no\\} \\| \\{setup\\.sh / manual\\} \\|`),
|
|
377
|
+
`| ${ext} | ${isActive} | ${source} |`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
safeWriteFile(profilePath, content, "utf-8");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function createProjectTokens(targetDir, projectType, tier, profile, onboarding, copied) {
|
|
385
|
+
const ctxDir = join(targetDir, ".context");
|
|
386
|
+
mkdirSync(ctxDir, { recursive: true });
|
|
387
|
+
const dst = join(ctxDir, "doc-tokens.yaml");
|
|
388
|
+
if (existsSync(dst)) return; // don't overwrite
|
|
389
|
+
|
|
390
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
391
|
+
|
|
392
|
+
// Count files that were actually created
|
|
393
|
+
const mdFiles = [];
|
|
394
|
+
const countMdFiles = (dir) => {
|
|
395
|
+
if (!existsSync(dir)) return;
|
|
396
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
397
|
+
const p = join(dir, entry.name);
|
|
398
|
+
if (entry.isDirectory()) countMdFiles(p);
|
|
399
|
+
else if (entry.name.endsWith(".md") || entry.name.endsWith(".yaml") || entry.name.endsWith(".yml")) {
|
|
400
|
+
mdFiles.push(relative(targetDir, p).replace(/\\/g, "/"));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
countMdFiles(targetDir);
|
|
405
|
+
|
|
406
|
+
const content = [
|
|
407
|
+
"# Project Token Registry",
|
|
408
|
+
"# Computed by wwa init. Updated by wwa close.",
|
|
409
|
+
"# Used by wwa casestudy for methodology reference data.",
|
|
410
|
+
"#",
|
|
411
|
+
"# Agent: read this before writing docs. Check values are current.",
|
|
412
|
+
"",
|
|
413
|
+
"tokens:",
|
|
414
|
+
"",
|
|
415
|
+
" # --- Project identity ---",
|
|
416
|
+
` project_type: ${projectType}`,
|
|
417
|
+
` template_tier: ${tier}`,
|
|
418
|
+
` integration_profile: ${profile}`,
|
|
419
|
+
` onboarding: ${onboarding}`,
|
|
420
|
+
` created: "${today}"`,
|
|
421
|
+
` last_close: "${today}"`,
|
|
422
|
+
"",
|
|
423
|
+
" # --- File counts (updated at close) ---",
|
|
424
|
+
` methodology_file_count: ${mdFiles.length}`,
|
|
425
|
+
" docs_file_count: 0",
|
|
426
|
+
" context_file_count: 0",
|
|
427
|
+
"",
|
|
428
|
+
" # --- Session metrics (updated at close) ---",
|
|
429
|
+
" session_count: 0",
|
|
430
|
+
" decision_count: 0",
|
|
431
|
+
" total_features_referenced: 0",
|
|
432
|
+
"",
|
|
433
|
+
" # --- Scale indicators (updated at close) ---",
|
|
434
|
+
" files_near_threshold: 0 # files with 250+ lines",
|
|
435
|
+
" files_over_threshold: 0 # files with 300+ lines (need splitting)",
|
|
436
|
+
"",
|
|
437
|
+
" # --- Docs coverage (updated at close) ---",
|
|
438
|
+
" docs_inventory_count: 0",
|
|
439
|
+
" docs_on_disk: 0",
|
|
440
|
+
" docs_missing: 0",
|
|
441
|
+
" component_mappings: 0",
|
|
442
|
+
"",
|
|
443
|
+
].join("\n");
|
|
444
|
+
|
|
445
|
+
safeWriteFile(dst, content, "utf-8");
|
|
446
|
+
copied.push(".context/doc-tokens.yaml");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function copyFeatureRegistry(methodRoot, targetDir, copied) {
|
|
450
|
+
const src = join(methodRoot, "docs", "internal", "feature-registry.yaml");
|
|
451
|
+
if (!existsSync(src)) return;
|
|
452
|
+
const ctxDir = join(targetDir, ".context");
|
|
453
|
+
mkdirSync(ctxDir, { recursive: true });
|
|
454
|
+
const dst = join(ctxDir, "feature-registry.yaml");
|
|
455
|
+
if (existsSync(dst)) return; // don't overwrite existing
|
|
456
|
+
safeCopyFile(src, dst);
|
|
457
|
+
copied.push(".context/feature-registry.yaml");
|
|
458
|
+
}
|
|
459
|
+
|
|
278
460
|
function removeUnusedEntryPoints(targetDir, runtime, isBrownfield) {
|
|
279
461
|
if (runtime === "all" || isBrownfield) return [];
|
|
280
462
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-method",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"description": "CLI tools for the wwa methodology — registry-driven routing, validation, and project setup for AI-agent-assisted development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
"context-engineering",
|
|
9
9
|
"methodology",
|
|
10
10
|
"developer-tools",
|
|
11
|
-
"cli"
|
|
11
|
+
"cli",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"cursor",
|
|
14
|
+
"gemini",
|
|
15
|
+
"codex",
|
|
16
|
+
"mcp",
|
|
17
|
+
"agent-framework"
|
|
12
18
|
],
|
|
13
19
|
"type": "module",
|
|
14
20
|
"license": "MIT",
|
|
@@ -21,7 +27,8 @@
|
|
|
21
27
|
"bin/",
|
|
22
28
|
"lib/",
|
|
23
29
|
"templates/",
|
|
24
|
-
"docs/internal/feature-registry.yaml"
|
|
30
|
+
"docs/internal/feature-registry.yaml",
|
|
31
|
+
"docs/internal/doc-tokens.yaml"
|
|
25
32
|
],
|
|
26
33
|
"main": "lib/registry.js",
|
|
27
34
|
"engines": {
|