monomind 1.6.8 → 1.7.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.
@@ -11,6 +11,7 @@ import { dirname } from 'path';
11
11
  const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = dirname(__filename);
13
13
  import { detectPlatform, DEFAULT_INIT_OPTIONS } from './types.js';
14
+ import { writeSharedInstructions } from './shared-instructions-generator.js';
14
15
  import { generateSettingsJson, generateSettings } from './settings-generator.js';
15
16
  import { generateMCPJson } from './mcp-generator.js';
16
17
  import { generateStatuslineScript } from './statusline-generator.js';
@@ -66,7 +67,7 @@ const SKILLS_MAP = {
66
67
  const COMMANDS_MAP = {
67
68
  core: [
68
69
  'monomind-help.md', 'monomind-swarm.md', 'monomind-memory.md',
69
- 'monomind-do.md', 'monomind-idea.md', 'monomind-createtask.md', 'monomind-repeat.md',
70
+ 'monomind-do.md', 'monomind-idea.md', 'monomind-improve.md', 'monomind-createtask.md', 'monomind-repeat.md',
70
71
  'mastermind.md', 'list-agents.md', 'use-agent.md',
71
72
  'metrics.md', 'tokens.md', 'browse.md', 'sparc.md', 'ts.md',
72
73
  ],
@@ -281,12 +282,18 @@ export async function executeInit(options) {
281
282
  if (options.components.claudeMd) {
282
283
  await writeClaudeMd(targetDir, options, result);
283
284
  }
285
+ // Generate .agents/shared_instructions.md + seed project memory
286
+ writeSharedInstructions(targetDir, options.force, result);
284
287
  // Count enabled hooks
285
288
  result.summary.hooksEnabled = countEnabledHooks(options);
286
289
  // Build knowledge graph in background (non-blocking)
287
290
  if (options.components.graphify) {
288
291
  await initKnowledgeGraph(targetDir, result);
289
292
  }
293
+ // Start daemon with background workers (non-blocking)
294
+ await startDaemonBackground(targetDir, result);
295
+ // Run doctor auto-fix (non-blocking, best-effort)
296
+ await runDoctorFix(targetDir, result);
290
297
  }
291
298
  catch (error) {
292
299
  result.success = false;
@@ -295,39 +302,129 @@ export async function executeInit(options) {
295
302
  return result;
296
303
  }
297
304
  /**
298
- * Spawn a background process to build the @monomind/graph knowledge graph.
299
- * Fire-and-forget: init does not wait for the ~20s graph build to complete.
300
- * Non-fatal: if @monomind/graph is unavailable the step is simply skipped.
305
+ * Ensure graphify (Python knowledge graph engine) is installed.
306
+ * Tries uv first, falls back to pip. Non-fatal if neither works.
307
+ */
308
+ async function ensureGraphifyInstalled(result) {
309
+ const { execSync } = await import('child_process');
310
+ try {
311
+ execSync('graphify --help', { encoding: 'utf8', stdio: 'ignore' });
312
+ return true;
313
+ }
314
+ catch {
315
+ const installers = ['uv tool install graphifyy', 'pip install graphifyy'];
316
+ for (const cmd of installers) {
317
+ try {
318
+ execSync(cmd, { encoding: 'utf8', stdio: 'ignore', timeout: 120000 });
319
+ result.created.files.push('graphify (installed via ' + cmd.split(' ')[0] + ')');
320
+ return true;
321
+ }
322
+ catch { /* try next */ }
323
+ }
324
+ result.skipped.push('graphify: could not auto-install (run: uv tool install graphifyy)');
325
+ return false;
326
+ }
327
+ }
328
+ /**
329
+ * Initialize graphify fully: install skill, git hooks, build graph, install watch.
330
+ * Fire-and-forget for the graph build — init does not wait for it to complete.
331
+ * Non-fatal: if graphify is unavailable the step is simply skipped.
301
332
  */
302
333
  async function initKnowledgeGraph(targetDir, result) {
334
+ const installed = await ensureGraphifyInstalled(result);
335
+ if (!installed)
336
+ return;
337
+ const { execSync, spawn } = await import('child_process');
338
+ const { mkdirSync } = await import('fs');
339
+ const outputDir = path.join(targetDir, '.monomind', 'graph');
340
+ mkdirSync(outputDir, { recursive: true });
341
+ // 1. Install graphify skill into Claude Code config
303
342
  try {
304
- // Verify the package is resolvable before spawning fast path to skip gracefully.
305
- await import('@monomind/graph');
306
- const outputDir = path.join(targetDir, '.monomind', 'graph');
307
- const { spawn } = await import('child_process');
308
- // Escape single quotes in path strings for the inline ES module script.
309
- const safePath = targetDir.replace(/'/g, "\\'");
310
- const safeOut = outputDir.replace(/'/g, "\\'");
311
- const script = `
312
- import('@monomind/graph').then(({ buildGraph }) =>
313
- buildGraph('${safePath}', { codeOnly: true, outputDir: '${safeOut}' })
314
- ).then(r => console.log('[graph] built: ' + r.filesProcessed + ' files, ' + r.analysis.stats.nodes + ' nodes'))
315
- .catch(e => console.error('[graph] build failed:', e.message));
316
- `;
317
- const child = spawn(process.execPath, ['--input-type=module'], {
318
- stdio: ['pipe', 'ignore', 'ignore'],
343
+ execSync('graphify claude install', { cwd: targetDir, stdio: 'ignore', timeout: 10000 });
344
+ result.created.files.push('graphify claude skill (installed)');
345
+ }
346
+ catch { /* non-fatal */ }
347
+ // 2. Install git hooks (auto-rebuild on commit)
348
+ try {
349
+ execSync('graphify hook install', { cwd: targetDir, stdio: 'ignore', timeout: 10000 });
350
+ result.created.files.push('graphify git hooks (post-commit, post-checkout)');
351
+ }
352
+ catch { /* non-fatal */ }
353
+ // 3. Build knowledge graph in background (fire-and-forget)
354
+ try {
355
+ const child = spawn('graphify', ['update', targetDir], {
356
+ stdio: 'ignore',
319
357
  detached: true,
320
358
  cwd: targetDir,
321
359
  });
322
- // Write the script to stdin then close so node processes it.
323
- child.stdin?.write(script);
324
- child.stdin?.end();
325
360
  child.unref();
326
361
  result.created.files.push('.monomind/graph/ (knowledge graph building in background)');
327
362
  }
328
363
  catch (_err) {
329
- // Non-fatal — @monomind/graph is an optional enhancement.
330
- result.skipped.push('knowledge graph: @monomind/graph not available');
364
+ result.skipped.push('knowledge graph: graphify update failed');
365
+ }
366
+ // 4. Start graphify watch daemon (live graph updates on file save)
367
+ try {
368
+ const watchChild = spawn('graphify', ['watch', targetDir], {
369
+ stdio: 'ignore',
370
+ detached: true,
371
+ cwd: targetDir,
372
+ });
373
+ watchChild.unref();
374
+ // Store PID so it can be stopped later
375
+ const { writeFileSync } = await import('fs');
376
+ const pidPath = path.join(outputDir, 'watch.pid');
377
+ writeFileSync(pidPath, String(watchChild.pid));
378
+ result.created.files.push('graphify watch (live graph updates on file save)');
379
+ }
380
+ catch { /* non-fatal */ }
381
+ }
382
+ /**
383
+ * Start the monomind daemon with background workers.
384
+ * Non-fatal: if daemon fails to start, init continues.
385
+ */
386
+ async function startDaemonBackground(targetDir, result) {
387
+ try {
388
+ const { execSync } = await import('child_process');
389
+ // Check if daemon is already running
390
+ const pidFile = path.join(targetDir, '.monomind', 'daemon.pid');
391
+ const { existsSync, readFileSync } = await import('fs');
392
+ if (existsSync(pidFile)) {
393
+ const pid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);
394
+ try {
395
+ process.kill(pid, 0);
396
+ result.skipped.push('daemon: already running (PID ' + pid + ')');
397
+ return;
398
+ }
399
+ catch { /* stale PID, continue */ }
400
+ }
401
+ execSync('npx monomind@latest daemon start --background', {
402
+ cwd: targetDir,
403
+ stdio: 'ignore',
404
+ timeout: 15000,
405
+ });
406
+ result.created.files.push('monomind daemon (background workers started)');
407
+ }
408
+ catch {
409
+ result.skipped.push('daemon: could not auto-start (run: monomind daemon start)');
410
+ }
411
+ }
412
+ /**
413
+ * Run doctor --install to auto-fix any remaining issues.
414
+ * Non-fatal: best-effort health check and auto-install.
415
+ */
416
+ async function runDoctorFix(targetDir, result) {
417
+ try {
418
+ const { execSync } = await import('child_process');
419
+ execSync('npx monomind@latest doctor --install', {
420
+ cwd: targetDir,
421
+ stdio: 'ignore',
422
+ timeout: 120000,
423
+ });
424
+ result.created.files.push('doctor --install (health check + auto-fix)');
425
+ }
426
+ catch {
427
+ result.skipped.push('doctor: auto-fix skipped (run: monomind doctor --install)');
331
428
  }
332
429
  }
333
430
  /**
@@ -1811,6 +1908,37 @@ async function writeClaudeMd(targetDir, options, result) {
1811
1908
  catch {
1812
1909
  // Non-critical — global CLAUDE.md is best-effort
1813
1910
  }
1911
+ // Also inject the token-display hook into ~/.claude/settings.json
1912
+ const globalSettingsPath = path.join(globalClaudeDir, 'settings.json');
1913
+ try {
1914
+ if (!fs.existsSync(globalClaudeDir)) {
1915
+ fs.mkdirSync(globalClaudeDir, { recursive: true });
1916
+ }
1917
+ let globalSettings = {};
1918
+ if (fs.existsSync(globalSettingsPath)) {
1919
+ try {
1920
+ globalSettings = JSON.parse(fs.readFileSync(globalSettingsPath, 'utf-8'));
1921
+ }
1922
+ catch { /* malformed JSON — start fresh */ }
1923
+ }
1924
+ // Inject SessionStart token hook if not already present
1925
+ const hooks = globalSettings.hooks ?? {};
1926
+ const sessionStartHooks = hooks['SessionStart'] ?? [];
1927
+ const tokenHookCommand = 'npx --yes monomind@latest tokens today';
1928
+ const alreadyPresent = sessionStartHooks.some(entry => Array.isArray(entry.hooks) && entry.hooks.some(h => h.command === tokenHookCommand));
1929
+ if (!alreadyPresent) {
1930
+ sessionStartHooks.push({
1931
+ hooks: [{ type: 'command', command: tokenHookCommand, timeout: 10000 }],
1932
+ });
1933
+ hooks['SessionStart'] = sessionStartHooks;
1934
+ globalSettings.hooks = hooks;
1935
+ fs.writeFileSync(globalSettingsPath, JSON.stringify(globalSettings, null, 2), 'utf-8');
1936
+ result.created.files.push('~/.claude/settings.json (added token hook)');
1937
+ }
1938
+ }
1939
+ catch {
1940
+ // Non-critical — global settings hook is best-effort
1941
+ }
1814
1942
  }
1815
1943
  }
1816
1944
  /**
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared Instructions Generator
3
+ *
4
+ * Auto-detects project profile and generates:
5
+ * 1. .agents/shared_instructions.md — prepended to every agent prompt
6
+ * 2. Memory seeds — pre-loaded into AgentDB so agents start with project best practices
7
+ */
8
+ import type { InitResult } from './types.js';
9
+ export interface ProjectProfile {
10
+ name: string;
11
+ description: string;
12
+ language: 'typescript' | 'javascript' | 'python' | 'go' | 'rust' | 'unknown';
13
+ framework: string[];
14
+ packageManager: 'pnpm' | 'npm' | 'yarn' | 'bun' | 'cargo' | 'poetry' | 'uv' | 'pip' | 'unknown';
15
+ testFramework: string[];
16
+ buildTool: string[];
17
+ isMonorepo: boolean;
18
+ monorepoTool: string;
19
+ database: string[];
20
+ hasDocker: boolean;
21
+ hasCi: boolean;
22
+ ciTool: string;
23
+ maxFileLines: number | null;
24
+ srcDir: string;
25
+ testDir: string;
26
+ version: string;
27
+ isPublicNpm: boolean;
28
+ }
29
+ export declare function detectProjectProfile(cwd: string): ProjectProfile;
30
+ export declare function generateSharedInstructions(profile: ProjectProfile): string;
31
+ export interface MemorySeed {
32
+ key: string;
33
+ value: string;
34
+ namespace: string;
35
+ }
36
+ export declare function generateMemorySeeds(profile: ProjectProfile): MemorySeed[];
37
+ export declare function writeSharedInstructions(cwd: string, force: boolean, result: InitResult): void;
38
+ //# sourceMappingURL=shared-instructions-generator.d.ts.map