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/src/install.mjs CHANGED
@@ -1,17 +1,41 @@
1
1
  /**
2
- * DME bootstrap installer — copies manifesto, system prompt, skills, rules
3
- * into every major agent CLI surface.
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 { filterTargets } from "./targets.mjs";
10
- import { ok, fail, skip, info, warn, step, hr, box, table } from "./ui.mjs";
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
- 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";
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
- fs.writeFileSync(filePath, content, "utf8");
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
- readAsset("agent", "DME-Agent-v7.4-SYSTEM.md") ||
90
- readAsset("agent", "SYSTEM.md") ||
91
- "# DME 7.4\n"
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") || systemPrompt();
141
+ return readAsset("agent", "PROMPT-COMPACT.md") || agentsDoctrine();
97
142
  }
98
143
 
99
144
  function manifesto() {
100
145
  return (
101
- readAsset("manifesto", "DME-Agent-v7.4.md") ||
102
- readAsset("manifesto", "MANIFESTO.md") ||
103
- systemPrompt()
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 readAsset("agent", "dme-v7.4.md") || systemPrompt();
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 7.4 — Neominimal Identity Engine. Apply on any UI/UX/frontend work.
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
- ${compactPrompt()}
173
+ ${agentsDoctrine()}
123
174
  `;
124
175
  }
125
176
 
126
- function rulesSnippet() {
127
- return compactPrompt();
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 {{ targets?: string[], force?: boolean, dryRun?: boolean, cwdProject?: boolean }} opts
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 targets = filterTargets(opts.targets);
135
- const results = [];
136
- const total = 6;
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
- console.log(step(1, total, "Reading DME 7.4 assets…"));
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
- if (!sys || sys.length < 100) {
145
- console.log(fail("System prompt asset missing or empty."));
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(ok(`Manifesto ${full.length} chars · system ${sys.length} chars`));
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 into ${targets.length} surfaces…`));
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
- writeFile(af, sys);
185
- } else if (af.endsWith("dme-v7.4.md")) {
186
- writeFile(af, profile);
368
+ writeRaw(af, sys);
369
+ } else {
370
+ writeRaw(af, profile);
187
371
  }
188
372
  row.files.push(af);
189
373
  }
190
374
 
191
- // rules — append or write dedicated
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
- writeFile(rf, cursorRule());
392
+ writeRaw(rf, cursorRule());
202
393
  row.files.push(rf);
203
394
  continue;
204
395
  }
205
396
 
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)");
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
- writeFile(rf, sys);
403
+ writeRaw(rf, sys);
226
404
  row.files.push(rf);
227
405
  continue;
228
406
  }
229
407
 
230
- writeFile(rf, compact);
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 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);
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 optional root */
422
+ /* ignore */
246
423
  }
247
424
  }
248
425
 
249
426
  results.push({ ...row, status: "ok" });
250
- console.log(ok(`${t.name.padEnd(28)} ${row.files.length} paths`));
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 install (cwd)…"));
435
+ console.log(step(3, total, "Project-local (cwd)…"));
259
436
  if (opts.cwdProject !== false) {
260
437
  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}`));
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, "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"));
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", "7.4.1"],
459
+ ["version", VERSION],
306
460
  ["targets ok", String(okN)],
307
461
  ["errors", String(errN)],
308
- ["skills", SKILL_NAMES.join(", ")],
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(6, total, "Next"));
468
+ console.log(step(5, total, "Next"));
314
469
  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
- ])
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
- const targets = filterTargets(["all"]);
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
- for (const t of targets) {
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
- const p = path.join(skillDir, skill, "SKILL.md");
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
- for (const af of t.agentFiles) {
344
- if (fs.existsSync(af)) hits.push(path.basename(af));
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
- 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
- }
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 uninstall() {
362
- console.log(warn("Uninstall removes DME-named files and <!-- DME-AGENT-7.4 --> blocks."));
363
- const targets = filterTargets(["all"]);
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
- 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, "");
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 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]) {
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 };