@mrpatronz/nexusflow 1.3.2 → 1.5.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.
Files changed (143) hide show
  1. package/README.md +50 -0
  2. package/dist/commands/create.d.ts.map +1 -1
  3. package/dist/commands/create.js +54 -37
  4. package/dist/commands/create.js.map +1 -1
  5. package/dist/commands/diff.d.ts.map +1 -1
  6. package/dist/commands/diff.js +16 -73
  7. package/dist/commands/diff.js.map +1 -1
  8. package/dist/commands/doctor.d.ts +4 -0
  9. package/dist/commands/doctor.d.ts.map +1 -1
  10. package/dist/commands/doctor.js +58 -318
  11. package/dist/commands/doctor.js.map +1 -1
  12. package/dist/commands/finish.d.ts +17 -0
  13. package/dist/commands/finish.d.ts.map +1 -0
  14. package/dist/commands/finish.js +172 -0
  15. package/dist/commands/finish.js.map +1 -0
  16. package/dist/commands/handoff.d.ts.map +1 -1
  17. package/dist/commands/handoff.js +11 -6
  18. package/dist/commands/handoff.js.map +1 -1
  19. package/dist/commands/knowledge.d.ts +36 -0
  20. package/dist/commands/knowledge.d.ts.map +1 -0
  21. package/dist/commands/knowledge.js +184 -0
  22. package/dist/commands/knowledge.js.map +1 -0
  23. package/dist/commands/open.d.ts.map +1 -1
  24. package/dist/commands/open.js +2 -4
  25. package/dist/commands/open.js.map +1 -1
  26. package/dist/core/commit.d.ts +41 -0
  27. package/dist/core/commit.d.ts.map +1 -0
  28. package/dist/core/commit.js +53 -0
  29. package/dist/core/commit.js.map +1 -0
  30. package/dist/core/config.d.ts +3 -1
  31. package/dist/core/config.d.ts.map +1 -1
  32. package/dist/core/config.js +60 -32
  33. package/dist/core/config.js.map +1 -1
  34. package/dist/core/config.test.js +24 -0
  35. package/dist/core/config.test.js.map +1 -1
  36. package/dist/core/diff.d.ts +27 -0
  37. package/dist/core/diff.d.ts.map +1 -0
  38. package/dist/core/diff.js +50 -0
  39. package/dist/core/diff.js.map +1 -0
  40. package/dist/core/doctor.d.ts +34 -0
  41. package/dist/core/doctor.d.ts.map +1 -0
  42. package/dist/core/doctor.js +276 -0
  43. package/dist/core/doctor.js.map +1 -0
  44. package/dist/core/finish.d.ts +56 -0
  45. package/dist/core/finish.d.ts.map +1 -0
  46. package/dist/core/finish.js +121 -0
  47. package/dist/core/finish.js.map +1 -0
  48. package/dist/core/finish.test.d.ts +2 -0
  49. package/dist/core/finish.test.d.ts.map +1 -0
  50. package/dist/core/finish.test.js +110 -0
  51. package/dist/core/finish.test.js.map +1 -0
  52. package/dist/core/knowledge.d.ts +96 -0
  53. package/dist/core/knowledge.d.ts.map +1 -0
  54. package/dist/core/knowledge.js +299 -0
  55. package/dist/core/knowledge.js.map +1 -0
  56. package/dist/core/knowledge.test.d.ts +2 -0
  57. package/dist/core/knowledge.test.d.ts.map +1 -0
  58. package/dist/core/knowledge.test.js +157 -0
  59. package/dist/core/knowledge.test.js.map +1 -0
  60. package/dist/core/refresh.test.d.ts +2 -0
  61. package/dist/core/refresh.test.d.ts.map +1 -0
  62. package/dist/core/refresh.test.js +67 -0
  63. package/dist/core/refresh.test.js.map +1 -0
  64. package/dist/core/status.d.ts +45 -0
  65. package/dist/core/status.d.ts.map +1 -0
  66. package/dist/core/status.js +45 -0
  67. package/dist/core/status.js.map +1 -0
  68. package/dist/core/sync.test.d.ts +2 -0
  69. package/dist/core/sync.test.d.ts.map +1 -0
  70. package/dist/core/sync.test.js +86 -0
  71. package/dist/core/sync.test.js.map +1 -0
  72. package/dist/core/workspace.create.test.d.ts +2 -0
  73. package/dist/core/workspace.create.test.d.ts.map +1 -0
  74. package/dist/core/workspace.create.test.js +95 -0
  75. package/dist/core/workspace.create.test.js.map +1 -0
  76. package/dist/core/workspace.d.ts +9 -3
  77. package/dist/core/workspace.d.ts.map +1 -1
  78. package/dist/core/workspace.js +91 -13
  79. package/dist/core/workspace.js.map +1 -1
  80. package/dist/core/worktree.d.ts +11 -1
  81. package/dist/core/worktree.d.ts.map +1 -1
  82. package/dist/core/worktree.js +2 -0
  83. package/dist/core/worktree.js.map +1 -1
  84. package/dist/core/worktree.test.d.ts +2 -0
  85. package/dist/core/worktree.test.d.ts.map +1 -0
  86. package/dist/core/worktree.test.js +111 -0
  87. package/dist/core/worktree.test.js.map +1 -0
  88. package/dist/generators/base.js +1 -1
  89. package/dist/generators/index.d.ts +7 -0
  90. package/dist/generators/index.d.ts.map +1 -1
  91. package/dist/generators/index.js +4 -1
  92. package/dist/generators/index.js.map +1 -1
  93. package/dist/index.js +110 -3
  94. package/dist/index.js.map +1 -1
  95. package/dist/mcp/server.d.ts.map +1 -1
  96. package/dist/mcp/server.js +51 -282
  97. package/dist/mcp/server.js.map +1 -1
  98. package/dist/mcp/tools.d.ts +45 -0
  99. package/dist/mcp/tools.d.ts.map +1 -0
  100. package/dist/mcp/tools.js +413 -0
  101. package/dist/mcp/tools.js.map +1 -0
  102. package/dist/server.d.ts.map +1 -1
  103. package/dist/server.js +43 -26
  104. package/dist/server.js.map +1 -1
  105. package/dist/utils/debug.d.ts +17 -0
  106. package/dist/utils/debug.d.ts.map +1 -0
  107. package/dist/utils/debug.js +25 -0
  108. package/dist/utils/debug.js.map +1 -0
  109. package/dist/utils/detect-ai.d.ts +14 -6
  110. package/dist/utils/detect-ai.d.ts.map +1 -1
  111. package/dist/utils/detect-ai.js +22 -9
  112. package/dist/utils/detect-ai.js.map +1 -1
  113. package/dist/utils/detect-ai.test.js +33 -0
  114. package/dist/utils/detect-ai.test.js.map +1 -1
  115. package/dist/utils/git.d.ts +13 -2
  116. package/dist/utils/git.d.ts.map +1 -1
  117. package/dist/utils/git.js +64 -5
  118. package/dist/utils/git.js.map +1 -1
  119. package/dist/utils/git.test.js +46 -8
  120. package/dist/utils/git.test.js.map +1 -1
  121. package/dist/utils/multi-git.d.ts +40 -0
  122. package/dist/utils/multi-git.d.ts.map +1 -1
  123. package/dist/utils/multi-git.js +80 -0
  124. package/dist/utils/multi-git.js.map +1 -1
  125. package/dist/utils/multi-git.test.js +54 -1
  126. package/dist/utils/multi-git.test.js.map +1 -1
  127. package/dist/utils/open-editor.d.ts +20 -0
  128. package/dist/utils/open-editor.d.ts.map +1 -0
  129. package/dist/utils/open-editor.js +44 -0
  130. package/dist/utils/open-editor.js.map +1 -0
  131. package/dist/utils/pr.d.ts +50 -0
  132. package/dist/utils/pr.d.ts.map +1 -0
  133. package/dist/utils/pr.js +144 -0
  134. package/dist/utils/pr.js.map +1 -0
  135. package/dist/utils/pr.test.d.ts +2 -0
  136. package/dist/utils/pr.test.d.ts.map +1 -0
  137. package/dist/utils/pr.test.js +47 -0
  138. package/dist/utils/pr.test.js.map +1 -0
  139. package/dist/utils/resolve-workspace.d.ts +30 -0
  140. package/dist/utils/resolve-workspace.d.ts.map +1 -0
  141. package/dist/utils/resolve-workspace.js +77 -0
  142. package/dist/utils/resolve-workspace.js.map +1 -0
  143. package/package.json +11 -11
