morpheus-cli 0.8.9 → 0.9.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.
@@ -32,12 +32,15 @@ vi.mock('../display.js', () => ({
32
32
  })),
33
33
  },
34
34
  }));
35
- vi.mock('../../devkit/index.js', () => ({
35
+ vi.mock('morpheus-devkit', () => ({
36
36
  buildDevKit: vi.fn(() => [
37
37
  { name: 'fs_read', description: 'Read file' },
38
38
  { name: 'shell_exec', description: 'Execute shell command' },
39
39
  ]),
40
40
  }));
41
+ vi.mock('../devkit-instrument.js', () => ({
42
+ instrumentDevKitTools: vi.fn((tools) => tools),
43
+ }));
41
44
  vi.mock('../tools/factory.js', () => ({
42
45
  Construtor: {
43
46
  create: vi.fn(() => Promise.resolve([
@@ -97,7 +100,7 @@ describe('Keymaker', () => {
97
100
  describe('initialize()', () => {
98
101
  it('should initialize agent with all tools', async () => {
99
102
  const { ProviderFactory } = await import('../providers/factory.js');
100
- const { buildDevKit } = await import('../../devkit/index.js');
103
+ const { buildDevKit } = await import('morpheus-devkit');
101
104
  const { Construtor } = await import('../tools/factory.js');
102
105
  const keymaker = new Keymaker('test-skill', '# Instructions');
103
106
  await keymaker.initialize();
@@ -3,7 +3,8 @@ import { ConfigManager } from "../config/manager.js";
3
3
  import { ProviderFactory } from "./providers/factory.js";
4
4
  import { ProviderError } from "./errors.js";
5
5
  import { DisplayManager } from "./display.js";
6
- import { buildDevKit } from "../devkit/index.js";
6
+ import { buildDevKit } from "morpheus-devkit";
7
+ import { instrumentDevKitTools } from "./devkit-instrument.js";
7
8
  import { extractRawUsage, persistAgentMessage, buildAgentResult, emitToolAuditEvents } from "./subagent-utils.js";
8
9
  import { buildDelegationTool } from "./tools/delegation-utils.js";
9
10
  /**
@@ -47,9 +48,9 @@ export class Apoc {
47
48
  const devkit = ConfigManager.getInstance().getDevKitConfig();
48
49
  const timeout_ms = devkit.timeout_ms || this.config.apoc?.timeout_ms || 30_000;
49
50
  const personality = this.config.apoc?.personality || 'pragmatic_dev';
50
- // Import all devkit tool factories (side-effect registration)
51
- await import("../devkit/index.js");
52
- const tools = buildDevKit({
51
+ // Import morpheus-devkit to trigger side-effect tool registration
52
+ await import("morpheus-devkit");
53
+ const rawTools = buildDevKit({
53
54
  working_dir: devkit.sandbox_dir || process.cwd(),
54
55
  allowed_commands: devkit.allowed_shell_commands || [],
55
56
  timeout_ms,
@@ -59,9 +60,8 @@ export class Apoc {
59
60
  enable_shell: devkit.enable_shell,
60
61
  enable_git: devkit.enable_git,
61
62
  enable_network: devkit.enable_network,
62
- getSessionId: () => Apoc.currentSessionId,
63
- getAgent: () => 'apoc',
64
63
  });
64
+ const tools = instrumentDevKitTools(rawTools, () => Apoc.currentSessionId, () => 'apoc');
65
65
  this.display.log(`Apoc initialized with ${tools.length} DevKit tools (sandbox_dir: ${devkit.sandbox_dir}, personality: ${personality})`, { source: "Apoc" });
66
66
  try {
67
67
  this.agent = await ProviderFactory.createBare(apocConfig, tools);
@@ -1,15 +1,4 @@
1
- import { AuditRepository } from '../runtime/audit/repository.js';
2
- const factories = [];
3
- export function registerToolFactory(factory, category = 'system') {
4
- factories.push({ category, factory });
5
- }
6
- /** Categories that can be toggled off via DevKit config */
7
- const TOGGLEABLE_CATEGORIES = {
8
- filesystem: 'enable_filesystem',
9
- shell: 'enable_shell',
10
- git: 'enable_git',
11
- network: 'enable_network',
12
- };
1
+ import { AuditRepository } from './audit/repository.js';
13
2
  /**
14
3
  * Wraps a StructuredTool to record audit events on each invocation.
15
4
  * The `getSessionId` getter is called at invocation time so it reflects
@@ -51,22 +40,9 @@ function instrumentTool(tool, getSessionId, getAgent) {
51
40
  return tool;
52
41
  }
53
42
  /**
54
- * Builds the full DevKit tool set for a given context.
55
- * Each factory receives the context (working_dir, allowed_commands, etc.)
56
- * and returns tools with the context captured in closure.
57
- * Disabled categories are filtered out based on context flags.
58
- * All tools are wrapped with audit instrumentation.
43
+ * Wraps all DevKit tools with audit instrumentation.
44
+ * Call this after buildDevKit() to add Morpheus audit tracking.
59
45
  */
60
- export function buildDevKit(ctx) {
61
- const getSessionId = ctx.getSessionId ?? (() => undefined);
62
- const getAgent = ctx.getAgent ?? (() => 'apoc');
63
- return factories
64
- .filter(({ category }) => {
65
- const ctxKey = TOGGLEABLE_CATEGORIES[category];
66
- if (!ctxKey)
67
- return true; // non-toggleable categories always load
68
- return ctx[ctxKey] !== false;
69
- })
70
- .flatMap(({ factory }) => factory(ctx))
71
- .map(tool => instrumentTool(tool, getSessionId, getAgent));
46
+ export function instrumentDevKitTools(tools, getSessionId, getAgent) {
47
+ return tools.map(tool => instrumentTool(tool, getSessionId, getAgent));
72
48
  }
@@ -3,7 +3,8 @@ import { ConfigManager } from "../config/manager.js";
3
3
  import { ProviderFactory } from "./providers/factory.js";
4
4
  import { ProviderError } from "./errors.js";
5
5
  import { DisplayManager } from "./display.js";
6
- import { buildDevKit } from "../devkit/index.js";
6
+ import { buildDevKit } from "morpheus-devkit";
7
+ import { instrumentDevKitTools } from "./devkit-instrument.js";
7
8
  import { Construtor } from "./tools/factory.js";
8
9
  import { morpheusTools } from "./tools/index.js";
9
10
  import { SkillRegistry } from "./skills/registry.js";
@@ -36,8 +37,8 @@ export class Keymaker {
36
37
  // Build DevKit tools (filesystem, shell, git, browser, network, etc.)
37
38
  const devkit = ConfigManager.getInstance().getDevKitConfig();
38
39
  const timeout_ms = devkit.timeout_ms || 30_000;
39
- await import("../devkit/index.js");
40
- const devKitTools = buildDevKit({
40
+ await import("morpheus-devkit");
41
+ const devKitTools = instrumentDevKitTools(buildDevKit({
41
42
  working_dir: devkit.sandbox_dir || process.cwd(),
42
43
  allowed_commands: devkit.allowed_shell_commands || [],
43
44
  timeout_ms,
@@ -47,7 +48,7 @@ export class Keymaker {
47
48
  enable_shell: devkit.enable_shell,
48
49
  enable_git: devkit.enable_git,
49
50
  enable_network: devkit.enable_network,
50
- });
51
+ }), () => undefined, () => 'keymaker');
51
52
  // Load MCP tools from configured servers
52
53
  const mcpTools = await Construtor.create();
53
54
  // Combine all tools
@@ -5,7 +5,7 @@ import { DisplayManager } from '../display.js';
5
5
  import { SmithRegistry } from './registry.js';
6
6
  import { ConfigManager } from '../../config/manager.js';
7
7
  import { ProviderFactory } from '../providers/factory.js';
8
- import { buildDevKit } from '../../devkit/index.js';
8
+ import { buildDevKit } from 'morpheus-devkit';
9
9
  import { SQLiteChatMessageHistory } from '../memory/sqlite.js';
10
10
  import { AuditRepository } from '../audit/repository.js';
11
11
  import { TaskRequestContext } from '../tasks/context.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morpheus-cli",
3
- "version": "0.8.9",
3
+ "version": "0.9.0",
4
4
  "description": "Morpheus is a local AI agent for developers, running as a CLI daemon that connects to LLMs, local tools, and MCPs, enabling interaction via Terminal, Telegram, and Discord. Inspired by the character Morpheus from *The Matrix*, the project acts as an intelligent orchestrator, bridging the gap between the developer and complex systems.",
5
5
  "bin": {
6
6
  "morpheus": "./bin/morpheus.js"
@@ -61,6 +61,7 @@
61
61
  "mcp-remote": "^0.1.38",
62
62
  "mongodb": "^6.21.0",
63
63
  "mongoose": "^8.23.0",
64
+ "morpheus-devkit": "^1.0.0",
64
65
  "multer": "^2.0.2",
65
66
  "music-metadata": "^11.12.1",
66
67
  "mysql2": "^3.17.4",
@@ -1,80 +0,0 @@
1
- import { platform } from 'os';
2
- export class ShellAdapter {
3
- /**
4
- * Factory: returns the appropriate adapter for the current OS.
5
- * Uses direct imports (ESM-compatible, no require()).
6
- */
7
- static create() {
8
- switch (platform()) {
9
- case 'win32': return new WindowsAdapter();
10
- case 'darwin': return new MacAdapter();
11
- default: return new LinuxAdapter();
12
- }
13
- }
14
- }
15
- // ─── Inline implementations (avoids ESM dynamic import issues) ────────────────
16
- import { spawn } from 'child_process';
17
- class WindowsAdapter extends ShellAdapter {
18
- getShell() { return { shell: 'cmd.exe', flag: '/c' }; }
19
- async run(command, args, options) {
20
- return spawnCommand(command, args, { ...options, windowsHide: true, shell: true });
21
- }
22
- async which(binary) {
23
- const result = await this.run('where', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
24
- if (result.exitCode !== 0)
25
- return null;
26
- const first = result.stdout.trim().split(/\r?\n/)[0];
27
- return first || null;
28
- }
29
- }
30
- class LinuxAdapter extends ShellAdapter {
31
- getShell() { return { shell: '/bin/bash', flag: '-c' }; }
32
- async run(command, args, options) {
33
- return spawnCommand(command, args, { ...options, shell: false });
34
- }
35
- async which(binary) {
36
- const result = await this.run('which', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
37
- if (result.exitCode !== 0)
38
- return null;
39
- return result.stdout.trim() || null;
40
- }
41
- }
42
- class MacAdapter extends ShellAdapter {
43
- getShell() { return { shell: '/bin/zsh', flag: '-c' }; }
44
- async run(command, args, options) {
45
- return spawnCommand(command, args, { ...options, shell: false });
46
- }
47
- async which(binary) {
48
- const result = await this.run('which', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
49
- if (result.exitCode !== 0)
50
- return null;
51
- return result.stdout.trim() || null;
52
- }
53
- }
54
- function spawnCommand(command, args, options) {
55
- return new Promise((resolve) => {
56
- let stdout = '';
57
- let stderr = '';
58
- let timedOut = false;
59
- const child = spawn(command, args, {
60
- cwd: options.cwd,
61
- env: { ...process.env, ...options.env },
62
- shell: options.shell ?? false,
63
- windowsHide: options.windowsHide ?? false,
64
- });
65
- const timer = setTimeout(() => {
66
- timedOut = true;
67
- child.kill('SIGKILL');
68
- }, options.timeout_ms);
69
- child.stdout?.on('data', (d) => { stdout += d.toString(); });
70
- child.stderr?.on('data', (d) => { stderr += d.toString(); });
71
- child.on('error', (err) => {
72
- clearTimeout(timer);
73
- resolve({ exitCode: 1, stdout, stderr: stderr + err.message, timedOut });
74
- });
75
- child.on('close', (code) => {
76
- clearTimeout(timer);
77
- resolve({ exitCode: code ?? 1, stdout, stderr, timedOut });
78
- });
79
- });
80
- }
@@ -1,11 +0,0 @@
1
- // Register all DevKit tool factories
2
- // Import order matters: each import triggers registerToolFactory() as a side effect
3
- import './tools/filesystem.js';
4
- import './tools/shell.js';
5
- import './tools/processes.js';
6
- import './tools/network.js';
7
- import './tools/git.js';
8
- import './tools/packages.js';
9
- import './tools/system.js';
10
- import './tools/browser.js';
11
- export { buildDevKit } from './registry.js';