dme-agent 7.4.1 → 7.4.2
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 +43 -44
- package/assets/agent/AGENTS.md +156 -0
- package/assets/agent/PROMPT-COMPACT.md +12 -15
- package/assets/agent/dme-v7.4.md +12 -14
- package/bin/dme.mjs +149 -55
- package/package.json +5 -4
- package/src/detect.mjs +260 -0
- package/src/install.mjs +352 -167
- package/src/postinstall-hint.mjs +3 -2
- package/src/targets.mjs +39 -13
- package/src/tui.mjs +352 -0
- package/src/ui.mjs +139 -52
package/src/install.mjs
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DME bootstrap installer —
|
|
3
|
-
* into every major agent CLI surface.
|
|
2
|
+
* DME bootstrap installer — detect-only by default, AGENTS.md conflict prompts.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import fs from "node:fs";
|
|
7
6
|
import path from "node:path";
|
|
8
7
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { resolveInstallTargets, getDetectedTargets, formatScanReport } from "./detect.mjs";
|
|
9
|
+
import { getTargets } from "./targets.mjs";
|
|
10
|
+
import {
|
|
11
|
+
ok,
|
|
12
|
+
fail,
|
|
13
|
+
skip,
|
|
14
|
+
info,
|
|
15
|
+
warn,
|
|
16
|
+
step,
|
|
17
|
+
hr,
|
|
18
|
+
box,
|
|
19
|
+
table,
|
|
20
|
+
progressBar,
|
|
21
|
+
asciiLogo,
|
|
22
|
+
c,
|
|
23
|
+
} from "./ui.mjs";
|
|
24
|
+
import { agentsConflictPromptInline, multiSelect, printScanTable } from "./tui.mjs";
|
|
11
25
|
|
|
12
26
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
27
|
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
14
28
|
const ASSETS = path.join(PKG_ROOT, "assets");
|
|
29
|
+
const VERSION = "7.4.2";
|
|
30
|
+
const MARKER_BEGIN = "<!-- DME-AGENT-7.4 BEGIN -->";
|
|
31
|
+
const MARKER_END = "<!-- DME-AGENT-7.4 END -->";
|
|
15
32
|
|
|
16
33
|
const SKILL_NAMES = [
|
|
17
34
|
"dme-design",
|
|
@@ -33,45 +50,34 @@ function readAsset(...parts) {
|
|
|
33
50
|
return fs.readFileSync(p, "utf8");
|
|
34
51
|
}
|
|
35
52
|
|
|
36
|
-
function
|
|
53
|
+
function escapeRe(s) {
|
|
54
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function writeRaw(filePath, content) {
|
|
37
58
|
ensureDir(path.dirname(filePath));
|
|
38
|
-
if (appendMarker && fs.existsSync(filePath)) {
|
|
39
|
-
const existing = fs.readFileSync(filePath, "utf8");
|
|
40
|
-
if (existing.includes(appendMarker)) {
|
|
41
|
-
// replace block between markers
|
|
42
|
-
const re = new RegExp(
|
|
43
|
-
`${escapeRe(appendMarker)}[\\s\\S]*?${escapeRe(appendMarker.replace("BEGIN", "END"))}`,
|
|
44
|
-
"m"
|
|
45
|
-
);
|
|
46
|
-
const endMarker = appendMarker.replace("BEGIN", "END");
|
|
47
|
-
const block = `${appendMarker}\n${content.trim()}\n${endMarker}\n`;
|
|
48
|
-
if (re.test(existing)) {
|
|
49
|
-
fs.writeFileSync(filePath, existing.replace(re, block.trimEnd() + "\n"), "utf8");
|
|
50
|
-
} else {
|
|
51
|
-
fs.writeFileSync(
|
|
52
|
-
filePath,
|
|
53
|
-
existing.trimEnd() + "\n\n" + block,
|
|
54
|
-
"utf8"
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return "updated";
|
|
58
|
-
}
|
|
59
|
-
const endMarker = appendMarker.replace("BEGIN", "END");
|
|
60
|
-
fs.writeFileSync(
|
|
61
|
-
filePath,
|
|
62
|
-
existing.trimEnd() +
|
|
63
|
-
"\n\n" +
|
|
64
|
-
`${appendMarker}\n${content.trim()}\n${endMarker}\n`,
|
|
65
|
-
"utf8"
|
|
66
|
-
);
|
|
67
|
-
return "appended";
|
|
68
|
-
}
|
|
69
59
|
fs.writeFileSync(filePath, content, "utf8");
|
|
70
|
-
return
|
|
60
|
+
return "written";
|
|
71
61
|
}
|
|
72
62
|
|
|
73
|
-
function
|
|
74
|
-
|
|
63
|
+
function mergeBlock(filePath, content) {
|
|
64
|
+
ensureDir(path.dirname(filePath));
|
|
65
|
+
const block = `${MARKER_BEGIN}\n${content.trim()}\n${MARKER_END}\n`;
|
|
66
|
+
if (!fs.existsSync(filePath)) {
|
|
67
|
+
fs.writeFileSync(filePath, block, "utf8");
|
|
68
|
+
return "created";
|
|
69
|
+
}
|
|
70
|
+
const existing = fs.readFileSync(filePath, "utf8");
|
|
71
|
+
const re = new RegExp(
|
|
72
|
+
`${escapeRe(MARKER_BEGIN)}[\\s\\S]*?${escapeRe(MARKER_END)}\\n?`,
|
|
73
|
+
"m"
|
|
74
|
+
);
|
|
75
|
+
if (re.test(existing)) {
|
|
76
|
+
fs.writeFileSync(filePath, existing.replace(re, block), "utf8");
|
|
77
|
+
return "updated";
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(filePath, existing.trimEnd() + "\n\n" + block, "utf8");
|
|
80
|
+
return "appended";
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
function copySkill(skillName, destRoot) {
|
|
@@ -92,15 +98,22 @@ function systemPrompt() {
|
|
|
92
98
|
);
|
|
93
99
|
}
|
|
94
100
|
|
|
101
|
+
function agentsDoctrine() {
|
|
102
|
+
return (
|
|
103
|
+
readAsset("agent", "AGENTS.md") ||
|
|
104
|
+
readAsset("agent", "PROMPT-COMPACT.md") ||
|
|
105
|
+
systemPrompt()
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
95
109
|
function compactPrompt() {
|
|
96
|
-
return readAsset("agent", "PROMPT-COMPACT.md") ||
|
|
110
|
+
return readAsset("agent", "PROMPT-COMPACT.md") || agentsDoctrine();
|
|
97
111
|
}
|
|
98
112
|
|
|
99
113
|
function manifesto() {
|
|
100
114
|
return (
|
|
101
115
|
readAsset("manifesto", "DME-Agent-v7.4.md") ||
|
|
102
|
-
|
|
103
|
-
systemPrompt()
|
|
116
|
+
agentsDoctrine()
|
|
104
117
|
);
|
|
105
118
|
}
|
|
106
119
|
|
|
@@ -110,7 +123,7 @@ function agentProfile() {
|
|
|
110
123
|
|
|
111
124
|
function cursorRule() {
|
|
112
125
|
return `---
|
|
113
|
-
description: DME Agent 7.4 — Neominimal Identity Engine. Apply on any UI/UX/frontend work.
|
|
126
|
+
description: DME Agent 7.4.2 — Neominimal Identity Engine. Apply on any UI/UX/frontend work.
|
|
114
127
|
globs:
|
|
115
128
|
- "**/*.{tsx,jsx,vue,svelte,css,html}"
|
|
116
129
|
- "**/components/**"
|
|
@@ -119,38 +132,185 @@ globs:
|
|
|
119
132
|
alwaysApply: false
|
|
120
133
|
---
|
|
121
134
|
|
|
122
|
-
${
|
|
135
|
+
${agentsDoctrine()}
|
|
123
136
|
`;
|
|
124
137
|
}
|
|
125
138
|
|
|
126
|
-
function
|
|
127
|
-
|
|
139
|
+
function isAgentsLikePath(filePath) {
|
|
140
|
+
const base = path.basename(filePath).toLowerCase();
|
|
141
|
+
return (
|
|
142
|
+
base === "agents.md" ||
|
|
143
|
+
base === "claude.md" ||
|
|
144
|
+
base === "gemini.md" ||
|
|
145
|
+
base === "conventions.md" ||
|
|
146
|
+
base === "copilot-instructions.md" ||
|
|
147
|
+
base === ".cursorrules" ||
|
|
148
|
+
base === ".windsurfrules"
|
|
149
|
+
);
|
|
128
150
|
}
|
|
129
151
|
|
|
130
152
|
/**
|
|
131
|
-
*
|
|
153
|
+
* Resolve policy for an existing AGENTS-like file.
|
|
154
|
+
* @returns {Promise<"merge"|"overwrite"|"skip">}
|
|
155
|
+
*/
|
|
156
|
+
async function resolveAgentsPolicy(filePath, opts) {
|
|
157
|
+
if (opts.agentsPolicy && opts.agentsPolicy !== "ask") {
|
|
158
|
+
return opts.agentsPolicy;
|
|
159
|
+
}
|
|
160
|
+
if (opts.force) return "merge";
|
|
161
|
+
if (opts.dryRun) return "merge";
|
|
162
|
+
if (!fs.existsSync(filePath)) return "overwrite"; // new file — write full doctrine
|
|
163
|
+
|
|
164
|
+
const existing = fs.readFileSync(filePath, "utf8");
|
|
165
|
+
// already only DME? safe to overwrite
|
|
166
|
+
if (
|
|
167
|
+
existing.includes("DME Agent 7.4") &&
|
|
168
|
+
existing.includes("Neominimal Identity") &&
|
|
169
|
+
existing.length < 12000 &&
|
|
170
|
+
!existing.includes(MARKER_BEGIN)
|
|
171
|
+
) {
|
|
172
|
+
// might be previous full DME — overwrite with stronger version
|
|
173
|
+
return "overwrite";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (opts.quiet) return "merge";
|
|
177
|
+
|
|
178
|
+
return agentsConflictPromptInline(filePath);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function writeAgentsFile(filePath, doctrine, opts, row) {
|
|
182
|
+
if (opts.dryRun) {
|
|
183
|
+
row.files.push(`[dry] agents → ${filePath}`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!fs.existsSync(filePath)) {
|
|
188
|
+
writeRaw(filePath, doctrine);
|
|
189
|
+
row.files.push(`${filePath} (created)`);
|
|
190
|
+
console.log(ok(`AGENTS created ${filePath}`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const policy = await resolveAgentsPolicy(filePath, opts);
|
|
195
|
+
if (policy === "skip") {
|
|
196
|
+
row.files.push(`${filePath} (skipped)`);
|
|
197
|
+
console.log(skip(`AGENTS skipped ${filePath}`));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (policy === "overwrite") {
|
|
201
|
+
writeRaw(filePath, doctrine);
|
|
202
|
+
row.files.push(`${filePath} (overwritten)`);
|
|
203
|
+
console.log(warn(`AGENTS overwrite ${filePath}`));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// merge
|
|
207
|
+
const mode = mergeBlock(filePath, doctrine);
|
|
208
|
+
row.files.push(`${filePath} (${mode})`);
|
|
209
|
+
console.log(ok(`AGENTS ${mode.padEnd(9)} ${filePath}`));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {{
|
|
214
|
+
* targets?: string[],
|
|
215
|
+
* force?: boolean,
|
|
216
|
+
* dryRun?: boolean,
|
|
217
|
+
* cwdProject?: boolean,
|
|
218
|
+
* agentsPolicy?: "ask"|"merge"|"overwrite"|"skip",
|
|
219
|
+
* interactive?: boolean,
|
|
220
|
+
* quiet?: boolean,
|
|
221
|
+
* pick?: boolean,
|
|
222
|
+
* }} opts
|
|
132
223
|
*/
|
|
133
224
|
export async function installDme(opts = {}) {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
225
|
+
const interactive = opts.interactive !== false && process.stdin.isTTY;
|
|
226
|
+
let targetIds = opts.targets;
|
|
227
|
+
|
|
228
|
+
// Default = detected only
|
|
229
|
+
if (!targetIds || targetIds.length === 0) {
|
|
230
|
+
targetIds = ["detected"];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let targets = resolveInstallTargets(targetIds, { force: opts.force });
|
|
234
|
+
|
|
235
|
+
// Interactive multi-select among detected
|
|
236
|
+
if (
|
|
237
|
+
interactive &&
|
|
238
|
+
opts.pick !== false &&
|
|
239
|
+
(targetIds.includes("detected") || targetIds.length === 0) &&
|
|
240
|
+
!opts.quiet
|
|
241
|
+
) {
|
|
242
|
+
const detected = getDetectedTargets();
|
|
243
|
+
if (detected.length === 0) {
|
|
244
|
+
console.log(asciiLogo(VERSION));
|
|
245
|
+
console.log(
|
|
246
|
+
fail("No AI CLIs detected on this machine.")
|
|
247
|
+
);
|
|
248
|
+
console.log(
|
|
249
|
+
info("Run with --targets all or --targets claude,cursor to force.")
|
|
250
|
+
);
|
|
251
|
+
console.log(info("Or: dme scan to see the detection report."));
|
|
252
|
+
console.log("");
|
|
253
|
+
return { ok: false, results: [], reason: "none-detected" };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const selected = await multiSelect(
|
|
257
|
+
"Select CLIs to install DME into",
|
|
258
|
+
detected.map((t) => ({
|
|
259
|
+
id: t.id,
|
|
260
|
+
label: t.name,
|
|
261
|
+
detail: t.agentsMd ? path.basename(t.agentsMd) : "",
|
|
262
|
+
checked: true,
|
|
263
|
+
}))
|
|
264
|
+
);
|
|
137
265
|
|
|
138
|
-
|
|
266
|
+
if (selected.length === 0) {
|
|
267
|
+
console.log(warn("Nothing selected. Abort."));
|
|
268
|
+
return { ok: false, results: [], reason: "none-selected" };
|
|
269
|
+
}
|
|
270
|
+
targets = resolveInstallTargets(selected, { force: true });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (targets.length === 0) {
|
|
274
|
+
console.log(fail("No targets to install."));
|
|
275
|
+
console.log(info("dme scan — see detection"));
|
|
276
|
+
console.log(info("dme install --targets all — force every surface"));
|
|
277
|
+
return { ok: false, results: [] };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const results = [];
|
|
281
|
+
const total = 5;
|
|
282
|
+
const doctrine = agentsDoctrine();
|
|
139
283
|
const sys = systemPrompt();
|
|
140
|
-
const compact = compactPrompt();
|
|
141
284
|
const full = manifesto();
|
|
142
285
|
const profile = agentProfile();
|
|
286
|
+
const compact = compactPrompt();
|
|
143
287
|
|
|
144
|
-
|
|
145
|
-
|
|
288
|
+
console.log(asciiLogo(VERSION));
|
|
289
|
+
console.log(hr());
|
|
290
|
+
console.log(step(1, total, "Loading DME doctrine…"));
|
|
291
|
+
if (!doctrine || doctrine.length < 80) {
|
|
292
|
+
console.log(fail("AGENTS.md asset missing."));
|
|
146
293
|
return { ok: false, results };
|
|
147
294
|
}
|
|
148
|
-
console.log(
|
|
295
|
+
console.log(
|
|
296
|
+
ok(
|
|
297
|
+
`doctrine ${doctrine.length} chars · manifesto ${full.length} · system ${sys.length}`
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
console.log(
|
|
301
|
+
info(
|
|
302
|
+
`targets: ${targets.map((t) => t.id).join(", ")} ${c.dim}(detected-only by default)${c.reset}`
|
|
303
|
+
)
|
|
304
|
+
);
|
|
149
305
|
console.log(hr());
|
|
150
306
|
|
|
151
|
-
console.log(step(2, total, `Installing
|
|
152
|
-
|
|
307
|
+
console.log(step(2, total, `Installing ${targets.length} surface(s)…`));
|
|
308
|
+
let i = 0;
|
|
153
309
|
for (const t of targets) {
|
|
310
|
+
i++;
|
|
311
|
+
console.log(
|
|
312
|
+
`\n ${c.brightMagenta}${c.bold}▸ ${t.name}${c.reset} ${c.dim}${t.id}${c.reset} ${progressBar(i, targets.length, 16)}`
|
|
313
|
+
);
|
|
154
314
|
const row = { id: t.id, name: t.name, files: [], errors: [] };
|
|
155
315
|
try {
|
|
156
316
|
// skills
|
|
@@ -164,6 +324,7 @@ export async function installDme(opts = {}) {
|
|
|
164
324
|
const dest = copySkill(skill, skillDir);
|
|
165
325
|
if (dest) row.files.push(dest);
|
|
166
326
|
}
|
|
327
|
+
console.log(ok(`skills → ${skillDir}`));
|
|
167
328
|
}
|
|
168
329
|
|
|
169
330
|
// agent profiles
|
|
@@ -172,82 +333,68 @@ export async function installDme(opts = {}) {
|
|
|
172
333
|
row.files.push(`[dry] agent → ${af}`);
|
|
173
334
|
continue;
|
|
174
335
|
}
|
|
175
|
-
const content =
|
|
176
|
-
af.endsWith(".mdc") || af.includes("rules")
|
|
177
|
-
? cursorRule()
|
|
178
|
-
: profile.includes("name: dme")
|
|
179
|
-
? profile
|
|
180
|
-
: profile;
|
|
181
|
-
writeFile(af, af.includes("cursor") && af.endsWith(".md") ? profile : content);
|
|
182
|
-
// for agent md files use profile; for CLAUDE_DME use full system
|
|
183
336
|
if (af.includes("CLAUDE_DME") || af.includes("instructions")) {
|
|
184
|
-
|
|
185
|
-
} else
|
|
186
|
-
|
|
337
|
+
writeRaw(af, sys);
|
|
338
|
+
} else {
|
|
339
|
+
writeRaw(af, profile);
|
|
187
340
|
}
|
|
188
341
|
row.files.push(af);
|
|
189
342
|
}
|
|
190
343
|
|
|
191
|
-
//
|
|
344
|
+
// Canonical AGENTS.md for this CLI
|
|
345
|
+
if (t.agentsMd) {
|
|
346
|
+
await writeAgentsFile(t.agentsMd, doctrine, opts, row);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Other rules files
|
|
192
350
|
for (const rf of t.rulesFiles) {
|
|
193
351
|
if (opts.dryRun) {
|
|
194
352
|
row.files.push(`[dry] rules → ${rf}`);
|
|
195
353
|
continue;
|
|
196
354
|
}
|
|
355
|
+
// skip if same as agentsMd (already handled)
|
|
356
|
+
if (t.agentsMd && path.resolve(rf) === path.resolve(t.agentsMd)) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
|
|
197
360
|
ensureDir(path.dirname(rf));
|
|
198
361
|
const base = path.basename(rf).toLowerCase();
|
|
199
362
|
|
|
200
363
|
if (base === "dme-agent.mdc" || rf.endsWith(".mdc")) {
|
|
201
|
-
|
|
364
|
+
writeRaw(rf, cursorRule());
|
|
202
365
|
row.files.push(rf);
|
|
203
366
|
continue;
|
|
204
367
|
}
|
|
205
368
|
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
base === "agents.md" ||
|
|
209
|
-
base === "agents.md" ||
|
|
210
|
-
base === ".cursorrules" ||
|
|
211
|
-
base === ".windsurfrules" ||
|
|
212
|
-
base === "gemini.md" ||
|
|
213
|
-
base === "copilot-instructions.md" ||
|
|
214
|
-
base === "dme-conventions.md" ||
|
|
215
|
-
base.endsWith("agents.md")
|
|
216
|
-
) {
|
|
217
|
-
// append DME block to shared rules so we don't wipe user content
|
|
218
|
-
const marker = "<!-- DME-AGENT-7.4 BEGIN -->";
|
|
219
|
-
writeFile(rf, rulesSnippet(), { appendMarker: marker });
|
|
220
|
-
row.files.push(rf + " (merged)");
|
|
369
|
+
if (isAgentsLikePath(rf)) {
|
|
370
|
+
await writeAgentsFile(rf, doctrine, opts, row);
|
|
221
371
|
continue;
|
|
222
372
|
}
|
|
223
373
|
|
|
224
374
|
if (base.includes("dme") || base.includes("conventions")) {
|
|
225
|
-
|
|
375
|
+
writeRaw(rf, sys);
|
|
226
376
|
row.files.push(rf);
|
|
227
377
|
continue;
|
|
228
378
|
}
|
|
229
379
|
|
|
230
|
-
|
|
380
|
+
writeRaw(rf, compact);
|
|
231
381
|
row.files.push(rf);
|
|
232
382
|
}
|
|
233
383
|
|
|
234
|
-
//
|
|
384
|
+
// manifesto + system in CLI root
|
|
235
385
|
if (!opts.dryRun && t.root) {
|
|
236
386
|
try {
|
|
237
387
|
ensureDir(t.root);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
row.files.push(
|
|
241
|
-
const sysPath = path.join(t.root, "DME-SYSTEM.md");
|
|
242
|
-
writeFile(sysPath, sys);
|
|
243
|
-
row.files.push(sysPath);
|
|
388
|
+
writeRaw(path.join(t.root, "DME-Agent-v7.4.md"), full);
|
|
389
|
+
writeRaw(path.join(t.root, "DME-SYSTEM.md"), sys);
|
|
390
|
+
row.files.push(path.join(t.root, "DME-Agent-v7.4.md"));
|
|
244
391
|
} catch {
|
|
245
|
-
/* ignore
|
|
392
|
+
/* ignore */
|
|
246
393
|
}
|
|
247
394
|
}
|
|
248
395
|
|
|
249
396
|
results.push({ ...row, status: "ok" });
|
|
250
|
-
console.log(ok(`${t.name
|
|
397
|
+
console.log(ok(`${t.name} done — ${row.files.length} paths`));
|
|
251
398
|
} catch (e) {
|
|
252
399
|
results.push({ ...row, status: "error", error: String(e?.message || e) });
|
|
253
400
|
console.log(fail(`${t.name}: ${e?.message || e}`));
|
|
@@ -255,32 +402,17 @@ export async function installDme(opts = {}) {
|
|
|
255
402
|
}
|
|
256
403
|
|
|
257
404
|
console.log(hr());
|
|
258
|
-
console.log(step(3, total, "Project-local
|
|
405
|
+
console.log(step(3, total, "Project-local (cwd)…"));
|
|
259
406
|
if (opts.cwdProject !== false) {
|
|
260
407
|
try {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (opts.dryRun) continue;
|
|
270
|
-
ensureDir(lr);
|
|
271
|
-
for (const skill of SKILL_NAMES) copySkill(skill, lr);
|
|
272
|
-
}
|
|
273
|
-
if (!opts.dryRun) {
|
|
274
|
-
writeFile(path.join(cwd, "AGENTS.dme.md"), compact);
|
|
275
|
-
writeFile(path.join(cwd, ".dme", "DME-Agent-v7.4.md"), full);
|
|
276
|
-
writeFile(path.join(cwd, ".dme", "SYSTEM.md"), sys);
|
|
277
|
-
// merge into AGENTS.md if present or create pointer
|
|
278
|
-
const agentsMd = path.join(cwd, "AGENTS.md");
|
|
279
|
-
writeFile(agentsMd, compact, {
|
|
280
|
-
appendMarker: "<!-- DME-AGENT-7.4 BEGIN -->",
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
console.log(ok(`Project skills + AGENTS.md in ${cwd}`));
|
|
408
|
+
await installProjectLocal({
|
|
409
|
+
opts,
|
|
410
|
+
doctrine,
|
|
411
|
+
sys,
|
|
412
|
+
full,
|
|
413
|
+
compact,
|
|
414
|
+
targets,
|
|
415
|
+
});
|
|
284
416
|
} catch (e) {
|
|
285
417
|
console.log(warn(`Project install: ${e?.message || e}`));
|
|
286
418
|
}
|
|
@@ -289,78 +421,128 @@ export async function installDme(opts = {}) {
|
|
|
289
421
|
}
|
|
290
422
|
|
|
291
423
|
console.log(hr());
|
|
292
|
-
console.log(step(4, total, "
|
|
293
|
-
if (!opts.dryRun) {
|
|
294
|
-
const obs = path.join(process.cwd(), "obsidian");
|
|
295
|
-
ensureDir(obs);
|
|
296
|
-
writeFile(path.join(obs, "DME-Agent-v7.4.md"), full);
|
|
297
|
-
console.log(ok(path.join(obs, "DME-Agent-v7.4.md")));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
console.log(step(5, total, "Summary"));
|
|
424
|
+
console.log(step(4, total, "Summary"));
|
|
301
425
|
const okN = results.filter((r) => r.status === "ok").length;
|
|
302
426
|
const errN = results.filter((r) => r.status === "error").length;
|
|
303
427
|
console.log(
|
|
304
428
|
table([
|
|
305
|
-
["version",
|
|
429
|
+
["version", VERSION],
|
|
306
430
|
["targets ok", String(okN)],
|
|
307
431
|
["errors", String(errN)],
|
|
308
|
-
["
|
|
432
|
+
["mode", targetIds.includes("all") ? "all" : "detected"],
|
|
433
|
+
["skills", String(SKILL_NAMES.length)],
|
|
309
434
|
])
|
|
310
435
|
);
|
|
311
436
|
|
|
312
437
|
console.log(hr());
|
|
313
|
-
console.log(step(
|
|
438
|
+
console.log(step(5, total, "Next"));
|
|
314
439
|
console.log(
|
|
315
|
-
box(
|
|
316
|
-
"
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
440
|
+
box(
|
|
441
|
+
"DME is live",
|
|
442
|
+
[
|
|
443
|
+
"Restart agent sessions (Claude / Cursor / Grok…)",
|
|
444
|
+
"AGENTS.md is in each CLI’s correct path",
|
|
445
|
+
"Skills: /dme-design /dme-dashboard /dme-landing /dme-ux",
|
|
446
|
+
"dme doctor · dme scan · dme uninstall",
|
|
447
|
+
],
|
|
448
|
+
"green"
|
|
449
|
+
)
|
|
321
450
|
);
|
|
322
451
|
console.log("");
|
|
323
|
-
console.log(info("dme doctor — verify install"));
|
|
324
|
-
console.log(info("dme update — re-sync latest assets"));
|
|
325
|
-
console.log(info("dme uninstall — remove DME blocks (safe merge)"));
|
|
326
|
-
console.log("");
|
|
327
452
|
|
|
328
453
|
return { ok: errN === 0, results };
|
|
329
454
|
}
|
|
330
455
|
|
|
456
|
+
async function installProjectLocal({ opts, doctrine, sys, full, compact, targets }) {
|
|
457
|
+
const cwd = process.cwd();
|
|
458
|
+
// Only drop project skill folders for CLIs that were actually installed
|
|
459
|
+
const skillRoots = new Set();
|
|
460
|
+
for (const t of targets) {
|
|
461
|
+
if (t.id === "claude") skillRoots.add(path.join(cwd, ".claude", "skills"));
|
|
462
|
+
if (t.id === "cursor") skillRoots.add(path.join(cwd, ".cursor", "skills"));
|
|
463
|
+
if (t.id === "grok") skillRoots.add(path.join(cwd, ".grok", "skills"));
|
|
464
|
+
if (t.id === "codex") skillRoots.add(path.join(cwd, ".codex", "skills"));
|
|
465
|
+
if (t.id === "zed") skillRoots.add(path.join(cwd, ".agents", "skills"));
|
|
466
|
+
}
|
|
467
|
+
// always allow generic project skills
|
|
468
|
+
skillRoots.add(path.join(cwd, ".agents", "skills"));
|
|
469
|
+
|
|
470
|
+
if (!opts.dryRun) {
|
|
471
|
+
for (const lr of skillRoots) {
|
|
472
|
+
ensureDir(lr);
|
|
473
|
+
for (const skill of SKILL_NAMES) copySkill(skill, lr);
|
|
474
|
+
}
|
|
475
|
+
writeRaw(path.join(cwd, ".dme", "DME-Agent-v7.4.md"), full);
|
|
476
|
+
writeRaw(path.join(cwd, ".dme", "SYSTEM.md"), sys);
|
|
477
|
+
writeRaw(path.join(cwd, ".dme", "AGENTS.md"), doctrine);
|
|
478
|
+
writeRaw(path.join(cwd, "AGENTS.dme.md"), doctrine);
|
|
479
|
+
|
|
480
|
+
const agentsMd = path.join(cwd, "AGENTS.md");
|
|
481
|
+
const row = { files: [] };
|
|
482
|
+
await writeAgentsFile(agentsMd, doctrine, opts, row);
|
|
483
|
+
console.log(ok(`project → ${cwd}`));
|
|
484
|
+
} else {
|
|
485
|
+
console.log(skip(`[dry] project → ${cwd}`));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
331
489
|
export function doctor() {
|
|
332
|
-
|
|
333
|
-
console.log(step(1, 1, "Scanning DME install…"));
|
|
490
|
+
console.log(asciiLogo(VERSION));
|
|
491
|
+
console.log(step(1, 1, "Scanning DME install health…"));
|
|
334
492
|
console.log(hr());
|
|
335
|
-
|
|
493
|
+
const report = formatScanReport();
|
|
494
|
+
for (const row of report) {
|
|
495
|
+
const t = getTargets().find((x) => x.id === row.id);
|
|
496
|
+
if (!t) continue;
|
|
336
497
|
const hits = [];
|
|
498
|
+
if (row.detected) hits.push("cli-present");
|
|
337
499
|
for (const skillDir of t.skillDirs) {
|
|
338
500
|
for (const skill of SKILL_NAMES) {
|
|
339
|
-
|
|
340
|
-
if (fs.existsSync(p)) hits.push(skill);
|
|
501
|
+
if (fs.existsSync(path.join(skillDir, skill, "SKILL.md"))) hits.push(skill);
|
|
341
502
|
}
|
|
342
503
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (fs.existsSync(rf)) {
|
|
348
|
-
const txt = fs.readFileSync(rf, "utf8");
|
|
349
|
-
if (txt.includes("DME") || txt.includes("7.4")) hits.push(path.basename(rf));
|
|
350
|
-
}
|
|
504
|
+
if (t.agentsMd && fs.existsSync(t.agentsMd)) {
|
|
505
|
+
const txt = fs.readFileSync(t.agentsMd, "utf8");
|
|
506
|
+
if (txt.includes("DME") || txt.includes("7.4")) hits.push("agents.md");
|
|
507
|
+
else hits.push("agents.md(no-dme)");
|
|
351
508
|
}
|
|
352
509
|
const man = path.join(t.root, "DME-Agent-v7.4.md");
|
|
353
510
|
if (fs.existsSync(man)) hits.push("manifesto");
|
|
354
511
|
|
|
355
|
-
if (hits.length
|
|
356
|
-
|
|
512
|
+
if (hits.length && row.detected) {
|
|
513
|
+
console.log(ok(`${t.name.padEnd(24)} ${hits.length} hits ${c.dim}${hits.slice(0, 5).join(", ")}${c.reset}`));
|
|
514
|
+
} else if (row.detected) {
|
|
515
|
+
console.log(warn(`${t.name.padEnd(24)} detected but DME not installed`));
|
|
516
|
+
} else {
|
|
517
|
+
console.log(skip(`${t.name.padEnd(24)} CLI not on this PC`));
|
|
518
|
+
}
|
|
357
519
|
}
|
|
358
520
|
console.log("");
|
|
359
521
|
}
|
|
360
522
|
|
|
361
|
-
export function
|
|
362
|
-
|
|
363
|
-
const
|
|
523
|
+
export function scan() {
|
|
524
|
+
printScanTable(formatScanReport());
|
|
525
|
+
const n = getDetectedTargets().length;
|
|
526
|
+
console.log(
|
|
527
|
+
info(
|
|
528
|
+
`${c.brightGreen}${n}${c.reset} CLI(s) detected — install will target only these by default`
|
|
529
|
+
)
|
|
530
|
+
);
|
|
531
|
+
console.log("");
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function uninstall(opts = {}) {
|
|
535
|
+
console.log(asciiLogo(VERSION));
|
|
536
|
+
console.log(warn("Removing DME files & <!-- DME-AGENT-7.4 --> blocks…"));
|
|
537
|
+
console.log(hr());
|
|
538
|
+
|
|
539
|
+
// Only uninstall from detected unless --all
|
|
540
|
+
const targets = opts.all
|
|
541
|
+
? getTargets()
|
|
542
|
+
: getDetectedTargets().length
|
|
543
|
+
? getDetectedTargets()
|
|
544
|
+
: getTargets();
|
|
545
|
+
|
|
364
546
|
for (const t of targets) {
|
|
365
547
|
for (const skillDir of t.skillDirs) {
|
|
366
548
|
for (const skill of SKILL_NAMES) {
|
|
@@ -372,12 +554,12 @@ export function uninstall() {
|
|
|
372
554
|
}
|
|
373
555
|
}
|
|
374
556
|
for (const af of t.agentFiles) {
|
|
375
|
-
if (fs.existsSync(af) && path.basename(af).includes("dme")) {
|
|
557
|
+
if (fs.existsSync(af) && path.basename(af).toLowerCase().includes("dme")) {
|
|
376
558
|
fs.unlinkSync(af);
|
|
377
559
|
console.log(ok(`removed ${af}`));
|
|
378
560
|
}
|
|
379
561
|
}
|
|
380
|
-
for (const rf of t.rulesFiles) {
|
|
562
|
+
for (const rf of [...t.rulesFiles, t.agentsMd].filter(Boolean)) {
|
|
381
563
|
if (!fs.existsSync(rf)) continue;
|
|
382
564
|
const base = path.basename(rf).toLowerCase();
|
|
383
565
|
if (base.includes("dme") || base.endsWith(".mdc")) {
|
|
@@ -386,17 +568,20 @@ export function uninstall() {
|
|
|
386
568
|
continue;
|
|
387
569
|
}
|
|
388
570
|
let txt = fs.readFileSync(rf, "utf8");
|
|
389
|
-
const re =
|
|
390
|
-
|
|
571
|
+
const re = new RegExp(
|
|
572
|
+
`${escapeRe(MARKER_BEGIN)}[\\s\\S]*?${escapeRe(MARKER_END)}\\n?`,
|
|
573
|
+
"g"
|
|
574
|
+
);
|
|
391
575
|
if (re.test(txt)) {
|
|
392
576
|
txt = txt.replace(re, "");
|
|
393
577
|
fs.writeFileSync(rf, txt, "utf8");
|
|
394
578
|
console.log(ok(`stripped DME block from ${rf}`));
|
|
395
579
|
}
|
|
396
580
|
}
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
581
|
+
for (const p of [
|
|
582
|
+
path.join(t.root, "DME-Agent-v7.4.md"),
|
|
583
|
+
path.join(t.root, "DME-SYSTEM.md"),
|
|
584
|
+
]) {
|
|
400
585
|
if (fs.existsSync(p)) {
|
|
401
586
|
fs.unlinkSync(p);
|
|
402
587
|
console.log(ok(`removed ${p}`));
|