mdboard 2.1.2 → 2.1.3
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/bin.js +62 -1
- package/package.json +1 -1
- package/src/cli/agentdocs.js +311 -0
- package/src/cli/init.js +34 -3
package/bin.js
CHANGED
|
@@ -81,6 +81,8 @@ if (command === 'create') {
|
|
|
81
81
|
handleList(resolveProjectDir(args), args.slice(1));
|
|
82
82
|
} else if (command === 'preset') {
|
|
83
83
|
require('./src/cli/preset').run(args.slice(1));
|
|
84
|
+
} else if (command === 'agentdocs') {
|
|
85
|
+
handleAgentDocs();
|
|
84
86
|
} else if (command === 'config') {
|
|
85
87
|
require('./src/cli/config').run(args.slice(1));
|
|
86
88
|
// readline keeps process alive; config.js calls process.exit(0) when done
|
|
@@ -96,6 +98,65 @@ if (command === 'create') {
|
|
|
96
98
|
process.exit(1);
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// --- Agent docs handler ---
|
|
102
|
+
|
|
103
|
+
function handleAgentDocs() {
|
|
104
|
+
const configEngine = require('./src/core/config');
|
|
105
|
+
const agentdocs = require('./src/cli/agentdocs');
|
|
106
|
+
const fs = require('fs');
|
|
107
|
+
const readline = require('readline');
|
|
108
|
+
|
|
109
|
+
const projectDir = process.cwd();
|
|
110
|
+
const projectMdboard = path.join(projectDir, 'project', 'mdboard.json');
|
|
111
|
+
|
|
112
|
+
if (!fs.existsSync(projectMdboard)) {
|
|
113
|
+
console.error('\n Error: no project found. Run `mdboard init` first.\n');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const cfg = configEngine.loadConfig(projectDir);
|
|
118
|
+
const preset = cfg._preset || configEngine.DEFAULT_PRESET;
|
|
119
|
+
|
|
120
|
+
// Read project name from PROJECT.md frontmatter
|
|
121
|
+
let projectName = path.basename(projectDir);
|
|
122
|
+
try {
|
|
123
|
+
const projMd = fs.readFileSync(path.join(projectDir, 'project', 'PROJECT.md'), 'utf-8');
|
|
124
|
+
const nameMatch = projMd.match(/^name:\s*"?([^"\n]+)"?/m);
|
|
125
|
+
if (nameMatch) projectName = nameMatch[1].trim();
|
|
126
|
+
} catch (e) { /* use directory name */ }
|
|
127
|
+
|
|
128
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(' Generate AI agent context files?');
|
|
131
|
+
console.log(' 1. Both CLAUDE.md + AGENTS.md (recommended)');
|
|
132
|
+
console.log(' 2. CLAUDE.md only (Claude Code)');
|
|
133
|
+
console.log(' 3. AGENTS.md only (Cursor, Copilot, etc.)');
|
|
134
|
+
rl.question(' Choice [1]: ', function (answer) {
|
|
135
|
+
rl.close();
|
|
136
|
+
const choice = (answer || '').trim() || '1';
|
|
137
|
+
let targets;
|
|
138
|
+
if (choice === '2') targets = ['claude'];
|
|
139
|
+
else if (choice === '3') targets = ['agents'];
|
|
140
|
+
else targets = ['claude', 'agents'];
|
|
141
|
+
|
|
142
|
+
// Check which files exist before writing
|
|
143
|
+
const existsBefore = {};
|
|
144
|
+
for (const t of targets) {
|
|
145
|
+
const fname = t === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
|
|
146
|
+
existsBefore[fname] = fs.existsSync(path.join(projectDir, fname));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const written = agentdocs.generate(projectDir, cfg, preset, projectName, targets);
|
|
150
|
+
console.log('');
|
|
151
|
+
for (const p of written) {
|
|
152
|
+
const fname = path.basename(p);
|
|
153
|
+
console.log(' ' + (existsBefore[fname] ? 'Updated' : 'Created') + ': ' + fname);
|
|
154
|
+
}
|
|
155
|
+
console.log('');
|
|
156
|
+
process.exit(0);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
99
160
|
// --- Cache handler ---
|
|
100
161
|
|
|
101
162
|
function handleCache() {
|
|
@@ -184,5 +245,5 @@ function printHelp() {
|
|
|
184
245
|
entityExamples = '\n CRUD examples:\n mdboard create <entity> "title" [--flags]\n mdboard update <entity> <id> --field value\n mdboard delete <entity> <id>\n mdboard list <entity>\n';
|
|
185
246
|
}
|
|
186
247
|
|
|
187
|
-
console.log('\n mdboard — Git-based project management dashboard\n\n Usage:\n mdboard Start the dashboard server\n mdboard init [name] Scaffold a new workspace\n mdboard create <entity> Create an entity\n mdboard update <entity> Update an entity\n mdboard delete <entity> Delete an entity\n mdboard list <entity> List entities\n mdboard status Generate status report\n mdboard preset Create & manage presets (scrum, kanban)\n mdboard config Setup preferences & AI skill\n mdboard skill Install AI skill to IDE/agent\n mdboard history list|clean Manage project switch history\n mdboard cache list|clean Manage remote source cache\n mdboard --version Print version\n mdboard --help Show this help\n' + entityExamples + '\n Options:\n --project <path> Workspace root (default: cwd)\n --preset <name> Methodology preset (shape-up, scrum, kanban)\n --config <path> Path to config directory\n --port <number> Server port (default: 3333)\n --workspace <path> Path to workspace.json\n');
|
|
248
|
+
console.log('\n mdboard — Git-based project management dashboard\n\n Usage:\n mdboard Start the dashboard server\n mdboard init [name] Scaffold a new workspace\n mdboard create <entity> Create an entity\n mdboard update <entity> Update an entity\n mdboard delete <entity> Delete an entity\n mdboard list <entity> List entities\n mdboard status Generate status report\n mdboard preset Create & manage presets (scrum, kanban)\n mdboard config Setup preferences & AI skill\n mdboard skill Install AI skill to IDE/agent\n mdboard agentdocs Generate CLAUDE.md / AGENTS.md context\n mdboard history list|clean Manage project switch history\n mdboard cache list|clean Manage remote source cache\n mdboard --version Print version\n mdboard --help Show this help\n' + entityExamples + '\n Options:\n --project <path> Workspace root (default: cwd)\n --preset <name> Methodology preset (shape-up, scrum, kanban)\n --config <path> Path to config directory\n --port <number> Server port (default: 3333)\n --workspace <path> Path to workspace.json\n');
|
|
188
249
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdboard",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "Git-based project management dashboard. Reads markdown files with YAML frontmatter and serves a visual kanban board, table, milestones, and metrics views.",
|
|
5
5
|
"main": "./src/server/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mdboard agentdocs — Generate CLAUDE.md / AGENTS.md context for AI agents
|
|
3
|
+
*
|
|
4
|
+
* Dynamically builds project management documentation from the loaded preset
|
|
5
|
+
* configuration so that any AI agent always has context about how to use mdboard.
|
|
6
|
+
*
|
|
7
|
+
* Called during `mdboard init` and also available as `mdboard agentdocs`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const configEngine = require('../core/config');
|
|
15
|
+
|
|
16
|
+
const SECTION_START = '<!-- mdboard:start -->';
|
|
17
|
+
const SECTION_END = '<!-- mdboard:end -->';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Content generation
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build the full markdown section from config.
|
|
25
|
+
* @param {object} cfg - Loaded config from configEngine.loadConfig()
|
|
26
|
+
* @param {string} preset - Preset name (e.g. "shape-up")
|
|
27
|
+
* @param {string} projectName - Workspace/project name
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
function generateSection(cfg, preset, projectName) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
|
|
33
|
+
lines.push(SECTION_START);
|
|
34
|
+
lines.push('');
|
|
35
|
+
lines.push('## mdboard — Project Management');
|
|
36
|
+
lines.push('');
|
|
37
|
+
lines.push('This project uses **mdboard** for project management. All board files live in `project/`.');
|
|
38
|
+
lines.push('');
|
|
39
|
+
|
|
40
|
+
// --- Config summary ---
|
|
41
|
+
lines.push('### Configuration');
|
|
42
|
+
lines.push('');
|
|
43
|
+
lines.push('- **Project**: ' + projectName);
|
|
44
|
+
lines.push('- **Preset**: ' + preset);
|
|
45
|
+
lines.push('- **Methodology**: ' + (cfg.entities && cfg.entities.methodology || preset));
|
|
46
|
+
lines.push('- **Root directory**: `project/`');
|
|
47
|
+
lines.push('');
|
|
48
|
+
|
|
49
|
+
// --- Hierarchy ---
|
|
50
|
+
const hierarchy = (cfg.structure && cfg.structure.hierarchy) || {};
|
|
51
|
+
const standalone = (cfg.structure && cfg.structure.standalone) || {};
|
|
52
|
+
const hierarchyChain = buildHierarchyChain(hierarchy);
|
|
53
|
+
const standaloneNames = Object.keys(standalone);
|
|
54
|
+
|
|
55
|
+
lines.push('### Entity hierarchy');
|
|
56
|
+
lines.push('');
|
|
57
|
+
if (hierarchyChain.length > 0) {
|
|
58
|
+
const entities = cfg.entities && cfg.entities.entities || {};
|
|
59
|
+
const chainLabels = hierarchyChain.map(t => {
|
|
60
|
+
const e = entities[t];
|
|
61
|
+
return e ? e.singular : t;
|
|
62
|
+
});
|
|
63
|
+
lines.push('```');
|
|
64
|
+
lines.push(chainLabels.join(' > '));
|
|
65
|
+
lines.push('```');
|
|
66
|
+
} else {
|
|
67
|
+
lines.push('No hierarchy (flat structure).');
|
|
68
|
+
}
|
|
69
|
+
if (standaloneNames.length > 0) {
|
|
70
|
+
const entities = cfg.entities && cfg.entities.entities || {};
|
|
71
|
+
const labels = standaloneNames.map(t => {
|
|
72
|
+
const e = entities[t];
|
|
73
|
+
return e ? '**' + e.singular + '**' : '**' + t + '**';
|
|
74
|
+
});
|
|
75
|
+
lines.push('');
|
|
76
|
+
lines.push('Standalone entities: ' + labels.join(', '));
|
|
77
|
+
}
|
|
78
|
+
lines.push('');
|
|
79
|
+
|
|
80
|
+
// --- File structure ---
|
|
81
|
+
lines.push('### File structure');
|
|
82
|
+
lines.push('');
|
|
83
|
+
lines.push('```');
|
|
84
|
+
const tree = buildFileTree(hierarchy, standalone, cfg.entities && cfg.entities.entities || {});
|
|
85
|
+
lines.push('project/');
|
|
86
|
+
for (const line of tree) {
|
|
87
|
+
lines.push(' ' + line);
|
|
88
|
+
}
|
|
89
|
+
lines.push('```');
|
|
90
|
+
lines.push('');
|
|
91
|
+
|
|
92
|
+
// --- Frontmatter enums ---
|
|
93
|
+
lines.push('### Frontmatter values (strict enums)');
|
|
94
|
+
lines.push('');
|
|
95
|
+
const enumTable = buildEnumTable(cfg);
|
|
96
|
+
if (enumTable.length > 0) {
|
|
97
|
+
lines.push('| Entity | Field | Allowed values |');
|
|
98
|
+
lines.push('|--------|-------|----------------|');
|
|
99
|
+
for (const row of enumTable) {
|
|
100
|
+
lines.push('| ' + row.entity + ' | ' + row.field + ' | ' + row.values + ' |');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
lines.push('');
|
|
104
|
+
|
|
105
|
+
// --- CLI commands ---
|
|
106
|
+
lines.push('### CLI commands');
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push('Always use the CLI to create/update/delete entities. Never edit IDs or prefixes manually.');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('```bash');
|
|
111
|
+
|
|
112
|
+
// Create commands
|
|
113
|
+
const allTypes = configEngine.getEntityTypes(cfg);
|
|
114
|
+
for (const type of allTypes) {
|
|
115
|
+
const entity = configEngine.getEntity(cfg, type);
|
|
116
|
+
if (!entity) continue;
|
|
117
|
+
const ancestors = configEngine.getAncestors(cfg, type);
|
|
118
|
+
const parentFlags = ancestors.map(a => '--' + a + ' <slug>').join(' ');
|
|
119
|
+
const cmd = 'mdboard create ' + type + ' "title"';
|
|
120
|
+
lines.push(cmd + (parentFlags ? ' ' + parentFlags : ''));
|
|
121
|
+
}
|
|
122
|
+
lines.push('');
|
|
123
|
+
|
|
124
|
+
// Update / list / delete
|
|
125
|
+
lines.push('# Update any field');
|
|
126
|
+
lines.push('mdboard update <type> <ID> --<field> <value>');
|
|
127
|
+
lines.push('');
|
|
128
|
+
lines.push('# List and delete');
|
|
129
|
+
lines.push('mdboard list <type>');
|
|
130
|
+
lines.push('mdboard delete <type> <ID>');
|
|
131
|
+
lines.push('');
|
|
132
|
+
|
|
133
|
+
// System commands
|
|
134
|
+
lines.push('# System');
|
|
135
|
+
lines.push('mdboard status # Project status report');
|
|
136
|
+
lines.push('```');
|
|
137
|
+
lines.push('');
|
|
138
|
+
|
|
139
|
+
// --- Rules ---
|
|
140
|
+
lines.push('### Rules');
|
|
141
|
+
lines.push('');
|
|
142
|
+
lines.push('- Parent flags (`--cycle`, `--bet`, etc.) use **slugs** (directory names), not IDs.');
|
|
143
|
+
lines.push('- Status/enum values are **strict** — only use the values listed above.');
|
|
144
|
+
lines.push('- Use `mdboard status` to get a quick summary of project state.');
|
|
145
|
+
lines.push('- Do not manually edit entity IDs or prefixes.');
|
|
146
|
+
lines.push('');
|
|
147
|
+
lines.push(SECTION_END);
|
|
148
|
+
|
|
149
|
+
return lines.join('\n');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Helpers
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Walk hierarchy to produce an ordered array of type names.
|
|
158
|
+
* e.g. ["cycle", "bet", "scope", "task"]
|
|
159
|
+
*/
|
|
160
|
+
function buildHierarchyChain(hierarchy) {
|
|
161
|
+
const chain = [];
|
|
162
|
+
function walk(node) {
|
|
163
|
+
for (const [type, def] of Object.entries(node)) {
|
|
164
|
+
chain.push(type);
|
|
165
|
+
if (def.children) walk(def.children);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
walk(hierarchy);
|
|
169
|
+
return chain;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Build a visual file tree array from hierarchy + standalone.
|
|
174
|
+
*/
|
|
175
|
+
function buildFileTree(hierarchy, standalone, entities) {
|
|
176
|
+
const lines = [];
|
|
177
|
+
|
|
178
|
+
function walkTree(node, indent) {
|
|
179
|
+
for (const [type, def] of Object.entries(node)) {
|
|
180
|
+
const entity = entities[type];
|
|
181
|
+
const isDir = entity && entity.file === 'README.md';
|
|
182
|
+
const isFile = entity && entity.file === 'PREFIX-NNN.md';
|
|
183
|
+
|
|
184
|
+
lines.push(indent + def.dir + '/');
|
|
185
|
+
if (isDir) {
|
|
186
|
+
// Directory-per-entity: show slug pattern
|
|
187
|
+
const slug = type + '-nnn/';
|
|
188
|
+
lines.push(indent + ' ' + slug);
|
|
189
|
+
if (def.children) {
|
|
190
|
+
walkTree(def.children, indent + ' ');
|
|
191
|
+
}
|
|
192
|
+
} else if (isFile && def.children) {
|
|
193
|
+
// File-per-entity but has children (unusual, handle anyway)
|
|
194
|
+
lines.push(indent + ' ' + (entity.prefix || type.toUpperCase()) + '-NNN.md');
|
|
195
|
+
walkTree(def.children, indent + ' ');
|
|
196
|
+
} else if (isFile) {
|
|
197
|
+
lines.push(indent + ' ' + (entity.prefix || type.toUpperCase()) + '-NNN.md');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
walkTree(hierarchy, '');
|
|
203
|
+
|
|
204
|
+
for (const [type, def] of Object.entries(standalone)) {
|
|
205
|
+
const entity = entities[type];
|
|
206
|
+
lines.push(def.dir + '/');
|
|
207
|
+
if (entity && entity.file === 'PREFIX-NNN.md') {
|
|
208
|
+
lines.push(' ' + (entity.prefix || type.toUpperCase()) + '-NNN.md');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return lines;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Build rows for the enum values table.
|
|
217
|
+
*/
|
|
218
|
+
function buildEnumTable(cfg) {
|
|
219
|
+
const rows = [];
|
|
220
|
+
const types = configEngine.getEntityTypes(cfg);
|
|
221
|
+
|
|
222
|
+
for (const type of types) {
|
|
223
|
+
const entity = configEngine.getEntity(cfg, type);
|
|
224
|
+
const fields = configEngine.getFields(cfg, type);
|
|
225
|
+
if (!entity || !fields) continue;
|
|
226
|
+
|
|
227
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
228
|
+
if (fieldDef.type !== 'enum') continue;
|
|
229
|
+
const values = fieldDef.values.map(v => '`' + v.key + '`').join(', ');
|
|
230
|
+
rows.push({
|
|
231
|
+
entity: entity.singular,
|
|
232
|
+
field: fieldName,
|
|
233
|
+
values: values
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return rows;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// File writing (create or append)
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Write or update agent docs in a target file.
|
|
247
|
+
* If the file exists and already has an mdboard section, replace it.
|
|
248
|
+
* If the file exists without a section, append it.
|
|
249
|
+
* If the file doesn't exist, create it.
|
|
250
|
+
*
|
|
251
|
+
* @param {string} filePath - Absolute path to the target file
|
|
252
|
+
* @param {string} section - Generated markdown section
|
|
253
|
+
*/
|
|
254
|
+
function writeAgentDoc(filePath, section) {
|
|
255
|
+
if (fs.existsSync(filePath)) {
|
|
256
|
+
let content = fs.readFileSync(filePath, 'utf-8');
|
|
257
|
+
const startIdx = content.indexOf(SECTION_START);
|
|
258
|
+
const endIdx = content.indexOf(SECTION_END);
|
|
259
|
+
|
|
260
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
261
|
+
// Replace existing section
|
|
262
|
+
content = content.substring(0, startIdx) + section + content.substring(endIdx + SECTION_END.length);
|
|
263
|
+
} else {
|
|
264
|
+
// Append
|
|
265
|
+
content = content.trimEnd() + '\n\n' + section + '\n';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
269
|
+
} else {
|
|
270
|
+
// Create new file
|
|
271
|
+
fs.writeFileSync(filePath, section + '\n', 'utf-8');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Public API
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Generate and write agent documentation files.
|
|
281
|
+
*
|
|
282
|
+
* @param {string} cwd - Working directory (repo root)
|
|
283
|
+
* @param {object} cfg - Loaded config
|
|
284
|
+
* @param {string} preset - Preset name
|
|
285
|
+
* @param {string} projectName - Project/workspace name
|
|
286
|
+
* @param {string[]} targets - Which files to generate: ['claude', 'agents']
|
|
287
|
+
* @returns {string[]} - Paths of files written
|
|
288
|
+
*/
|
|
289
|
+
function generate(cwd, cfg, preset, projectName, targets) {
|
|
290
|
+
const section = generateSection(cfg, preset, projectName);
|
|
291
|
+
const written = [];
|
|
292
|
+
|
|
293
|
+
for (const target of targets) {
|
|
294
|
+
let filename;
|
|
295
|
+
if (target === 'claude') {
|
|
296
|
+
filename = 'CLAUDE.md';
|
|
297
|
+
} else if (target === 'agents') {
|
|
298
|
+
filename = 'AGENTS.md';
|
|
299
|
+
} else {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const filePath = path.join(cwd, filename);
|
|
304
|
+
writeAgentDoc(filePath, section);
|
|
305
|
+
written.push(filePath);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return written;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = { generate, generateSection, writeAgentDoc, SECTION_START, SECTION_END };
|
package/src/cli/init.js
CHANGED
|
@@ -14,6 +14,7 @@ const fs = require('fs');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const readline = require('readline');
|
|
16
16
|
const configEngine = require('../core/config');
|
|
17
|
+
const agentdocs = require('./agentdocs');
|
|
17
18
|
|
|
18
19
|
const cwd = process.cwd();
|
|
19
20
|
const projectPath = path.join(cwd, 'project');
|
|
@@ -50,17 +51,39 @@ if (!fs.existsSync(presetDir)) {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
if (nameArg) {
|
|
53
|
-
scaffold(nameArg);
|
|
54
|
+
askAgentTargets(function (targets) { scaffold(nameArg, targets); });
|
|
54
55
|
} else {
|
|
55
56
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
56
57
|
rl.question(' Workspace name: ', function (answer) {
|
|
57
58
|
rl.close();
|
|
58
59
|
const name = answer.trim() || path.basename(cwd);
|
|
59
|
-
scaffold(name);
|
|
60
|
+
askAgentTargets(function (targets) { scaffold(name, targets); });
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Prompt user to select which agent doc files to generate.
|
|
66
|
+
* @param {function} cb - Callback receiving string[] of targets
|
|
67
|
+
*/
|
|
68
|
+
function askAgentTargets(cb) {
|
|
69
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(' Generate AI agent context files?');
|
|
72
|
+
console.log(' 1. Both CLAUDE.md + AGENTS.md (recommended)');
|
|
73
|
+
console.log(' 2. CLAUDE.md only (Claude Code)');
|
|
74
|
+
console.log(' 3. AGENTS.md only (Cursor, Copilot, etc.)');
|
|
75
|
+
console.log(' 4. Skip');
|
|
76
|
+
rl.question(' Choice [1]: ', function (answer) {
|
|
77
|
+
rl.close();
|
|
78
|
+
const choice = (answer || '').trim() || '1';
|
|
79
|
+
if (choice === '2') return cb(['claude']);
|
|
80
|
+
if (choice === '3') return cb(['agents']);
|
|
81
|
+
if (choice === '4') return cb([]);
|
|
82
|
+
return cb(['claude', 'agents']);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function scaffold(name, agentTargets) {
|
|
64
87
|
// Load preset config
|
|
65
88
|
const cfg = configEngine.loadConfig(null);
|
|
66
89
|
const structure = cfg.structure || {};
|
|
@@ -119,6 +142,14 @@ function scaffold(name) {
|
|
|
119
142
|
console.log(' project/mdboard.css');
|
|
120
143
|
console.log(' workspace.json');
|
|
121
144
|
|
|
145
|
+
// Generate AI agent documentation files
|
|
146
|
+
if (agentTargets && agentTargets.length > 0) {
|
|
147
|
+
const written = agentdocs.generate(cwd, cfg, preset, name, agentTargets);
|
|
148
|
+
for (const p of written) {
|
|
149
|
+
console.log(' ' + path.basename(p));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
122
153
|
console.log('\n Next steps:');
|
|
123
154
|
console.log(' 1. Edit project/PROJECT.md with your project details');
|
|
124
155
|
|