devsmind-mcp 2.1.1 → 2.2.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.
@@ -0,0 +1,330 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TARGETS = void 0;
37
+ exports.stdioEntry = stdioEntry;
38
+ exports.httpEntry = httpEntry;
39
+ exports.resolveOsPath = resolveOsPath;
40
+ exports.resolveScopeFile = resolveScopeFile;
41
+ exports.getTarget = getTarget;
42
+ const os = __importStar(require("os"));
43
+ const path = __importStar(require("path"));
44
+ // ─── Shared entry payloads ───────────────────────────────────────────────────
45
+ /** Standard stdio entry: the IDE spawns `devsmind start --stdio`. */
46
+ function stdioEntry() {
47
+ return { command: 'devsmind', args: ['start', '--stdio'] };
48
+ }
49
+ /** Standard HTTP entry: connect to the already-running server. */
50
+ function httpEntry(ctx) {
51
+ return { url: `http://localhost:${ctx.port}/mcp` };
52
+ }
53
+ // ─── OS-aware path resolution ────────────────────────────────────────────────
54
+ /** Resolve an {@link OsPath} to a concrete string for the current platform, expanding a leading `~`. */
55
+ function resolveOsPath(p) {
56
+ const raw = typeof p === 'string' ? p : p[process.platform] ?? p.linux;
57
+ if (raw.startsWith('~')) {
58
+ return path.join(os.homedir(), raw.slice(1));
59
+ }
60
+ return raw;
61
+ }
62
+ /**
63
+ * Resolve the absolute file path for a config/rule scope.
64
+ * Project scopes are joined onto `workspaceRoot`; global scopes are absolute.
65
+ */
66
+ function resolveScopeFile(file, scope, workspaceRoot) {
67
+ const resolved = resolveOsPath(file);
68
+ if (scope === 'project') {
69
+ return path.isAbsolute(resolved) ? resolved : path.join(workspaceRoot, resolved);
70
+ }
71
+ return path.resolve(resolved);
72
+ }
73
+ // ─── Transport-specific entry helpers ────────────────────────────────────────
74
+ // Different tools key the HTTP endpoint differently (url / serverUrl / httpUrl)
75
+ // and some require an explicit `type`. These builders capture each tool's shape.
76
+ const httpUrl = (ctx) => `http://localhost:${ctx.port}/mcp`;
77
+ /** Cursor / Kiro: bare `url`, no type. */
78
+ const entryUrl = (t, ctx) => t === 'stdio' ? stdioEntry() : { url: httpUrl(ctx) };
79
+ /** VS Code / Claude Code: explicit `type` + url. */
80
+ const entryTyped = (t, ctx) => t === 'stdio'
81
+ ? { type: 'stdio', ...stdioEntry() }
82
+ : { type: 'http', url: httpUrl(ctx) };
83
+ /** Windsurf / Antigravity: HTTP endpoint keyed as `serverUrl`. */
84
+ const entryServerUrl = (t, ctx) => t === 'stdio' ? stdioEntry() : { serverUrl: httpUrl(ctx) };
85
+ /** Qwen Code: Streamable-HTTP endpoint keyed as `httpUrl`. */
86
+ const entryHttpUrl = (t, ctx) => t === 'stdio' ? stdioEntry() : { httpUrl: httpUrl(ctx) };
87
+ const cursorMdcWrap = (body) => `---\ndescription: DevsMind — Team AI Brain workspace rule\nalwaysApply: true\n---\n\n${body}\n`;
88
+ /** Antigravity Skills format: YAML frontmatter (name + description) + markdown body. */
89
+ const antigravitySkillWrap = (body) => `---\nname: devsmind\ndescription: DevsMind team code-graph MCP server — when and how to use it\n---\n\n${body}\n`;
90
+ // VS Code user-profile mcp.json lives in the platform user-data dir.
91
+ const VSCODE_GLOBAL = {
92
+ win32: '~/AppData/Roaming/Code/User/mcp.json',
93
+ darwin: '~/Library/Application Support/Code/User/mcp.json',
94
+ linux: '~/.config/Code/User/mcp.json',
95
+ };
96
+ // ─── The registry ────────────────────────────────────────────────────────────
97
+ // IDEs first, then CLIs, for the picker menu. Each tool's HTTP-key quirk and
98
+ // rules location are encoded here — the rest of the code is tool-agnostic.
99
+ exports.TARGETS = [
100
+ // ── IDEs ──────────────────────────────────────────────────────────────────
101
+ {
102
+ id: 'cursor',
103
+ label: 'Cursor',
104
+ kind: 'ide',
105
+ mcp: {
106
+ scopes: [
107
+ { scope: 'project', file: '.cursor/mcp.json', format: 'json', serverMapPath: ['mcpServers'] },
108
+ { scope: 'global', file: '~/.cursor/mcp.json', format: 'json', serverMapPath: ['mcpServers'] },
109
+ ],
110
+ transports: ['stdio', 'http'],
111
+ entry: entryUrl,
112
+ },
113
+ rules: {
114
+ scopes: [{ scope: 'project', file: '.cursor/rules/devsmind.mdc' }],
115
+ style: 'standalone',
116
+ wrap: cursorMdcWrap,
117
+ },
118
+ memory: {
119
+ supported: false,
120
+ featureName: 'Memories',
121
+ note: 'Cursor\'s Memories are stored in an internal, undocumented database and only save after the agent proposes one and you approve it — there is no file DevsMind can safely write to. Ask the agent to remember the DevsMind workflow in conversation (e.g. "remember to always search_nodes before grep, and stage_change + commit_changes after every change") and approve the memory Cursor proposes.',
122
+ },
123
+ },
124
+ {
125
+ id: 'vscode',
126
+ label: 'VS Code (GitHub Copilot)',
127
+ kind: 'ide',
128
+ mcp: {
129
+ scopes: [
130
+ { scope: 'project', file: '.vscode/mcp.json', format: 'json', serverMapPath: ['servers'] },
131
+ { scope: 'global', file: VSCODE_GLOBAL, format: 'json', serverMapPath: ['servers'] },
132
+ ],
133
+ transports: ['stdio', 'http'],
134
+ entry: entryTyped,
135
+ note: 'VS Code uses the "servers" key (not "mcpServers").',
136
+ },
137
+ rules: {
138
+ scopes: [{ scope: 'project', file: '.github/copilot-instructions.md' }],
139
+ style: 'append-section',
140
+ },
141
+ memory: {
142
+ supported: false,
143
+ featureName: 'Copilot Memory',
144
+ note: 'Copilot Memory has no documented write API and its file format has changed shape multiple times through 2026 — writing into it directly risks corrupting it. It should pick up the DevsMind workflow itself after a few real sessions using the tools; check what it has learned with the "Chat: Show Memory Files" command.',
145
+ },
146
+ },
147
+ {
148
+ id: 'windsurf',
149
+ label: 'Windsurf (Cascade)',
150
+ kind: 'ide',
151
+ mcp: {
152
+ scopes: [
153
+ { scope: 'global', file: '~/.codeium/windsurf/mcp_config.json', format: 'json', serverMapPath: ['mcpServers'] },
154
+ ],
155
+ transports: ['stdio', 'http'],
156
+ entry: entryServerUrl,
157
+ note: 'Windsurf keys the remote endpoint as "serverUrl". Config is global-only.',
158
+ },
159
+ rules: {
160
+ scopes: [{ scope: 'project', file: '.windsurf/rules/devsmind.md' }],
161
+ style: 'standalone',
162
+ },
163
+ memory: {
164
+ supported: false,
165
+ featureName: 'Cascade Memories',
166
+ note: 'Cascade Memories are stored at ~/.codeium/windsurf/memories/, keyed per-workspace by a mechanism Windsurf doesn\'t document — there\'s no confirmed evidence a manually-placed file there is ever discovered. Ask Cascade directly to "create a memory of the DevsMind workflow" and it will generate one through its own supported path.',
167
+ },
168
+ },
169
+ {
170
+ id: 'kiro',
171
+ label: 'Kiro',
172
+ kind: 'ide',
173
+ mcp: {
174
+ scopes: [
175
+ { scope: 'project', file: '.kiro/settings/mcp.json', format: 'json', serverMapPath: ['mcpServers'] },
176
+ { scope: 'global', file: '~/.kiro/settings/mcp.json', format: 'json', serverMapPath: ['mcpServers'] },
177
+ ],
178
+ transports: ['stdio', 'http'],
179
+ entry: entryUrl,
180
+ },
181
+ rules: {
182
+ scopes: [{ scope: 'project', file: '.kiro/steering/devsmind.md' }],
183
+ style: 'standalone',
184
+ },
185
+ memory: {
186
+ supported: false,
187
+ featureName: 'Knowledge / PR-comment learning',
188
+ note: 'Kiro has no file-based memory: its manual "Knowledge" store uses JSON + embeddings (not something safe to hand-write), and its autonomous agent\'s PR-comment-driven learning is an undocumented, AWS-internal, non-file-based store. The one thing DevsMind CAN influence — steering docs — is already handled by `devsmind rule`. To also engage the autonomous agent\'s learning, leave a PR review comment once, e.g. "always call search_nodes before grep, and stage_change + commit_changes after every change."',
189
+ },
190
+ },
191
+ {
192
+ id: 'antigravity',
193
+ label: 'Google Antigravity (IDE)',
194
+ kind: 'ide',
195
+ mcp: {
196
+ scopes: [
197
+ { scope: 'global', file: '~/.gemini/config/mcp_config.json', format: 'json', serverMapPath: ['mcpServers'] },
198
+ { scope: 'project', file: '.agents/mcp_config.json', format: 'json', serverMapPath: ['mcpServers'] },
199
+ ],
200
+ transports: ['stdio', 'http'],
201
+ entry: entryServerUrl,
202
+ note: 'Antigravity keys the remote endpoint as "serverUrl".',
203
+ },
204
+ rules: {
205
+ scopes: [{ scope: 'project', file: 'AGENTS.md' }],
206
+ style: 'append-section',
207
+ },
208
+ memory: {
209
+ supported: true,
210
+ featureName: 'Skills (/learn)',
211
+ scopes: [
212
+ { scope: 'project', dir: '.agents/skills/devsmind', file: 'SKILL.md', format: 'skill-md' },
213
+ ],
214
+ wrap: antigravitySkillWrap,
215
+ note: 'Antigravity discovers skills by scanning .agents/skills/ for any SKILL.md — same mechanism whether it was created via /learn or placed here directly.',
216
+ },
217
+ },
218
+ // ── CLI tools ───────────────────────────────────────────────────────────────
219
+ {
220
+ id: 'claude-code',
221
+ label: 'Claude Code (claude CLI / IDE extension)',
222
+ kind: 'cli',
223
+ mcp: {
224
+ scopes: [
225
+ { scope: 'project', file: '.mcp.json', format: 'json', serverMapPath: ['mcpServers'] },
226
+ ],
227
+ transports: ['stdio', 'http'],
228
+ entry: entryTyped,
229
+ cliInstaller: (t, ctx) => t === 'stdio'
230
+ ? 'claude mcp add --transport stdio devsmind -- devsmind start --stdio'
231
+ : `claude mcp add --transport http devsmind ${httpUrl(ctx)}`,
232
+ },
233
+ rules: {
234
+ scopes: [{ scope: 'project', file: 'CLAUDE.md' }],
235
+ style: 'append-section',
236
+ },
237
+ memory: {
238
+ supported: true,
239
+ featureName: 'Auto Memory',
240
+ scopes: [
241
+ { scope: 'global', dir: '~/.claude/projects', file: 'devsmind.md', format: 'markdown', needsUserConfirmedDir: true },
242
+ ],
243
+ note: 'MEMORY.md\'s first 200 lines/25KB load every session automatically; topic files like devsmind.md load only "on demand", so a one-line pointer is also appended into MEMORY.md so it actually gets found.',
244
+ pointerFile: { file: 'MEMORY.md', style: 'append-section' },
245
+ },
246
+ },
247
+ {
248
+ id: 'antigravity-cli',
249
+ label: 'Antigravity CLI',
250
+ kind: 'cli',
251
+ mcp: {
252
+ scopes: [
253
+ { scope: 'project', file: '.agents/mcp_config.json', format: 'json', serverMapPath: ['mcpServers'] },
254
+ { scope: 'global', file: '~/.gemini/config/mcp_config.json', format: 'json', serverMapPath: ['mcpServers'] },
255
+ ],
256
+ transports: ['stdio', 'http'],
257
+ entry: entryServerUrl,
258
+ note: 'Shares config with the Antigravity IDE; keys the remote endpoint as "serverUrl".',
259
+ },
260
+ rules: {
261
+ scopes: [{ scope: 'project', file: 'AGENTS.md' }],
262
+ style: 'append-section',
263
+ },
264
+ memory: {
265
+ supported: true,
266
+ featureName: 'Skills (/learn)',
267
+ scopes: [
268
+ { scope: 'project', dir: '.agents/skills/devsmind', file: 'SKILL.md', format: 'skill-md' },
269
+ ],
270
+ wrap: antigravitySkillWrap,
271
+ note: 'Same Skills mechanism as the Antigravity IDE — the CLI\'s /skills command browses this same .agents/skills/ directory.',
272
+ },
273
+ },
274
+ {
275
+ id: 'codex',
276
+ label: 'OpenAI Codex CLI',
277
+ kind: 'cli',
278
+ mcp: {
279
+ scopes: [
280
+ { scope: 'global', file: '~/.codex/config.toml', format: 'toml', serverMapPath: ['mcp_servers'] },
281
+ { scope: 'project', file: '.codex/config.toml', format: 'toml', serverMapPath: ['mcp_servers'] },
282
+ ],
283
+ transports: ['stdio', 'http'],
284
+ entry: entryUrl,
285
+ cliInstaller: (t) => t === 'stdio'
286
+ ? 'codex mcp add devsmind -- devsmind start --stdio'
287
+ : '# Codex: add the [mcp_servers.devsmind] url entry to ~/.codex/config.toml (no CLI flag for remote)',
288
+ note: 'Codex config is TOML. Remote (url) servers must be added by editing config.toml.',
289
+ },
290
+ rules: {
291
+ scopes: [{ scope: 'project', file: 'AGENTS.md' }],
292
+ style: 'append-section',
293
+ },
294
+ memory: {
295
+ supported: false,
296
+ featureName: 'Memories',
297
+ note: 'Codex\'s own docs explicitly warn: "these files are treated as generated state... don\'t rely on editing them by hand." A background consolidation job periodically regenerates ~/.codex/memories/MEMORY.md and memory_summary.md, so a manual write would likely just get overwritten. Nothing is written here — let Codex build this on its own over real sessions.',
298
+ },
299
+ },
300
+ {
301
+ id: 'qwen',
302
+ label: 'Qwen Code CLI',
303
+ kind: 'cli',
304
+ mcp: {
305
+ scopes: [
306
+ { scope: 'project', file: '.qwen/settings.json', format: 'json', serverMapPath: ['mcpServers'] },
307
+ { scope: 'global', file: '~/.qwen/settings.json', format: 'json', serverMapPath: ['mcpServers'] },
308
+ ],
309
+ transports: ['stdio', 'http'],
310
+ entry: entryHttpUrl,
311
+ cliInstaller: (t, ctx) => t === 'stdio'
312
+ ? 'qwen mcp add devsmind devsmind start --stdio'
313
+ : `qwen mcp add --transport http devsmind ${httpUrl(ctx)}`,
314
+ note: 'Qwen keys the Streamable-HTTP endpoint as "httpUrl".',
315
+ },
316
+ rules: {
317
+ scopes: [{ scope: 'project', file: 'QWEN.md' }],
318
+ style: 'append-section',
319
+ },
320
+ memory: {
321
+ supported: false,
322
+ featureName: 'Memory (background) / QWEN.md',
323
+ note: 'QWEN.md itself is confirmed safe to write to, but that\'s already the exact file `devsmind rule` places its block in for Qwen — no separate action needed. The newer, separate ~/.qwen/projects/<project>/memory/ background auto-memory directory has the same undocumented, auto-generated pattern as Codex\'s Memories, with no source confirming a manually-placed file survives — nothing is written there.',
324
+ },
325
+ },
326
+ ];
327
+ function getTarget(id) {
328
+ return exports.TARGETS.find(t => t.id === id);
329
+ }
330
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/cli/integrations/registry.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgGA,gCAEC;AAGD,8BAEC;AAKD,sCAMC;AAMD,4CAMC;AAqRD,8BAEC;AArZD,uCAAyB;AACzB,2CAA6B;AA4F7B,gFAAgF;AAEhF,qEAAqE;AACrE,SAAgB,UAAU;IACxB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;AAC7D,CAAC;AAED,kEAAkE;AAClE,SAAgB,SAAS,CAAC,GAAiB;IACzC,OAAO,EAAE,GAAG,EAAE,oBAAoB,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,gFAAgF;AAEhF,wGAAwG;AACxG,SAAgB,aAAa,CAAC,CAAS;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA4B,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAK,CAAS,CAAC,KAAK,CAAC;IAC5G,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,IAAY,EAAE,KAAY,EAAE,aAAqB;IAChF,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AAEjF,MAAM,OAAO,GAAG,CAAC,GAAiB,EAAE,EAAE,CAAC,oBAAoB,GAAG,CAAC,IAAI,MAAM,CAAC;AAE1E,0CAA0C;AAC1C,MAAM,QAAQ,GAAG,CAAC,CAAY,EAAE,GAAiB,EAAE,EAAE,CACnD,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAEvD,oDAAoD;AACpD,MAAM,UAAU,GAAG,CAAC,CAAY,EAAE,GAAiB,EAAE,EAAE,CACrD,CAAC,KAAK,OAAO;IACX,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,EAAE;IACpC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAE1C,kEAAkE;AAClE,MAAM,cAAc,GAAG,CAAC,CAAY,EAAE,GAAiB,EAAE,EAAE,CACzD,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAE7D,8DAA8D;AAC9D,MAAM,YAAY,GAAG,CAAC,CAAY,EAAE,GAAiB,EAAE,EAAE,CACvD,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAE3D,MAAM,aAAa,GAAG,CAAC,IAAY,EAAU,EAAE,CAC7C,wFAAwF,IAAI,IAAI,CAAC;AAEnG,wFAAwF;AACxF,MAAM,oBAAoB,GAAG,CAAC,IAAY,EAAU,EAAE,CACpD,0GAA0G,IAAI,IAAI,CAAC;AAErH,qEAAqE;AACrE,MAAM,aAAa,GAAW;IAC5B,KAAK,EAAE,sCAAsC;IAC7C,MAAM,EAAE,kDAAkD;IAC1D,KAAK,EAAE,8BAA8B;CACtC,CAAC;AAEF,gFAAgF;AAChF,6EAA6E;AAC7E,2EAA2E;AAE9D,QAAA,OAAO,GAAgB;IAClC,6EAA6E;IAC7E;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;gBAC7F,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aAC/F;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,QAAQ;SAChB;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC;YAClE,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,aAAa;SACpB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,uYAAuY;SAC9Y;KACF;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,SAAS,CAAC,EAAE;gBAC1F,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,SAAS,CAAC,EAAE;aACrF;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,oDAAoD;SAC3D;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,iCAAiC,EAAE,CAAC;YACvE,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,gBAAgB;YAC7B,IAAI,EAAE,8TAA8T;SACrU;KACF;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,qCAAqC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aAChH;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,0EAA0E;SACjF;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC;YACnE,KAAK,EAAE,YAAY;SACpB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,kBAAkB;YAC/B,IAAI,EAAE,2UAA2U;SAClV;KACF;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;gBACpG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,2BAA2B,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aACtG;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,QAAQ;SAChB;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC;YAClE,KAAK,EAAE,YAAY;SACpB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,iCAAiC;YAC9C,IAAI,EAAE,yfAAyf;SAChgB;KACF;IACD;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;gBAC5G,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aACrG;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,sDAAsD;SAC7D;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,iBAAiB;YAC9B,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,yBAAyB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE;aAC3F;YACD,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,uJAAuJ;SAC9J;KACF;IAED,+EAA+E;IAC/E;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,0CAA0C;QACjD,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aACvF;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,UAAU;YACjB,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CACvB,CAAC,KAAK,OAAO;gBACX,CAAC,CAAC,qEAAqE;gBACvE,CAAC,CAAC,4CAA4C,OAAO,CAAC,GAAG,CAAC,EAAE;SACjE;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,aAAa;YAC1B,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,qBAAqB,EAAE,IAAI,EAAE;aACrH;YACD,IAAI,EAAE,0MAA0M;YAChN,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE;SAC5D;KACF;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,KAAK,EAAE,iBAAiB;QACxB,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;gBACpG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aAC7G;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,kFAAkF;SACzF;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,iBAAiB;YAC9B,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,yBAAyB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE;aAC3F;YACD,IAAI,EAAE,oBAAoB;YAC1B,IAAI,EAAE,wHAAwH;SAC/H;KACF;IACD;QACE,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,kBAAkB;QACzB,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,aAAa,CAAC,EAAE;gBACjG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,aAAa,CAAC,EAAE;aACjG;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,QAAQ;YACf,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAClB,CAAC,KAAK,OAAO;gBACX,CAAC,CAAC,kDAAkD;gBACpD,CAAC,CAAC,oGAAoG;YAC1G,IAAI,EAAE,kFAAkF;SACzF;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACjD,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,uWAAuW;SAC9W;KACF;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,KAAK;QACX,GAAG,EAAE;YACH,MAAM,EAAE;gBACN,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;gBAChG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,YAAY,CAAC,EAAE;aAClG;YACD,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YAC7B,KAAK,EAAE,YAAY;YACnB,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CACvB,CAAC,KAAK,OAAO;gBACX,CAAC,CAAC,8CAA8C;gBAChD,CAAC,CAAC,0CAA0C,OAAO,CAAC,GAAG,CAAC,EAAE;YAC9D,IAAI,EAAE,sDAAsD;SAC7D;QACD,KAAK,EAAE;YACL,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC/C,KAAK,EAAE,gBAAgB;SACxB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,KAAK;YAChB,WAAW,EAAE,+BAA+B;YAC5C,IAAI,EAAE,kZAAkZ;SACzZ;KACF;CACF,CAAC;AAEF,SAAgB,SAAS,CAAC,EAAU;IAClC,OAAO,eAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACxC,CAAC"}
@@ -1,3 +1,16 @@
1
+ import { DevMindConfig } from '../utils/config';
2
+ /**
3
+ * Build the ready-to-paste DevsMind workspace rule from a project's config.
4
+ * Pure string builder — no I/O — so it can be printed or written to a file.
5
+ */
6
+ export declare function buildRule(config: DevMindConfig, devmindDir: string): string;
7
+ /**
8
+ * `devsmind rule` — print the workspace rule and, interactively, help place it
9
+ * in the chosen tool's native rules file (manual snippet or automatic write).
10
+ * Falls back to plain printing when piped/non-TTY or when `--print` is passed,
11
+ * preserving `devsmind rule > file` usage.
12
+ */
1
13
  export declare function handleRule(opts: {
2
14
  path?: string;
3
- }): void;
15
+ print?: boolean;
16
+ }): Promise<void>;
package/dist/cli/rule.js CHANGED
@@ -33,46 +33,19 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildRule = buildRule;
36
37
  exports.handleRule = handleRule;
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
39
- function findDevmindDir(startDir) {
40
- let current = path.resolve(startDir);
41
- while (true) {
42
- const candidate = path.join(current, '.devmind');
43
- if (fs.existsSync(path.join(candidate, 'config.json'))) {
44
- return candidate;
45
- }
46
- const parent = path.dirname(current);
47
- if (parent === current)
48
- return null;
49
- current = parent;
50
- }
51
- }
52
- function handleRule(opts) {
53
- const cwd = process.cwd();
54
- let devmindDir;
55
- if (opts.path) {
56
- const resolved = path.resolve(opts.path);
57
- devmindDir = fs.existsSync(path.join(resolved, 'config.json')) ? resolved : null;
58
- }
59
- else {
60
- devmindDir = findDevmindDir(cwd);
61
- }
62
- if (!devmindDir) {
63
- console.error(`❌ No .devmind directory found.\n` +
64
- ` Run from inside a DevsMind brain folder, or pass --path <devmind_path>.`);
65
- process.exit(1);
66
- }
67
- const configPath = path.join(devmindDir, 'config.json');
68
- let config;
69
- try {
70
- config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
71
- }
72
- catch {
73
- console.error(`❌ Failed to read config.json at ${configPath}`);
74
- process.exit(1);
75
- }
40
+ const config_1 = require("../utils/config");
41
+ const prompt_1 = require("./integrations/prompt");
42
+ const registry_1 = require("./integrations/registry");
43
+ const scanner_1 = require("../utils/scanner");
44
+ /**
45
+ * Build the ready-to-paste DevsMind workspace rule from a project's config.
46
+ * Pure string builder — no I/O — so it can be printed or written to a file.
47
+ */
48
+ function buildRule(config, devmindDir) {
76
49
  const projectName = config.project_name;
77
50
  const mode = config.mode;
78
51
  const notes = config.notes;
@@ -95,10 +68,19 @@ function handleRule(opts) {
95
68
  `**Repos**: ${repoLines}`,
96
69
  notes ? `**Notes**: ${notes}` : '',
97
70
  '',
71
+ '### 🧠 What DevsMind Actually Is — Read This First',
72
+ '',
73
+ 'This is NOT a normal tool you reach for only when asked to search something. DevsMind is the **persistent shared brain for this entire team** — every teammate\'s AI agent, in every session, reads from the exact same graph you are about to write to. There is no "your copy" vs "their copy."',
74
+ '',
75
+ `**If you skip ${bt}stage_change${bt} + ${bt}commit_changes${bt}, you are not skipping a formality.** You are leaving the graph stale for every other developer's AI agent that queries this code later — tomorrow, next week, on a completely different task. And the reasoning behind your change (why it was made, what ticket drove it, what was broken before, what you tried and rejected) exists **only in this conversation, right now**. It is not in the diff. It is not in the commit message. If it isn't recorded this turn, it is gone forever — no reindex, no log, no ${bt}git blame${bt} can ever recover it.`,
76
+ '',
77
+ `* ${bt}get_node_graph${bt} fills the gap git can't: git shows you WHAT lines changed, never what depends on them. This gives you the live call graph instantly — every caller, every callee — so you find out what breaks BEFORE you change a signature, not after a teammate hits the bug.`,
78
+ `* ${bt}get_node_history${bt} fills a different gap: git blame tells you WHO and WHEN. It never tells you WHY. The actual decision — what was tried, what was rejected, what ticket demanded this, what was broken before — only exists here, written by whichever AI agent made the change. Skip it before refactoring and you risk silently re-breaking a bug that was already fixed once, or undoing a decision that had a reason you never saw.`,
79
+ '',
98
80
  '### ⚠️ CRITICAL PRE-FLIGHT CHECK & FEW-SHOT EXAMPLE',
99
81
  '',
100
82
  'Before executing any filesystem search or reading any file contents, you MUST perform this check:',
101
- `1. **Am I searching for code/modules/features?** -> You MUST use ${bt}search_nodes${bt} or ${bt}search_code${bt} first. DO NOT start with grep or native filesystem search.`,
83
+ `1. **Am I searching for code/modules/features?** -> You MUST use ${bt}search_nodes${bt} first it searches names/reasoning AND falls back to full code-content search automatically if nothing matches, in the SAME call. DO NOT start with grep or native filesystem search.`,
102
84
  `2. **Am I reading a source file?** -> You MUST call ${bt}get_node_code${bt} instead. It returns that one function/class parsed live from the file, not the whole file — far cheaper. Only read the raw file if the node genuinely isn't in the graph.`,
103
85
  `3. **Am I tracing how something flows through the code?** -> You MUST call ${bt}get_node_graph${bt} with ${bt}direction: "out"${bt} and ${bt}include_code: true${bt}. This returns the entry point PLUS everything it transitively calls, each with its source, in ONE call. Do NOT chain ${bt}get_node_code${bt} calls one function at a time — that wastes a chat turn per function.`,
104
86
  '',
@@ -118,8 +100,7 @@ function handleRule(opts) {
118
100
  '',
119
101
  '| Situation | Tool |',
120
102
  '|-----------|------|',
121
- `| Searching for a module, feature, or concept | ${bt}search_nodes${bt} |`,
122
- `| Searching for specific code fragments, variables, or regex patterns | ${bt}search_code${bt} |`,
103
+ `| Searching for a module, feature, concept, code fragment, variable, or regex pattern | ${bt}search_nodes${bt} (name/reasoning match, auto-falls-back to code-content search) |`,
123
104
  `| Want to list/discover all nodes for a component or directory | ${bt}list_nodes${bt} |`,
124
105
  `| Need to read the code of ONE specific function/class | ${bt}get_node_code${bt} |`,
125
106
  `| Tracing a request/feature/flow through MULTIPLE functions | ${bt}get_node_graph${bt} with ${bt}direction:"out"${bt} + ${bt}include_code:true${bt} (ONE call — never chain ${bt}get_node_code${bt}) |`,
@@ -131,32 +112,33 @@ function handleRule(opts) {
131
112
  '',
132
113
  '### ⚠️ MANDATORY: Record Every Code Change in the Graph',
133
114
  '',
134
- 'This is NOT optional. Whenever you add, modify, rename, or delete code, you MUST record it in the graph in the SAME turn — before you consider the task done. An answer that changed code but did not update the graph is INCOMPLETE.',
115
+ 'This is NOT optional. Whenever you add, modify, rename, or delete code, you MUST record it in the graph in the SAME turn — before you consider the task done. An answer that changed code but did not update the graph is INCOMPLETE: the code changed, but every other developer\'s AI agent querying this project still sees the old version and none of the reasoning behind the new one.',
135
116
  '',
136
- `1. **For ANY change (one file or many):** call ${bt}stage_change${bt} EXACTLY ONCE for EVERY function/class/entity you touched pass its ${bt}node_id${bt}, ${bt}file_path${bt}, ${bt}code_snapshot${bt}, and ${bt}reasoning${bt}. You do NOT reason about connections. When every touched entity is staged, call ${bt}commit_changes${bt} EXACTLY ONCE.`,
137
- `2. **⚠️ ${bt}commit_changes${bt} IS REQUIRED.** Staging alone writes NOTHING to the graph. If you call ${bt}stage_change${bt} and forget ${bt}commit_changes${bt}, all your work is lost and the run is wasted. NEVER end your turn with un-committed staged changes.`,
138
- `3. **Do NOT hand-manage edges.** ${bt}commit_changes${bt} resolves all connections from the code via AST automatically. Never try to reason about or pass connections yourself.`,
139
- `4. **Do NOT print node/history data as text instead of calling the tools.** Printing does not write to the graph and wastes the turn.`,
117
+ `**Scope this applies to source code only.** ${bt}stage_change${bt} models functions/classes/logic entities and will be REJECTED for anything outside: ${Array.from(scanner_1.INDEXABLE_EXTENSIONS).sort().join(', ')}. Do NOT stage stylesheets (${bt}.css${bt}/${bt}.scss${bt}/${bt}.less${bt}), markup, JSON/config, docs, images, or other non-code assets — they have no callers/callees to resolve and only bloat the graph with dead-end nodes. If a file's extension isn't in that list, skip it; do not retry.`,
118
+ '',
119
+ `1. **For ANY change (one file or many):** call ${bt}stage_change${bt} EXACTLY ONCE for EVERY function/class/entity you touched pass its ${bt}node_id${bt}, ${bt}file_path${bt}, ${bt}code_snapshot${bt}, and ${bt}reasoning${bt}. You do NOT reason about connections. The ${bt}reasoning${bt} you write is the whole point — it's the only place "why" ever gets recorded, and it only exists if you write it down right now. When every touched entity is staged, call ${bt}commit_changes${bt} EXACTLY ONCE.`,
120
+ `2. **⚠️ ${bt}commit_changes${bt} IS REQUIRED.** Staging alone writes NOTHING to the graph it just buffers to a local file no one else will ever read. If you call ${bt}stage_change${bt} and forget ${bt}commit_changes${bt}, the reasoning you just wrote is stranded and effectively lost: the next session (yours or a teammate's) starts from a graph that never learned about this change. NEVER end your turn with un-committed staged changes.`,
121
+ `3. **Do NOT hand-manage edges.** ${bt}commit_changes${bt} resolves all connections from the code via AST automatically. Never try to reason about or pass connections yourself — a hand-guessed edge is more likely wrong than one resolved from the actual code.`,
122
+ `4. **Do NOT print node/history data as text instead of calling the tools.** Printing looks like you did the work, but writes nothing to the graph — the next session sees no trace this ever happened.`,
140
123
  '',
141
124
  '### Critical Rules',
142
125
  '',
143
- `1. **Never guess dependencies** — call ${bt}get_node_graph${bt} before touching any function signature.`,
144
- `2. **Always read history first** — call ${bt}get_node_history${bt} before refactoring to understand past decisions.`,
145
- `3. **No deletions** — never delete nodes. Use ${bt}deprecate_node${bt} to preserve history.`,
126
+ `1. **Never guess dependencies** — call ${bt}get_node_graph${bt} before touching any function signature. Git shows you what a diff changed; it never shows you what else calls this function and would silently break. Guessing here is how a "small" change becomes a production incident someone else has to debug without knowing why you touched this.`,
127
+ `2. **Always read history first** — call ${bt}get_node_history${bt} before refactoring to understand past decisions. Skipping this risks re-introducing a bug that was already fixed once, or undoing a decision that had a reason you can't see from the code alone.`,
128
+ `3. **No deletions** — never delete nodes. Use ${bt}deprecate_node${bt} to preserve history, so the reasoning behind code that's no longer active isn't lost the moment it's removed.`,
146
129
  `4. **Resurrecting nodes** — calling ${bt}stage_change${bt} on a deprecated node automatically re-activates it on the next ${bt}commit_changes${bt}.`,
147
- `5. **Search before grep** — use ${bt}search_nodes${bt} or ${bt}search_code${bt} before any filesystem search. If no nodes are found using ${bt}search_nodes${bt}, use the ${bt}list_nodes${bt} tool to see all available nodes in the graph.`,
148
- `6. **Read code through the graph, not the filesystem** — call ${bt}get_node_code${bt} instead of opening a source file. It parses the node live from disk and returns only that entity, so it is always current AND far cheaper than reading the whole file. If the node isn't in the graph at all, read the file, then ${bt}stage_change${bt} + ${bt}commit_changes${bt} to add it.`,
149
- `7. **Fix drift when the tools report it** — if ${bt}get_node_code${bt} returns ${bt}snapshot_outdated: true${bt}, the graph has fallen behind the code on disk. Re-record that node with ${bt}stage_change${bt} + ${bt}commit_changes${bt}. If it returns ${bt}source: "cached"${bt}, the symbol could NOT be found in its file — it was likely renamed, moved, or deleted, so the code you got may be wrong. Verify against the file, then ${bt}rename_node${bt} or ${bt}deprecate_node${bt} as appropriate.`,
130
+ `5. **Search before grep** — use ${bt}search_nodes${bt} before any filesystem search; it tries identifiers first and automatically falls back to a full code-content search if nothing matches, so one call covers both cases. A raw grep finds text; it can't tell you the reasoning already recorded behind that code, which is the whole reason to check here first. If it still finds nothing, use the ${bt}list_nodes${bt} tool to see all available nodes in the graph.`,
131
+ `6. **Read code through the graph, not the filesystem** — call ${bt}get_node_code${bt} instead of opening a source file. It parses the node live from disk and returns only that entity, so it is always current AND far cheaper than reading the whole file. Reading the raw file instead means the graph has no way to know you looked, so any drift between what's recorded and what's on disk goes unnoticed. If the node isn't in the graph at all, read the file, then ${bt}stage_change${bt} + ${bt}commit_changes${bt} to add it.`,
132
+ `7. **Fix drift when the tools report it** — if ${bt}get_node_code${bt} returns ${bt}snapshot_outdated: true${bt}, the graph has fallen behind the code on disk. Re-record that node with ${bt}stage_change${bt} + ${bt}commit_changes${bt}. If it returns ${bt}source: "cached"${bt}, the symbol could NOT be found in its file — it was likely renamed, moved, or deleted, so the code you got may be wrong. Verify against the file, then ${bt}rename_node${bt} or ${bt}deprecate_node${bt} as appropriate. Ignoring a drift signal means everyone after you keeps trusting a record you already know is wrong.`,
150
133
  `8. **No external scripts for indexing** — When indexing a repository, NEVER write or run external scripts (like Python, Bash, or Node.js) to automate or lazy load indexing. You must perform the indexing natively step-by-step in the chat using the designated tools: ${bt}index_start${bt}, ${bt}index_checkpoint${bt}, ${bt}index_continue${bt}, and ${bt}index_complete${bt}. This ensures progress is tracked in the SQLite scratchpad database and allows indexing to be safely resumed across chat sessions if context limits are hit.`,
151
134
  `9. **Continuous Indexing** — Once you start the codebase indexing process, do not stop, pause, or ask for user confirmation between checkpoints. Keep executing and indexing files continuously until the workspace is fully indexed, or until the chat session's context token limit is reached.`,
152
- `10. **Grow-as-you-go Graph Maintenance (MANDATORY)** — DevsMind is a living code graph and keeping it in sync is a hard requirement, not a nicety. At the end of EVERY task or message where code was added, modified, renamed, or deleted, you MUST record it per the "MANDATORY: Record Every Code Change" section above — ${bt}stage_change${bt} per touched entity then ${bt}commit_changes${bt} once. NEVER finish a turn with un-committed staged changes. If you notice deprecated/stale nodes, clean them up with ${bt}deprecate_node${bt} or ${bt}rename_node${bt}. Do not let the graph go stale.`,
135
+ `10. **Grow-as-you-go Graph Maintenance (MANDATORY)** — DevsMind is a living code graph and keeping it in sync is a hard requirement, not a nicety. At the end of EVERY task or message where code was added, modified, renamed, or deleted, you MUST record it per the "MANDATORY: Record Every Code Change" section above — ${bt}stage_change${bt} per touched entity then ${bt}commit_changes${bt} once. NEVER finish a turn with un-committed staged changes. If you notice deprecated/stale nodes, clean them up with ${bt}deprecate_node${bt} or ${bt}rename_node${bt}. A graph that's allowed to go stale stops being worth checking at all — every skipped update is one more reason for the next agent to distrust what's here and fall back to guessing.`,
153
136
  '',
154
137
  '### Available Tools',
155
138
  '',
156
139
  '| Tool | Use when |',
157
140
  '|------|----------|',
158
- `| ${bt}search_nodes${bt} | Find nodes by name, keyword, or reasoning text |`,
159
- `| ${bt}search_code${bt} | Regex or string search over cached codebase code snapshots |`,
141
+ `| ${bt}search_nodes${bt} | Find code by name, keyword, or reasoning text — auto-falls-back to a regex/string code-content search if nothing matches |`,
160
142
  `| ${bt}list_nodes${bt} | List nodes in the graph with optional filters (type, file path, etc.) |`,
161
143
  `| ${bt}get_node_summary${bt} | Get file location, connection count, history count for a node |`,
162
144
  `| ${bt}get_node_code${bt} | Get ONE node's current source, parsed live from its file (use instead of reading the file) |`,
@@ -176,7 +158,9 @@ function handleRule(opts) {
176
158
  '',
177
159
  '> All tool argument schemas are exposed automatically by the MCP server.'
178
160
  ];
179
- const rule = lines.filter(l => l !== null).join('\n');
161
+ return lines.filter(l => l !== null).join('\n');
162
+ }
163
+ function printRuleBanner(rule, projectName, tip) {
180
164
  const divider = '═'.repeat(70);
181
165
  console.log(`\n${divider}`);
182
166
  console.log(` DevsMind Workspace Rule — "${projectName}"`);
@@ -184,8 +168,86 @@ function handleRule(opts) {
184
168
  console.log(`${divider}\n`);
185
169
  console.log(rule);
186
170
  console.log(`\n${divider}`);
187
- console.log(` 💡 Tip: save this to .agents/AGENTS.md in your workspace root`);
188
- console.log(` or paste directly into your IDE's AI rules/instructions panel.`);
171
+ if (tip) {
172
+ console.log(tip);
173
+ }
174
+ else {
175
+ console.log(` 💡 Tip: save this to .agents/AGENTS.md in your workspace root`);
176
+ console.log(` or paste directly into your IDE's AI rules/instructions panel.`);
177
+ }
189
178
  console.log(`${divider}\n`);
190
179
  }
180
+ /**
181
+ * `devsmind rule` — print the workspace rule and, interactively, help place it
182
+ * in the chosen tool's native rules file (manual snippet or automatic write).
183
+ * Falls back to plain printing when piped/non-TTY or when `--print` is passed,
184
+ * preserving `devsmind rule > file` usage.
185
+ */
186
+ async function handleRule(opts) {
187
+ const devmindDir = (0, config_1.resolveDevmindDir)(opts.path);
188
+ if (!devmindDir) {
189
+ console.error(`❌ No .devmind directory found.\n` +
190
+ ` Run from inside a DevsMind brain folder, or pass --path <devmind_path>.`);
191
+ process.exit(1);
192
+ }
193
+ const configPath = path.join(devmindDir, 'config.json');
194
+ let config;
195
+ try {
196
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
197
+ }
198
+ catch {
199
+ console.error(`❌ Failed to read config.json at ${configPath}`);
200
+ process.exit(1);
201
+ return;
202
+ }
203
+ const rule = buildRule(config, devmindDir);
204
+ const projectName = config.project_name;
205
+ // Backward-compat: piped/redirected output or explicit --print → plain print.
206
+ if (opts.print || !process.stdout.isTTY) {
207
+ printRuleBanner(rule, projectName);
208
+ return;
209
+ }
210
+ const workspaceRoot = path.dirname(devmindDir);
211
+ try {
212
+ const target = await (0, prompt_1.pickTarget)();
213
+ const mode = await (0, prompt_1.pickMode)();
214
+ if (mode === 'manual') {
215
+ const scope = target.rules.scopes[0];
216
+ const file = (0, registry_1.resolveScopeFile)(scope.file, scope.scope, workspaceRoot);
217
+ const noteFrontmatter = target.rules.wrap
218
+ ? '\n (this file needs frontmatter — automatic mode adds it for you)'
219
+ : '';
220
+ printRuleBanner(rule, projectName, ` 💡 Save this to ${file.replace(/\\/g, '/')}${noteFrontmatter}`);
221
+ return;
222
+ }
223
+ // Automatic mode.
224
+ const scope = await (0, prompt_1.pickRuleScope)(target);
225
+ let filePath;
226
+ if (scope.scope === 'project') {
227
+ const base = await (0, prompt_1.pickDirectory)(workspaceRoot, `Where is the project root for ${target.label}?`);
228
+ filePath = path.join(base, (0, registry_1.resolveOsPath)(scope.file));
229
+ }
230
+ else {
231
+ filePath = (0, registry_1.resolveScopeFile)(scope.file, 'global', workspaceRoot);
232
+ }
233
+ const merged = (0, prompt_1.mergeRuleFile)(filePath, rule, target.rules.style, target.rules.wrap);
234
+ console.log(`\n📝 Target: ${filePath.replace(/\\/g, '/')} (${merged.existed ? (target.rules.style === 'append-section' ? 'merge DevsMind block into existing' : 'overwrite dedicated file') : 'create new'})`);
235
+ console.log(`\n${target.rules.style === 'append-section' ? 'The DevsMind block to be written:' : 'File contents to be written:'}\n`);
236
+ console.log(merged.preview.split('\n').map(l => ' ' + l).join('\n'));
237
+ const ok = await (0, prompt_1.confirmPrompt)('Write this?', true);
238
+ if (!ok) {
239
+ console.log('\nAborted — nothing written.');
240
+ return;
241
+ }
242
+ (0, prompt_1.writeConfigFile)(filePath, merged.content);
243
+ console.log(`\n✅ DevsMind rule written to ${filePath.replace(/\\/g, '/')} for ${target.label}.`);
244
+ }
245
+ catch (err) {
246
+ if (err instanceof prompt_1.CancelledError) {
247
+ console.log('\nCancelled.');
248
+ return;
249
+ }
250
+ throw err;
251
+ }
252
+ }
191
253
  //# sourceMappingURL=rule.js.map