dme-agent 7.4.1
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/LICENSE +21 -0
- package/README.md +63 -0
- package/assets/agent/DME-Agent-v7.4-SYSTEM.md +112 -0
- package/assets/agent/PROMPT-COMPACT.md +20 -0
- package/assets/agent/dme-v7.4.md +27 -0
- package/assets/manifesto/DME-Agent-v7.4.md +588 -0
- package/assets/references/books.md +3 -0
- package/assets/references/visual-anchors.md +7 -0
- package/assets/skills/dme-critique/SKILL.md +14 -0
- package/assets/skills/dme-dashboard/SKILL.md +56 -0
- package/assets/skills/dme-design/SKILL.md +18 -0
- package/assets/skills/dme-landing/SKILL.md +48 -0
- package/assets/skills/dme-research/SKILL.md +14 -0
- package/assets/skills/dme-shadcn/SKILL.md +14 -0
- package/assets/skills/dme-ux/SKILL.md +53 -0
- package/bin/dme.mjs +144 -0
- package/package.json +50 -0
- package/src/install.mjs +407 -0
- package/src/postinstall-hint.mjs +16 -0
- package/src/targets.mjs +179 -0
- package/src/ui.mjs +93 -0
package/src/install.mjs
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DME bootstrap installer — copies manifesto, system prompt, skills, rules
|
|
3
|
+
* into every major agent CLI surface.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { filterTargets } from "./targets.mjs";
|
|
10
|
+
import { ok, fail, skip, info, warn, step, hr, box, table } from "./ui.mjs";
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
14
|
+
const ASSETS = path.join(PKG_ROOT, "assets");
|
|
15
|
+
|
|
16
|
+
const SKILL_NAMES = [
|
|
17
|
+
"dme-design",
|
|
18
|
+
"dme-shadcn",
|
|
19
|
+
"dme-research",
|
|
20
|
+
"dme-critique",
|
|
21
|
+
"dme-dashboard",
|
|
22
|
+
"dme-landing",
|
|
23
|
+
"dme-ux",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function ensureDir(dir) {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readAsset(...parts) {
|
|
31
|
+
const p = path.join(ASSETS, ...parts);
|
|
32
|
+
if (!fs.existsSync(p)) return null;
|
|
33
|
+
return fs.readFileSync(p, "utf8");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function writeFile(filePath, content, { appendMarker } = {}) {
|
|
37
|
+
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
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
70
|
+
return fs.existsSync(filePath) ? "written" : "written";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function escapeRe(s) {
|
|
74
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function copySkill(skillName, destRoot) {
|
|
78
|
+
const src = path.join(ASSETS, "skills", skillName, "SKILL.md");
|
|
79
|
+
if (!fs.existsSync(src)) return null;
|
|
80
|
+
const destDir = path.join(destRoot, skillName);
|
|
81
|
+
ensureDir(destDir);
|
|
82
|
+
const dest = path.join(destDir, "SKILL.md");
|
|
83
|
+
fs.copyFileSync(src, dest);
|
|
84
|
+
return dest;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function systemPrompt() {
|
|
88
|
+
return (
|
|
89
|
+
readAsset("agent", "DME-Agent-v7.4-SYSTEM.md") ||
|
|
90
|
+
readAsset("agent", "SYSTEM.md") ||
|
|
91
|
+
"# DME 7.4\n"
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function compactPrompt() {
|
|
96
|
+
return readAsset("agent", "PROMPT-COMPACT.md") || systemPrompt();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function manifesto() {
|
|
100
|
+
return (
|
|
101
|
+
readAsset("manifesto", "DME-Agent-v7.4.md") ||
|
|
102
|
+
readAsset("manifesto", "MANIFESTO.md") ||
|
|
103
|
+
systemPrompt()
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function agentProfile() {
|
|
108
|
+
return readAsset("agent", "dme-v7.4.md") || systemPrompt();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function cursorRule() {
|
|
112
|
+
return `---
|
|
113
|
+
description: DME Agent 7.4 — Neominimal Identity Engine. Apply on any UI/UX/frontend work.
|
|
114
|
+
globs:
|
|
115
|
+
- "**/*.{tsx,jsx,vue,svelte,css,html}"
|
|
116
|
+
- "**/components/**"
|
|
117
|
+
- "**/app/**"
|
|
118
|
+
- "**/pages/**"
|
|
119
|
+
alwaysApply: false
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
${compactPrompt()}
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function rulesSnippet() {
|
|
127
|
+
return compactPrompt();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {{ targets?: string[], force?: boolean, dryRun?: boolean, cwdProject?: boolean }} opts
|
|
132
|
+
*/
|
|
133
|
+
export async function installDme(opts = {}) {
|
|
134
|
+
const targets = filterTargets(opts.targets);
|
|
135
|
+
const results = [];
|
|
136
|
+
const total = 6;
|
|
137
|
+
|
|
138
|
+
console.log(step(1, total, "Reading DME 7.4 assets…"));
|
|
139
|
+
const sys = systemPrompt();
|
|
140
|
+
const compact = compactPrompt();
|
|
141
|
+
const full = manifesto();
|
|
142
|
+
const profile = agentProfile();
|
|
143
|
+
|
|
144
|
+
if (!sys || sys.length < 100) {
|
|
145
|
+
console.log(fail("System prompt asset missing or empty."));
|
|
146
|
+
return { ok: false, results };
|
|
147
|
+
}
|
|
148
|
+
console.log(ok(`Manifesto ${full.length} chars · system ${sys.length} chars`));
|
|
149
|
+
console.log(hr());
|
|
150
|
+
|
|
151
|
+
console.log(step(2, total, `Installing into ${targets.length} surfaces…`));
|
|
152
|
+
|
|
153
|
+
for (const t of targets) {
|
|
154
|
+
const row = { id: t.id, name: t.name, files: [], errors: [] };
|
|
155
|
+
try {
|
|
156
|
+
// skills
|
|
157
|
+
for (const skillDir of t.skillDirs) {
|
|
158
|
+
if (opts.dryRun) {
|
|
159
|
+
row.files.push(`[dry] skills → ${skillDir}`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
ensureDir(skillDir);
|
|
163
|
+
for (const skill of SKILL_NAMES) {
|
|
164
|
+
const dest = copySkill(skill, skillDir);
|
|
165
|
+
if (dest) row.files.push(dest);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// agent profiles
|
|
170
|
+
for (const af of t.agentFiles) {
|
|
171
|
+
if (opts.dryRun) {
|
|
172
|
+
row.files.push(`[dry] agent → ${af}`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
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
|
+
if (af.includes("CLAUDE_DME") || af.includes("instructions")) {
|
|
184
|
+
writeFile(af, sys);
|
|
185
|
+
} else if (af.endsWith("dme-v7.4.md")) {
|
|
186
|
+
writeFile(af, profile);
|
|
187
|
+
}
|
|
188
|
+
row.files.push(af);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// rules — append or write dedicated
|
|
192
|
+
for (const rf of t.rulesFiles) {
|
|
193
|
+
if (opts.dryRun) {
|
|
194
|
+
row.files.push(`[dry] rules → ${rf}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
ensureDir(path.dirname(rf));
|
|
198
|
+
const base = path.basename(rf).toLowerCase();
|
|
199
|
+
|
|
200
|
+
if (base === "dme-agent.mdc" || rf.endsWith(".mdc")) {
|
|
201
|
+
writeFile(rf, cursorRule());
|
|
202
|
+
row.files.push(rf);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
base === "claude.md" ||
|
|
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)");
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (base.includes("dme") || base.includes("conventions")) {
|
|
225
|
+
writeFile(rf, sys);
|
|
226
|
+
row.files.push(rf);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
writeFile(rf, compact);
|
|
231
|
+
row.files.push(rf);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// always drop full manifesto into target root if root exists or is under home config
|
|
235
|
+
if (!opts.dryRun && t.root) {
|
|
236
|
+
try {
|
|
237
|
+
ensureDir(t.root);
|
|
238
|
+
const manPath = path.join(t.root, "DME-Agent-v7.4.md");
|
|
239
|
+
writeFile(manPath, full);
|
|
240
|
+
row.files.push(manPath);
|
|
241
|
+
const sysPath = path.join(t.root, "DME-SYSTEM.md");
|
|
242
|
+
writeFile(sysPath, sys);
|
|
243
|
+
row.files.push(sysPath);
|
|
244
|
+
} catch {
|
|
245
|
+
/* ignore optional root */
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
results.push({ ...row, status: "ok" });
|
|
250
|
+
console.log(ok(`${t.name.padEnd(28)} ${row.files.length} paths`));
|
|
251
|
+
} catch (e) {
|
|
252
|
+
results.push({ ...row, status: "error", error: String(e?.message || e) });
|
|
253
|
+
console.log(fail(`${t.name}: ${e?.message || e}`));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log(hr());
|
|
258
|
+
console.log(step(3, total, "Project-local install (cwd)…"));
|
|
259
|
+
if (opts.cwdProject !== false) {
|
|
260
|
+
try {
|
|
261
|
+
const cwd = process.cwd();
|
|
262
|
+
const localRoots = [
|
|
263
|
+
path.join(cwd, ".grok", "skills"),
|
|
264
|
+
path.join(cwd, ".claude", "skills"),
|
|
265
|
+
path.join(cwd, ".cursor", "skills"),
|
|
266
|
+
path.join(cwd, ".agents", "skills"),
|
|
267
|
+
];
|
|
268
|
+
for (const lr of localRoots) {
|
|
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}`));
|
|
284
|
+
} catch (e) {
|
|
285
|
+
console.log(warn(`Project install: ${e?.message || e}`));
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
console.log(skip("skipped (--no-project)"));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
console.log(hr());
|
|
292
|
+
console.log(step(4, total, "Obsidian-ready manifesto…"));
|
|
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"));
|
|
301
|
+
const okN = results.filter((r) => r.status === "ok").length;
|
|
302
|
+
const errN = results.filter((r) => r.status === "error").length;
|
|
303
|
+
console.log(
|
|
304
|
+
table([
|
|
305
|
+
["version", "7.4.1"],
|
|
306
|
+
["targets ok", String(okN)],
|
|
307
|
+
["errors", String(errN)],
|
|
308
|
+
["skills", SKILL_NAMES.join(", ")],
|
|
309
|
+
])
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
console.log(hr());
|
|
313
|
+
console.log(step(6, total, "Next"));
|
|
314
|
+
console.log(
|
|
315
|
+
box("DME is live", [
|
|
316
|
+
"Restart Claude / Cursor / Zed / Grok sessions",
|
|
317
|
+
"Slash: /dme-design /dme-dashboard /dme-landing",
|
|
318
|
+
"System: ask the agent to follow DME 7.4",
|
|
319
|
+
"Full doctrine: DME-Agent-v7.4.md in each root",
|
|
320
|
+
])
|
|
321
|
+
);
|
|
322
|
+
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
|
+
|
|
328
|
+
return { ok: errN === 0, results };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function doctor() {
|
|
332
|
+
const targets = filterTargets(["all"]);
|
|
333
|
+
console.log(step(1, 1, "Scanning DME install…"));
|
|
334
|
+
console.log(hr());
|
|
335
|
+
for (const t of targets) {
|
|
336
|
+
const hits = [];
|
|
337
|
+
for (const skillDir of t.skillDirs) {
|
|
338
|
+
for (const skill of SKILL_NAMES) {
|
|
339
|
+
const p = path.join(skillDir, skill, "SKILL.md");
|
|
340
|
+
if (fs.existsSync(p)) hits.push(skill);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
for (const af of t.agentFiles) {
|
|
344
|
+
if (fs.existsSync(af)) hits.push(path.basename(af));
|
|
345
|
+
}
|
|
346
|
+
for (const rf of t.rulesFiles) {
|
|
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
|
+
}
|
|
351
|
+
}
|
|
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
|
+
}
|
|
358
|
+
console.log("");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function uninstall() {
|
|
362
|
+
console.log(warn("Uninstall removes DME-named files and <!-- DME-AGENT-7.4 --> blocks."));
|
|
363
|
+
const targets = filterTargets(["all"]);
|
|
364
|
+
for (const t of targets) {
|
|
365
|
+
for (const skillDir of t.skillDirs) {
|
|
366
|
+
for (const skill of SKILL_NAMES) {
|
|
367
|
+
const dir = path.join(skillDir, skill);
|
|
368
|
+
if (fs.existsSync(dir)) {
|
|
369
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
370
|
+
console.log(ok(`removed skill ${dir}`));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (const af of t.agentFiles) {
|
|
375
|
+
if (fs.existsSync(af) && path.basename(af).includes("dme")) {
|
|
376
|
+
fs.unlinkSync(af);
|
|
377
|
+
console.log(ok(`removed ${af}`));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const rf of t.rulesFiles) {
|
|
381
|
+
if (!fs.existsSync(rf)) continue;
|
|
382
|
+
const base = path.basename(rf).toLowerCase();
|
|
383
|
+
if (base.includes("dme") || base.endsWith(".mdc")) {
|
|
384
|
+
fs.unlinkSync(rf);
|
|
385
|
+
console.log(ok(`removed ${rf}`));
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
let txt = fs.readFileSync(rf, "utf8");
|
|
389
|
+
const re =
|
|
390
|
+
/<!-- DME-AGENT-7\.4 BEGIN -->[\s\S]*?<!-- DME-AGENT-7\.4 END -->\n?/g;
|
|
391
|
+
if (re.test(txt)) {
|
|
392
|
+
txt = txt.replace(re, "");
|
|
393
|
+
fs.writeFileSync(rf, txt, "utf8");
|
|
394
|
+
console.log(ok(`stripped DME block from ${rf}`));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const man = path.join(t.root, "DME-Agent-v7.4.md");
|
|
398
|
+
const sys = path.join(t.root, "DME-SYSTEM.md");
|
|
399
|
+
for (const p of [man, sys]) {
|
|
400
|
+
if (fs.existsSync(p)) {
|
|
401
|
+
fs.unlinkSync(p);
|
|
402
|
+
console.log(ok(`removed ${p}`));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
console.log(ok("Uninstall complete."));
|
|
407
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soft hint after npm install — never fails the install.
|
|
3
|
+
*/
|
|
4
|
+
try {
|
|
5
|
+
if (process.env.DME_SILENT === "1") process.exit(0);
|
|
6
|
+
const y = process.stdout.isTTY ? "\x1b[33m" : "";
|
|
7
|
+
const d = process.stdout.isTTY ? "\x1b[2m" : "";
|
|
8
|
+
const r = process.stdout.isTTY ? "\x1b[0m" : "";
|
|
9
|
+
console.log("");
|
|
10
|
+
console.log(`${y} DME Agent installed.${r}`);
|
|
11
|
+
console.log(`${d} Run: npx dme-agent install${r}`);
|
|
12
|
+
console.log(`${d} Or: dme install${r}`);
|
|
13
|
+
console.log("");
|
|
14
|
+
} catch {
|
|
15
|
+
/* ignore */
|
|
16
|
+
}
|
package/src/targets.mjs
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install targets for every major AI coding CLI / editor agent surface.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
const home = os.homedir();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} Target
|
|
12
|
+
* @property {string} id
|
|
13
|
+
* @property {string} name
|
|
14
|
+
* @property {string} root
|
|
15
|
+
* @property {"claude"|"cursor"|"codex"|"zed"|"grok"|"windsurf"|"continue"|"aider"|"copilot"|"antigravity"|"generic"} kind
|
|
16
|
+
* @property {string[]} skillDirs
|
|
17
|
+
* @property {string[]} agentFiles
|
|
18
|
+
* @property {string[]} rulesFiles
|
|
19
|
+
* @property {boolean} [optional]
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** @returns {Target[]} */
|
|
23
|
+
export function getTargets() {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
id: "claude",
|
|
27
|
+
name: "Claude Code",
|
|
28
|
+
root: path.join(home, ".claude"),
|
|
29
|
+
kind: "claude",
|
|
30
|
+
skillDirs: [path.join(home, ".claude", "skills")],
|
|
31
|
+
agentFiles: [
|
|
32
|
+
path.join(home, ".claude", "agents", "dme-v7.4.md"),
|
|
33
|
+
path.join(home, ".claude", "CLAUDE_DME.md"),
|
|
34
|
+
],
|
|
35
|
+
rulesFiles: [
|
|
36
|
+
path.join(home, ".claude", "CLAUDE.md"),
|
|
37
|
+
path.join(home, ".claude", "Agents.md"),
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "cursor",
|
|
42
|
+
name: "Cursor",
|
|
43
|
+
root: path.join(home, ".cursor"),
|
|
44
|
+
kind: "cursor",
|
|
45
|
+
skillDirs: [
|
|
46
|
+
path.join(home, ".cursor", "skills"),
|
|
47
|
+
path.join(home, ".cursor", "skills-cursor"),
|
|
48
|
+
],
|
|
49
|
+
agentFiles: [path.join(home, ".cursor", "agents", "dme-v7.4.md")],
|
|
50
|
+
rulesFiles: [
|
|
51
|
+
path.join(home, ".cursor", "rules", "dme-agent.mdc"),
|
|
52
|
+
path.join(home, ".cursorrules"),
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "codex",
|
|
57
|
+
name: "OpenAI Codex CLI",
|
|
58
|
+
root: path.join(home, ".codex"),
|
|
59
|
+
kind: "codex",
|
|
60
|
+
skillDirs: [path.join(home, ".codex", "skills")],
|
|
61
|
+
agentFiles: [path.join(home, ".codex", "agents", "dme-v7.4.md")],
|
|
62
|
+
rulesFiles: [
|
|
63
|
+
path.join(home, ".codex", "AGENTS.md"),
|
|
64
|
+
path.join(home, ".codex", "instructions", "dme-v7.4.md"),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "zed",
|
|
69
|
+
name: "Zed",
|
|
70
|
+
root: path.join(home, ".config", "zed"),
|
|
71
|
+
kind: "zed",
|
|
72
|
+
skillDirs: [path.join(home, ".config", "zed", "skills")],
|
|
73
|
+
agentFiles: [
|
|
74
|
+
path.join(home, ".config", "zed", "agents", "dme-v7.4.md"),
|
|
75
|
+
],
|
|
76
|
+
rulesFiles: [
|
|
77
|
+
path.join(home, ".config", "zed", "rules", "dme.md"),
|
|
78
|
+
path.join(home, ".config", "zed", "AGENTS.md"),
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "grok",
|
|
83
|
+
name: "Grok / xAI",
|
|
84
|
+
root: path.join(home, ".grok"),
|
|
85
|
+
kind: "grok",
|
|
86
|
+
skillDirs: [path.join(home, ".grok", "skills")],
|
|
87
|
+
agentFiles: [
|
|
88
|
+
path.join(home, ".grok", "bundled", "agents", "dme-v7.4.md"),
|
|
89
|
+
path.join(home, ".grok", "agents", "dme-v7.4.md"),
|
|
90
|
+
],
|
|
91
|
+
rulesFiles: [path.join(home, ".grok", "AGENTS.md")],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "windsurf",
|
|
95
|
+
name: "Windsurf",
|
|
96
|
+
root: path.join(home, ".codeium", "windsurf"),
|
|
97
|
+
kind: "windsurf",
|
|
98
|
+
skillDirs: [path.join(home, ".codeium", "windsurf", "skills")],
|
|
99
|
+
agentFiles: [
|
|
100
|
+
path.join(home, ".codeium", "windsurf", "agents", "dme-v7.4.md"),
|
|
101
|
+
],
|
|
102
|
+
rulesFiles: [
|
|
103
|
+
path.join(home, ".codeium", "windsurf", "memories", "dme-v7.4.md"),
|
|
104
|
+
path.join(home, ".windsurfrules"),
|
|
105
|
+
],
|
|
106
|
+
optional: true,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "continue",
|
|
110
|
+
name: "Continue.dev",
|
|
111
|
+
root: path.join(home, ".continue"),
|
|
112
|
+
kind: "continue",
|
|
113
|
+
skillDirs: [path.join(home, ".continue", "skills")],
|
|
114
|
+
agentFiles: [path.join(home, ".continue", "agents", "dme-v7.4.md")],
|
|
115
|
+
rulesFiles: [path.join(home, ".continue", "rules", "dme.md")],
|
|
116
|
+
optional: true,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: "aider",
|
|
120
|
+
name: "Aider",
|
|
121
|
+
root: path.join(home, ".aider"),
|
|
122
|
+
kind: "aider",
|
|
123
|
+
skillDirs: [],
|
|
124
|
+
agentFiles: [],
|
|
125
|
+
rulesFiles: [
|
|
126
|
+
path.join(home, ".aider", "dme-CONVENTIONS.md"),
|
|
127
|
+
path.join(home, ".aider.conf.dme.yml"),
|
|
128
|
+
],
|
|
129
|
+
optional: true,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: "copilot",
|
|
133
|
+
name: "GitHub Copilot (chat instructions)",
|
|
134
|
+
root: path.join(home, ".copilot"),
|
|
135
|
+
kind: "copilot",
|
|
136
|
+
skillDirs: [],
|
|
137
|
+
agentFiles: [],
|
|
138
|
+
rulesFiles: [
|
|
139
|
+
path.join(home, ".github", "copilot-instructions.md"),
|
|
140
|
+
path.join(home, ".copilot", "dme-instructions.md"),
|
|
141
|
+
],
|
|
142
|
+
optional: true,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "antigravity",
|
|
146
|
+
name: "Google Antigravity / Gemini CLI",
|
|
147
|
+
root: path.join(home, ".gemini"),
|
|
148
|
+
kind: "antigravity",
|
|
149
|
+
skillDirs: [path.join(home, ".gemini", "skills")],
|
|
150
|
+
agentFiles: [path.join(home, ".gemini", "agents", "dme-v7.4.md")],
|
|
151
|
+
rulesFiles: [
|
|
152
|
+
path.join(home, ".gemini", "GEMINI.md"),
|
|
153
|
+
path.join(home, ".gemini", "AGENTS.md"),
|
|
154
|
+
],
|
|
155
|
+
optional: true,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "global-agents",
|
|
159
|
+
name: "Global AGENTS.md (home)",
|
|
160
|
+
root: home,
|
|
161
|
+
kind: "generic",
|
|
162
|
+
skillDirs: [
|
|
163
|
+
path.join(home, ".agents", "skills"),
|
|
164
|
+
path.join(home, ".config", "agents", "skills"),
|
|
165
|
+
],
|
|
166
|
+
agentFiles: [path.join(home, ".agents", "dme-v7.4.md")],
|
|
167
|
+
rulesFiles: [path.join(home, "AGENTS.md")],
|
|
168
|
+
optional: true,
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function filterTargets(ids) {
|
|
174
|
+
const all = getTargets();
|
|
175
|
+
if (!ids || ids.length === 0) return all;
|
|
176
|
+
const set = new Set(ids.map((x) => x.toLowerCase()));
|
|
177
|
+
if (set.has("all")) return all;
|
|
178
|
+
return all.filter((t) => set.has(t.id));
|
|
179
|
+
}
|
package/src/ui.mjs
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal UI — neominimal, readable, no emoji spam.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const isWin = process.platform === "win32";
|
|
6
|
+
const forceColor =
|
|
7
|
+
process.env.FORCE_COLOR === "1" ||
|
|
8
|
+
process.env.DME_COLOR === "1" ||
|
|
9
|
+
(process.stdout.isTTY && process.env.NO_COLOR !== "1");
|
|
10
|
+
|
|
11
|
+
const c = {
|
|
12
|
+
reset: forceColor ? "\x1b[0m" : "",
|
|
13
|
+
bold: forceColor ? "\x1b[1m" : "",
|
|
14
|
+
dim: forceColor ? "\x1b[2m" : "",
|
|
15
|
+
yellow: forceColor ? "\x1b[33m" : "",
|
|
16
|
+
cyan: forceColor ? "\x1b[36m" : "",
|
|
17
|
+
green: forceColor ? "\x1b[32m" : "",
|
|
18
|
+
red: forceColor ? "\x1b[31m" : "",
|
|
19
|
+
white: forceColor ? "\x1b[37m" : "",
|
|
20
|
+
gray: forceColor ? "\x1b[90m" : "",
|
|
21
|
+
bg: forceColor ? "\x1b[40m" : "",
|
|
22
|
+
bgYellow: forceColor ? "\x1b[43m" : "",
|
|
23
|
+
black: forceColor ? "\x1b[30m" : "",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const colors = c;
|
|
27
|
+
|
|
28
|
+
export function banner(version = "7.4.1") {
|
|
29
|
+
const y = c.yellow;
|
|
30
|
+
const d = c.dim;
|
|
31
|
+
const r = c.reset;
|
|
32
|
+
const b = c.bold;
|
|
33
|
+
const lines = [
|
|
34
|
+
"",
|
|
35
|
+
`${y}${b} ╔══════════════════════════════════════════════════╗${r}`,
|
|
36
|
+
`${y}${b} ║${r} ${y}${b}║${r}`,
|
|
37
|
+
`${y}${b} ║${r} ${b}D M E A G E N T${r} ${d}v${version}${r} ${y}${b}║${r}`,
|
|
38
|
+
`${y}${b} ║${r} ${d}Neominimal Identity Engine${r} ${y}${b}║${r}`,
|
|
39
|
+
`${y}${b} ║${r} ${y}${b}║${r}`,
|
|
40
|
+
`${y}${b} ╚══════════════════════════════════════════════════╝${r}`,
|
|
41
|
+
`${d} less, but better — with a face · shadcn first · UX for humans${r}`,
|
|
42
|
+
"",
|
|
43
|
+
];
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function step(n, total, msg) {
|
|
48
|
+
return `${c.yellow}${c.bold}[${n}/${total}]${c.reset} ${msg}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function ok(msg) {
|
|
52
|
+
return `${c.green} ✓${c.reset} ${msg}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function fail(msg) {
|
|
56
|
+
return `${c.red} ✗${c.reset} ${msg}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function skip(msg) {
|
|
60
|
+
return `${c.gray} –${c.reset} ${c.dim}${msg}${c.reset}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function info(msg) {
|
|
64
|
+
return `${c.cyan} →${c.reset} ${msg}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function warn(msg) {
|
|
68
|
+
return `${c.yellow} !${c.reset} ${msg}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function hr() {
|
|
72
|
+
return `${c.dim} ────────────────────────────────────────────────${c.reset}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function box(title, lines = []) {
|
|
76
|
+
const width = 52;
|
|
77
|
+
const top = `${c.yellow} ┌${"─".repeat(width - 2)}┐${c.reset}`;
|
|
78
|
+
const mid = `${c.yellow} │${c.reset} ${c.bold}${title.padEnd(width - 4)}${c.reset}${c.yellow}│${c.reset}`;
|
|
79
|
+
const body = lines.map(
|
|
80
|
+
(l) =>
|
|
81
|
+
`${c.yellow} │${c.reset} ${String(l).slice(0, width - 4).padEnd(width - 4)}${c.yellow}│${c.reset}`
|
|
82
|
+
);
|
|
83
|
+
const bot = `${c.yellow} └${"─".repeat(width - 2)}┘${c.reset}`;
|
|
84
|
+
return [top, mid, ...body, bot].join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function table(rows) {
|
|
88
|
+
return rows
|
|
89
|
+
.map(([k, v]) => ` ${c.dim}${String(k).padEnd(16)}${c.reset}${v}`)
|
|
90
|
+
.join("\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { isWin };
|