@@ -0,0 +1,413 @@
1
+ /**
2
+ * @module mcp/tools
3
+ * Registry of MCP tools NexusFlow exposes to AI assistants. Each tool declares
4
+ * its schema, MCP annotations, an optional `enabled` predicate, and a handler.
5
+ * `server.ts` lists the enabled tools and dispatches calls to their handlers,
6
+ * so adding a tool means adding one entry here.
7
+ */
8
+ import { execa } from 'execa';
9
+ import * as fs from 'node:fs/promises';
10
+ import * as path from 'node:path';
11
+ import { loadFeatureConfig } from '../core/workspace.js';
12
+ import { syncWorkspace } from '../core/sync.js';
13
+ import { getWorkspaceStatusReport } from '../core/status.js';
14
+ import { getWorkspaceDiffReport } from '../core/diff.js';
15
+ import { commitWorkspace } from '../core/commit.js';
16
+ import { refreshWorkspace } from '../core/refresh.js';
17
+ import { runDoctor } from '../core/doctor.js';
18
+ import { finishWorkspace } from '../core/finish.js';
19
+ import { addWorkspaceKnowledge, addBaseKnowledge, promoteKnowledge, } from '../core/knowledge.js';
20
+ import { callLocalLlm } from '../utils/local-ai.js';
21
+ // ─── Result helpers ─────────────────────────────────────────────────────────
22
+ function text(t) {
23
+ return { content: [{ type: 'text', text: t }] };
24
+ }
25
+ function json(value) {
26
+ return text(JSON.stringify(value, null, 2));
27
+ }
28
+ function errorResult(message) {
29
+ return { content: [{ type: 'text', text: message }], isError: true };
30
+ }
31
+ /** Shared `workspaceId` schema property. */
32
+ const workspaceIdProp = {
33
+ workspaceId: {
34
+ type: 'string',
35
+ description: 'Optional ID/branchName of the workspace. If omitted, uses the currently active workspace.',
36
+ },
37
+ };
38
+ async function requireWorkspace(ctx) {
39
+ const feature = await loadFeatureConfig(ctx.workspacePath);
40
+ if (!feature) {
41
+ throw new Error(`Workspace not found at ${ctx.workspacePath}. Make sure you are in a NexusFlow workspace or provide a valid workspaceId.`);
42
+ }
43
+ }
44
+ // ─── Tools ──────────────────────────────────────────────────────────────────
45
+ export const tools = [
46
+ {
47
+ name: 'search_workspace',
48
+ description: 'Search for a string or regex across all repositories in the NexusFlow workspace. Extremely fast and useful for finding where a specific variable, function, or concept is used across microservices.',
49
+ annotations: { readOnlyHint: true },
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: {
53
+ query: { type: 'string', description: 'The search query or regular expression' },
54
+ ...workspaceIdProp,
55
+ },
56
+ required: ['query'],
57
+ },
58
+ handler: async (args, ctx) => {
59
+ const query = args.query;
60
+ try {
61
+ const feature = await loadFeatureConfig(ctx.workspacePath);
62
+ if (!feature) {
63
+ throw new Error(`Workspace not found at ${ctx.workspacePath}. Make sure you are in a NexusFlow workspace or provide a valid workspaceId.`);
64
+ }
65
+ let allResults = '';
66
+ for (const repoPath of feature.repos) {
67
+ const repoName = path.basename(repoPath);
68
+ const worktreePath = path.join(ctx.workspacePath, repoName);
69
+ try {
70
+ await fs.access(worktreePath);
71
+ const { stdout } = await execa('git', ['grep', '-n', '-I', query], { cwd: worktreePath, reject: false });
72
+ if (stdout) {
73
+ allResults += stdout.split('\n').map((line) => `[${repoName}] ${line}`).join('\n') + '\n';
74
+ }
75
+ }
76
+ catch {
77
+ // No match or repo missing — skip.
78
+ }
79
+ }
80
+ return text(allResults || 'No results found across the workspace repositories.');
81
+ }
82
+ catch (error) {
83
+ return errorResult(`Error searching workspace: ${error.message}`);
84
+ }
85
+ },
86
+ },
87
+ {
88
+ name: 'workspace_status',
89
+ description: 'Report the git status of every repo in the workspace: current branch, whether it matches the feature branch, dirty files, commits ahead/behind origin, and remote URL. Read-only; use this before committing or finishing.',
90
+ annotations: { readOnlyHint: true },
91
+ inputSchema: { type: 'object', properties: { ...workspaceIdProp } },
92
+ handler: async (_args, ctx) => {
93
+ try {
94
+ await requireWorkspace(ctx);
95
+ return json(await getWorkspaceStatusReport(ctx.workspacePath));
96
+ }
97
+ catch (error) {
98
+ return errorResult(`Error getting workspace status: ${error.message}`);
99
+ }
100
+ },
101
+ },
102
+ {
103
+ name: 'get_workspace_diff',
104
+ description: 'Summarize uncommitted changes and unpushed commits across the workspace repos (files changed, insertions/deletions, commits ahead of origin). Read-only.',
105
+ annotations: { readOnlyHint: true },
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ repos: { type: 'array', items: { type: 'string' }, description: 'Optional list of repo names to restrict the diff to.' },
110
+ ...workspaceIdProp,
111
+ },
112
+ },
113
+ handler: async (args, ctx) => {
114
+ try {
115
+ await requireWorkspace(ctx);
116
+ const repos = Array.isArray(args.repos) ? args.repos : undefined;
117
+ return json(await getWorkspaceDiffReport(ctx.workspacePath, repos));
118
+ }
119
+ catch (error) {
120
+ return errorResult(`Error getting workspace diff: ${error.message}`);
121
+ }
122
+ },
123
+ },
124
+ {
125
+ name: 'commit_workspace',
126
+ description: 'Stage, commit, and (unless noPush) push all repos that have changes, using one commit message. Returns a per-repo report. This writes to git history and pushes to the remote.',
127
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
128
+ inputSchema: {
129
+ type: 'object',
130
+ properties: {
131
+ message: { type: 'string', description: 'Commit message applied to every changed repo.' },
132
+ noPush: { type: 'boolean', description: 'Commit but do not push. Default false.' },
133
+ repos: { type: 'array', items: { type: 'string' }, description: 'Optional list of repo names to restrict the commit to.' },
134
+ ...workspaceIdProp,
135
+ },
136
+ required: ['message'],
137
+ },
138
+ handler: async (args, ctx) => {
139
+ try {
140
+ await requireWorkspace(ctx);
141
+ const message = String(args.message ?? '').trim();
142
+ if (!message)
143
+ return errorResult('A commit message is required.');
144
+ const repos = Array.isArray(args.repos) ? args.repos : undefined;
145
+ return json(await commitWorkspace(ctx.workspacePath, message, { noPush: Boolean(args.noPush), repos }));
146
+ }
147
+ catch (error) {
148
+ return errorResult(`Error committing workspace: ${error.message}`);
149
+ }
150
+ },
151
+ },
152
+ {
153
+ name: 'sync_workspace',
154
+ description: 'Rebase every repository in the NexusFlow workspace onto its base branch. Safe to call non-interactively: dirty working trees are auto-stashed and restored, so a dirty tree is never mis-reported as a conflict. Returns structured per-repo results (status: up-to-date | rebased | conflict | stash-conflict | error) and records them to the workspace state file.',
155
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
156
+ inputSchema: { type: 'object', properties: { ...workspaceIdProp } },
157
+ handler: async (_args, ctx) => {
158
+ try {
159
+ await requireWorkspace(ctx);
160
+ return json(await syncWorkspace(ctx.workspacePath));
161
+ }
162
+ catch (error) {
163
+ return errorResult(`Error syncing workspace: ${error.message}`);
164
+ }
165
+ },
166
+ },
167
+ {
168
+ name: 'refresh_context',
169
+ description: 'Regenerate the workspace context files, architecture maps, and plan. Only re-analyzes repos whose content changed (unchanged maps stay byte-identical to preserve prompt caches). Run this after code changes so the AI context stays current.',
170
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
171
+ inputSchema: {
172
+ type: 'object',
173
+ properties: {
174
+ repo: { type: 'string', description: 'Only refresh a single repo (by directory name).' },
175
+ baseOnly: { type: 'boolean', description: 'Only refresh base-layer maps and knowledge.' },
176
+ force: { type: 'boolean', description: 'Ignore the analysis cache and re-analyze every repo.' },
177
+ ...workspaceIdProp,
178
+ },
179
+ },
180
+ handler: async (args, ctx) => {
181
+ try {
182
+ await requireWorkspace(ctx);
183
+ return json(await refreshWorkspace(ctx.workspacePath, {
184
+ onlyRepo: args.repo ? String(args.repo) : undefined,
185
+ baseOnly: Boolean(args.baseOnly),
186
+ force: Boolean(args.force),
187
+ }));
188
+ }
189
+ catch (error) {
190
+ return errorResult(`Error refreshing context: ${error.message}`);
191
+ }
192
+ },
193
+ },
194
+ {
195
+ name: 'run_doctor',
196
+ description: 'Run workspace health diagnostics: worktree paths, branch alignment, uncommitted changes, local package setup, test commands, core artifacts, and local-AI connectivity. Returns a structured report. Read-only.',
197
+ annotations: { readOnlyHint: true },
198
+ inputSchema: { type: 'object', properties: { ...workspaceIdProp } },
199
+ handler: async (_args, ctx) => {
200
+ try {
201
+ await requireWorkspace(ctx);
202
+ return json(await runDoctor(ctx.workspacePath));
203
+ }
204
+ catch (error) {
205
+ return errorResult(`Error running doctor: ${error.message}`);
206
+ }
207
+ },
208
+ },
209
+ {
210
+ name: 'add_knowledge',
211
+ description: 'Record a learning in the workspace knowledge file (or a repo base file with `repo`). Use this whenever you make an architecture decision, discover a gotcha, complete a milestone, or note an assumption — it is the preferred way to persist learnings instead of editing nexusflow-knowledge.md directly.',
212
+ annotations: { readOnlyHint: false, destructiveHint: false },
213
+ inputSchema: {
214
+ type: 'object',
215
+ properties: {
216
+ type: { type: 'string', enum: ['decision', 'gotcha', 'progress', 'assumption', 'question'], description: 'The kind of learning.' },
217
+ message: { type: 'string', description: 'The learning to record.' },
218
+ title: { type: 'string', description: 'Short title (used for decision headings).' },
219
+ repo: { type: 'string', description: "Write to this repo's persistent base knowledge instead of the workspace file (decision/gotcha/assumption only)." },
220
+ ...workspaceIdProp,
221
+ },
222
+ required: ['type', 'message'],
223
+ },
224
+ handler: async (args, ctx) => {
225
+ try {
226
+ await requireWorkspace(ctx);
227
+ const type = args.type;
228
+ const message = String(args.message ?? '').trim();
229
+ if (!message)
230
+ return errorResult('A knowledge message is required.');
231
+ const entry = { type, message, title: args.title ? String(args.title) : undefined };
232
+ const result = args.repo
233
+ ? await addBaseKnowledge(ctx.workspacePath, String(args.repo), entry)
234
+ : await addWorkspaceKnowledge(ctx.workspacePath, entry);
235
+ return json(result);
236
+ }
237
+ catch (error) {
238
+ return errorResult(`Error adding knowledge: ${error.message}`);
239
+ }
240
+ },
241
+ },
242
+ {
243
+ name: 'promote_knowledge',
244
+ description: "Promote a reusable learning into a repo's persistent base knowledge so it survives across features. Provide the repo, the entry type (decision | gotcha | assumption), and the text.",
245
+ annotations: { readOnlyHint: false, destructiveHint: false },
246
+ inputSchema: {
247
+ type: 'object',
248
+ properties: {
249
+ repo: { type: 'string', description: 'Target repository (by directory name).' },
250
+ type: { type: 'string', enum: ['decision', 'gotcha', 'assumption'], description: 'The kind of learning to promote.' },
251
+ text: { type: 'string', description: 'The learning text to store in base knowledge.' },
252
+ move: { type: 'boolean', description: 'Reserved; base promotion is additive. Default false.' },
253
+ ...workspaceIdProp,
254
+ },
255
+ required: ['repo', 'type', 'text'],
256
+ },
257
+ handler: async (args, ctx) => {
258
+ try {
259
+ await requireWorkspace(ctx);
260
+ const type = args.type;
261
+ const textValue = String(args.text ?? '').trim();
262
+ if (!textValue)
263
+ return errorResult('The learning text is required.');
264
+ // Store the supplied text verbatim under the base section for its type.
265
+ const entry = { section: '', type, text: textValue };
266
+ const result = await promoteKnowledge(ctx.workspacePath, {
267
+ repoName: String(args.repo),
268
+ entries: [entry],
269
+ mode: 'copy',
270
+ });
271
+ return json(result);
272
+ }
273
+ catch (error) {
274
+ return errorResult(`Error promoting knowledge: ${error.message}`);
275
+ }
276
+ },
277
+ },
278
+ {
279
+ name: 'finish_workspace',
280
+ description: 'Finish the feature: commit any remaining changes (with the given message), push every repo, and return per-repo PR/compare links. Does NOT delete anything — to remove the workspace, the user runs `nexusflow finish --cleanup` from outside it.',
281
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
282
+ inputSchema: {
283
+ type: 'object',
284
+ properties: {
285
+ message: { type: 'string', description: 'Commit message for any remaining changes.' },
286
+ skipPush: { type: 'boolean', description: 'Commit but do not push. Default false.' },
287
+ ...workspaceIdProp,
288
+ },
289
+ },
290
+ handler: async (args, ctx) => {
291
+ try {
292
+ await requireWorkspace(ctx);
293
+ const report = await finishWorkspace(ctx.workspacePath, {
294
+ message: args.message ? String(args.message) : undefined,
295
+ skipPush: Boolean(args.skipPush),
296
+ createPrs: true,
297
+ });
298
+ // MCP never deletes worktrees (an agent's CWD is usually inside the
299
+ // workspace). Point the user at the CLI for cleanup instead.
300
+ return json({
301
+ ...report,
302
+ note: report.safeToCleanup
303
+ ? 'Workspace is fully pushed. To remove it, run `nexusflow finish --cleanup` from outside the workspace.'
304
+ : 'Some repos are still dirty or unpushed — see the per-repo report.',
305
+ });
306
+ }
307
+ catch (error) {
308
+ return errorResult(`Error finishing workspace: ${error.message}`);
309
+ }
310
+ },
311
+ },
312
+ {
313
+ name: 'get_service_logs',
314
+ description: 'Get the recent logs for a specific running service in the workspace to debug runtime issues.',
315
+ annotations: { readOnlyHint: true },
316
+ inputSchema: {
317
+ type: 'object',
318
+ properties: {
319
+ serviceName: { type: 'string', description: 'The name of the service to fetch logs for (e.g. "frontend", "backend-api")' },
320
+ lines: { type: 'number', description: 'Number of lines to fetch. Default 50.' },
321
+ ...workspaceIdProp,
322
+ },
323
+ required: ['serviceName'],
324
+ },
325
+ handler: async (args, ctx) => {
326
+ const serviceName = args.serviceName;
327
+ const lines = args.lines || 50;
328
+ try {
329
+ const logFilePath = path.join(ctx.workspacePath, '.nexusflow-logs', `${serviceName}.log`);
330
+ try {
331
+ await fs.access(logFilePath);
332
+ }
333
+ catch {
334
+ return errorResult(`Log file for service "${serviceName}" not found. Ensure the service is running via "nexusflow start".`);
335
+ }
336
+ const content = await fs.readFile(logFilePath, 'utf8');
337
+ const tail = content.split('\n').slice(-lines).join('\n');
338
+ return text(tail || '(empty log)');
339
+ }
340
+ catch (error) {
341
+ return errorResult(`Error reading logs: ${error.message}`);
342
+ }
343
+ },
344
+ },
345
+ {
346
+ name: 'delegate_to_local_agent',
347
+ description: "Delegate simple, high-volume, or repetitive tasks (like searching code, parsing raw service logs, or generating boilerplate) to the local Small Language Model (SLM) running on the user's machine to save remote tokens.",
348
+ enabled: (config) => Boolean(config.localLlm?.enabled),
349
+ annotations: { readOnlyHint: true },
350
+ inputSchema: {
351
+ type: 'object',
352
+ properties: {
353
+ instruction: { type: 'string', description: 'The specific instruction for the local agent.' },
354
+ filesToRead: { type: 'array', items: { type: 'string' }, description: 'Optional workspace-relative file paths the local agent should read as context.' },
355
+ },
356
+ required: ['instruction'],
357
+ },
358
+ handler: async (args, ctx) => {
359
+ const instruction = args.instruction;
360
+ const filesToRead = args.filesToRead || [];
361
+ try {
362
+ if (!ctx.config.localLlm || !ctx.config.localLlm.enabled) {
363
+ throw new Error('Local AI agent delegation is disabled. Enable it in ~/.nexusflow/config.json or settings.');
364
+ }
365
+ const contextFiles = [];
366
+ const normalizedWorkspace = ctx.workspacePath.endsWith(path.sep) ? ctx.workspacePath : ctx.workspacePath + path.sep;
367
+ for (const relativePath of filesToRead) {
368
+ const filePath = path.resolve(ctx.workspacePath, relativePath);
369
+ if (!filePath.startsWith(normalizedWorkspace)) {
370
+ contextFiles.push({ path: relativePath, content: '[Access denied: path is outside workspace boundary]' });
371
+ continue;
372
+ }
373
+ try {
374
+ const content = await fs.readFile(filePath, 'utf-8');
375
+ const truncatedContent = content.length > 50000
376
+ ? content.substring(0, 50000) + '\n\n[Content truncated by NexusFlow to save local context size...]'
377
+ : content;
378
+ contextFiles.push({ path: relativePath, content: truncatedContent });
379
+ }
380
+ catch (e) {
381
+ contextFiles.push({ path: relativePath, content: `[Error reading file: ${e.message || 'unknown error'}]` });
382
+ }
383
+ }
384
+ const messages = [];
385
+ let systemPrompt = 'You are the NexusFlow local assistant. You are running locally on the user\'s machine to help the remote supervisor agent solve a specific sub-task in a multi-repo workspace.\n';
386
+ systemPrompt += 'Your goal is to be extremely precise, concise, and return only the distilled findings/code to save remote network tokens. Keep your response short, focused, and directly address the instruction.';
387
+ messages.push({ role: 'system', content: systemPrompt });
388
+ let userPrompt = '';
389
+ if (contextFiles.length > 0) {
390
+ userPrompt += 'Here is the local file/log context:\n\n';
391
+ for (const file of contextFiles) {
392
+ userPrompt += `--- FILE: ${file.path} ---\n${file.content}\n\n`;
393
+ }
394
+ }
395
+ userPrompt += `Instruction: ${instruction}`;
396
+ messages.push({ role: 'user', content: userPrompt });
397
+ return text(await callLocalLlm(ctx.config.localLlm, messages));
398
+ }
399
+ catch (error) {
400
+ return errorResult(`Error delegating to local agent: ${error.message}`);
401
+ }
402
+ },
403
+ },
404
+ ];
405
+ /** Returns the tools enabled for the given config. */
406
+ export function enabledTools(config) {
407
+ return tools.filter((t) => !t.enabled || t.enabled(config));
408
+ }
409
+ /** Finds a tool by name (regardless of enabled state). */
410
+ export function findTool(name) {
411
+ return tools.find((t) => t.name === name);
412
+ }
413
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA4D5B,eAAO,MAAM,GAAG,4EAAa,CAAC;AAW9B,yEAAyE;AACzE,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,SAA2B;CAI/C;AAcD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/E;AAWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAc7D;AAw5CD,wBAAgB,WAAW,CACzB,IAAI,SAAO,EACX,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAwBxC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAoE5B,eAAO,MAAM,GAAG,4EAAa,CAAC;AAW9B,yEAAyE;AACzE,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,SAA2B;CAI/C;AAcD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/E;AAWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAc7D;AA46CD,wBAAgB,WAAW,CACzB,IAAI,SAAO,EACX,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAwBxC"}
package/dist/server.js CHANGED
@@ -27,9 +27,12 @@ import { detectAIAssistants } from './utils/detect-ai.js';
27
27
  import { detectEditors } from './utils/detect-editors.js';
