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