28
28
  import { findSessions, getSessionTranscript } from './utils/session-finder.js';
29
29
  import { scanSystemSpecs } from './utils/system-scanner.js';
30
- import { getWorkspaceRepos, commitAndPush, getRepoStatus } from './utils/multi-git.js';
30
+ import { getRepoStatus } from './utils/multi-git.js';
31
31
  import { syncWorkspace } from './core/sync.js';
32
+ import { commitWorkspace } from './core/commit.js';
32
33
  import { refreshWorkspace } from './core/refresh.js';
34
+ import { writeWorkspaceFile } from './core/storage.js';
35
+ import { readWorkspaceKnowledge, addWorkspaceKnowledge, addBaseKnowledge, } from './core/knowledge.js';
33
36
  import { addSchedule, loadSchedules, nextDueAt, parseInterval, removeSchedule, runJob, setScheduleEnabled, startScheduler, } from './core/scheduler.js';
34
37
  import { detectAllServices, detectOrchestrationTools, startServices, stopServices, loadRunningState, getPm2List, } from './orchestration/index.js';
35
38
  import { checkForUpdates, getCurrentVersion, getToolsStatus } from './utils/update-check.js';
@@ -849,19 +852,15 @@ app.get('/api/workspace/:id/changes/diff', async (c) => {
849
852
  }
850
853
  });
851
854
  // 13a. Get workspace knowledge (nexusflow-knowledge.md)
855
+ // Routed through the active storage adapter so the GUI edits the same file the
856
+ // generators write (under the Obsidian/vault adapters this lives outside the
857
+ // workspace directory).
852
858
  app.get('/api/workspace/:id/knowledge', async (c) => {
853
859
  try {
854
860
  const id = c.req.param('id');
855
861
  const config = await loadConfig();
856
862
  const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
857
- const knowledgeFile = path.join(workspacePath, 'nexusflow-knowledge.md');
858
- let content = '';
859
- try {
860
- content = await fs.readFile(knowledgeFile, 'utf-8');
861
- }
862
- catch {
863
- content = '# Workspace Knowledge\n\nNo knowledge file yet.';
864
- }
863
+ const content = (await readWorkspaceKnowledge(workspacePath)) ?? '# Workspace Knowledge\n\nNo knowledge file yet.';
865
864
  return c.json({ content });
866
865
  }
867
866
  catch (error) {
@@ -875,14 +874,39 @@ app.put('/api/workspace/:id/knowledge', async (c) => {
875
874
  const { content } = await c.req.json();
876
875
  const config = await loadConfig();
877
876
  const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
878
- const knowledgeFile = path.join(workspacePath, 'nexusflow-knowledge.md');
879
- await fs.writeFile(knowledgeFile, content, 'utf-8');
877
+ const feature = await loadFeatureConfig(workspacePath);
878
+ const featureId = feature?.id ?? path.basename(workspacePath);
879
+ await writeWorkspaceFile(workspacePath, featureId, 'nexusflow-knowledge.md', content);
880
880
  return c.json({ success: true });
881
881
  }
882
882
  catch (error) {
883
883
  return errorResponse(c, error);
884
884
  }
885
885
  });
886
+ // 13b-2. Append a single structured learning (used for a GUI quick-add).
887
+ app.post('/api/workspace/:id/knowledge/entry', async (c) => {
888
+ try {
889
+ const id = c.req.param('id');
890
+ const body = (await c.req.json());
891
+ const config = await loadConfig();
892
+ const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
893
+ const result = body.repo
894
+ ? await addBaseKnowledge(workspacePath, body.repo, {
895
+ type: body.type,
896
+ message: body.message,
897
+ title: body.title,
898
+ })
899
+ : await addWorkspaceKnowledge(workspacePath, {
900
+ type: body.type,
901
+ message: body.message,
902
+ title: body.title,
903
+ });
904
+ return c.json({ success: true, ...result });
905
+ }
906
+ catch (error) {
907
+ return errorResponse(c, error);
908
+ }
909
+ });
886
910
  // 13c. Get workspace plan (nexusflow-plan.md)
887
911
  app.get('/api/workspace/:id/plan', async (c) => {
888
912
  try {
@@ -935,21 +959,14 @@ app.post('/api/workspace/:id/commit', async (c) => {
935
959
  const { message } = await c.req.json();
936
960
  const config = await loadConfig();
937
961
  const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
938
- const repos = await getWorkspaceRepos(workspacePath);
939
- const results = [];
940
- for (const repo of repos) {
941
- const status = await getRepoStatus(repo.path);
942
- if (status.hasChanges) {
943
- const result = await commitAndPush(repo.path, message, repo.branchName);
944
- results.push({
945
- repoName: repo.name,
946
- success: result.success,
947
- commitHash: result.commitHash,
948
- filesChanged: result.filesChanged,
949
- message: result.message,
950
- });
951
- }
952
- }
962
+ const report = await commitWorkspace(workspacePath, message);
963
+ const results = report.repos.map((repo) => ({
964
+ repoName: repo.name,
965
+ success: repo.success,
966
+ commitHash: repo.commitHash,
967
+ filesChanged: repo.filesChanged,
968
+ message: repo.message,
969
+ }));
953
970
  return c.json({ results });
954
971
  }
955
972
  catch (error) {