archgraph-argo 0.9.6 → 0.9.7
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.
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
getWorkspaceRoot,
|
|
19
19
|
hasStaticWorkspace,
|
|
20
20
|
resolveArgoPath,
|
|
21
|
+
resolveCallWorkspaceRoot,
|
|
21
22
|
setMcpWorkspaceRoots,
|
|
22
23
|
} = require('./argo-paths.js');
|
|
23
24
|
const canonicalSemanticInitStorage = new AsyncLocalStorage();
|
|
@@ -304,6 +305,25 @@ const TOOLS = [
|
|
|
304
305
|
},
|
|
305
306
|
];
|
|
306
307
|
|
|
308
|
+
// Every tool accepts an optional per-call `workspaceRoot` (absolute path,
|
|
309
|
+
// honored only when listed in ARGO_WORKSPACE_ROOTS). The DeepSeek Harness
|
|
310
|
+
// bridge injects the current session's workspace directory here automatically.
|
|
311
|
+
// (tools/list dedupes with systemArchitectureMcp/validatorMcp tools, which
|
|
312
|
+
// carry the same field; this covers the local-only rows like initializeWorkspace.)
|
|
313
|
+
const WORKSPACE_ROOT_PARAM = Object.freeze({
|
|
314
|
+
type: 'string',
|
|
315
|
+
description:
|
|
316
|
+
'Optional absolute workspace root for this call. Honored only when listed in ARGO_WORKSPACE_ROOTS; otherwise the server launch directory is used.',
|
|
317
|
+
});
|
|
318
|
+
for (const tool of TOOLS) {
|
|
319
|
+
const inputSchema = tool && tool.inputSchema;
|
|
320
|
+
if (inputSchema && inputSchema.type === 'object' && inputSchema.properties) {
|
|
321
|
+
if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, 'workspaceRoot')) {
|
|
322
|
+
inputSchema.properties.workspaceRoot = WORKSPACE_ROOT_PARAM;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
307
327
|
function intentElementContextInputSchema() {
|
|
308
328
|
return {
|
|
309
329
|
type: 'object',
|
|
@@ -374,14 +394,20 @@ function resolveWorkspaceRoot() {
|
|
|
374
394
|
return getWorkspaceRoot();
|
|
375
395
|
}
|
|
376
396
|
|
|
397
|
+
function resolveWorkspaceRoot(args) {
|
|
398
|
+
// Per-call workspaceRoot override (honored only when in the
|
|
399
|
+
// ARGO_WORKSPACE_ROOTS allowlist); defaults to the launch-directory root.
|
|
400
|
+
return resolveCallWorkspaceRoot(args);
|
|
401
|
+
}
|
|
402
|
+
|
|
377
403
|
async function callTool(name, args = {}, progressToken = null, dependencies = undefined) {
|
|
378
|
-
loadRepositoryArgoEnvironment(resolveWorkspaceRoot());
|
|
404
|
+
loadRepositoryArgoEnvironment(resolveWorkspaceRoot(args));
|
|
379
405
|
if (name === 'initializeWorkspace') {
|
|
380
|
-
const workspace = await initializeWorkspace(resolveWorkspaceRoot());
|
|
406
|
+
const workspace = await initializeWorkspace(resolveWorkspaceRoot(args));
|
|
381
407
|
const composition = canonicalSemanticInitStorage.getStore()
|
|
382
408
|
|| systemArchitectureMcp.createDefaultCanonicalSemanticInitComposition();
|
|
383
409
|
const semanticLifecycle = await runCanonicalSemanticInit(composition, {
|
|
384
|
-
repositoryRoot: resolveWorkspaceRoot(),
|
|
410
|
+
repositoryRoot: resolveWorkspaceRoot(args),
|
|
385
411
|
workspace,
|
|
386
412
|
});
|
|
387
413
|
return toolResult({
|
|
@@ -121,6 +121,48 @@ function normalizeRelativePath(value) {
|
|
|
121
121
|
return String(value == null ? '' : value).replace(/\\/g, '/').replace(/^\/+/, '');
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the workspace root for one tool call.
|
|
126
|
+
*
|
|
127
|
+
* Defaults to the launch-directory root (`getWorkspaceRoot()`). A per-call
|
|
128
|
+
* `workspaceRoot` argument (absolute path) is honored only when it appears in
|
|
129
|
+
* the `ARGO_WORKSPACE_ROOTS` allowlist (semicolon or comma separated absolute
|
|
130
|
+
* paths). Without an allowlist the override is ignored, so existing
|
|
131
|
+
* deployments keep launch-directory resolution with zero behavior change.
|
|
132
|
+
* This powers the DeepSeek Harness bridge, which injects the current session's
|
|
133
|
+
* workspace directory into every call so one dsh instance can follow the
|
|
134
|
+
* workspace the user switched to.
|
|
135
|
+
*
|
|
136
|
+
* @param {object} [args] - tool call arguments (may carry `workspaceRoot`).
|
|
137
|
+
* @returns {string} resolved absolute workspace root.
|
|
138
|
+
* @throws when the requested root is not in the allowlist.
|
|
139
|
+
*/
|
|
140
|
+
function resolveCallWorkspaceRoot(args = {}) {
|
|
141
|
+
const requested =
|
|
142
|
+
typeof args === 'object' && args !== null && typeof args.workspaceRoot === 'string'
|
|
143
|
+
? String(args.workspaceRoot).trim()
|
|
144
|
+
: '';
|
|
145
|
+
if (requested === '') {
|
|
146
|
+
return getWorkspaceRoot();
|
|
147
|
+
}
|
|
148
|
+
const allowlist = (process.env.ARGO_WORKSPACE_ROOTS || '')
|
|
149
|
+
.split(/[;,]/)
|
|
150
|
+
.map((entry) => entry.trim())
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
.map((entry) => path.resolve(entry));
|
|
153
|
+
if (allowlist.length === 0) {
|
|
154
|
+
return getWorkspaceRoot();
|
|
155
|
+
}
|
|
156
|
+
const resolved = path.resolve(requested);
|
|
157
|
+
const allowed = allowlist.some(
|
|
158
|
+
(root) => resolved === root || resolved.startsWith(root + path.sep),
|
|
159
|
+
);
|
|
160
|
+
if (!allowed) {
|
|
161
|
+
throw new Error(`workspaceRoot not in ARGO_WORKSPACE_ROOTS allowlist: ${requested}`);
|
|
162
|
+
}
|
|
163
|
+
return resolved;
|
|
164
|
+
}
|
|
165
|
+
|
|
124
166
|
module.exports = {
|
|
125
167
|
getArgoRoot,
|
|
126
168
|
getArgoEnvPath,
|
|
@@ -128,6 +170,7 @@ module.exports = {
|
|
|
128
170
|
hasStaticWorkspace,
|
|
129
171
|
normalizeRelativePath,
|
|
130
172
|
resolveArgoPath,
|
|
173
|
+
resolveCallWorkspaceRoot,
|
|
131
174
|
resolveWorkspacePath,
|
|
132
175
|
setMcpWorkspaceRoots,
|
|
133
176
|
};
|
|
@@ -5,7 +5,7 @@ const crypto = require('node:crypto');
|
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
7
|
getArgoRoot,
|
|
8
|
-
|
|
8
|
+
resolveCallWorkspaceRoot,
|
|
9
9
|
} = require('./argo-paths.js');
|
|
10
10
|
|
|
11
11
|
const DEFAULT_GRAPH_PATH = 'design/KG/SystemArchitecture.json';
|
|
@@ -381,6 +381,23 @@ const TOOLS = [
|
|
|
381
381
|
},
|
|
382
382
|
];
|
|
383
383
|
|
|
384
|
+
// Every tool accepts an optional per-call `workspaceRoot` (absolute path,
|
|
385
|
+
// honored only when listed in ARGO_WORKSPACE_ROOTS). The DeepSeek Harness
|
|
386
|
+
// bridge injects the current session's workspace directory here automatically.
|
|
387
|
+
const WORKSPACE_ROOT_PARAM = Object.freeze({
|
|
388
|
+
type: 'string',
|
|
389
|
+
description:
|
|
390
|
+
'Optional absolute workspace root for this call. Honored only when listed in ARGO_WORKSPACE_ROOTS; otherwise the server launch directory is used.',
|
|
391
|
+
});
|
|
392
|
+
for (const tool of TOOLS) {
|
|
393
|
+
const inputSchema = tool && tool.inputSchema;
|
|
394
|
+
if (inputSchema && inputSchema.type === 'object' && inputSchema.properties) {
|
|
395
|
+
if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, 'workspaceRoot')) {
|
|
396
|
+
inputSchema.properties.workspaceRoot = WORKSPACE_ROOT_PARAM;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
384
401
|
function intentElementContextInputSchema() {
|
|
385
402
|
return {
|
|
386
403
|
type: 'object',
|
|
@@ -448,13 +465,15 @@ function mutationInputSchema() {
|
|
|
448
465
|
};
|
|
449
466
|
}
|
|
450
467
|
|
|
451
|
-
function resolveWorkspaceRoot() {
|
|
452
|
-
|
|
468
|
+
function resolveWorkspaceRoot(args) {
|
|
469
|
+
// Per-call workspaceRoot override (honored only when in the
|
|
470
|
+
// ARGO_WORKSPACE_ROOTS allowlist); defaults to the launch-directory root.
|
|
471
|
+
return resolveCallWorkspaceRoot(args);
|
|
453
472
|
}
|
|
454
473
|
|
|
455
474
|
function initializeWorkspace(request) {
|
|
456
475
|
return require('./argo-mcp-server.js').initializeWorkspace(
|
|
457
|
-
request && request.repositoryRoot ? request.repositoryRoot : resolveWorkspaceRoot(),
|
|
476
|
+
request && request.repositoryRoot ? request.repositoryRoot : resolveWorkspaceRoot(request),
|
|
458
477
|
);
|
|
459
478
|
}
|
|
460
479
|
|
|
@@ -508,7 +527,7 @@ function readJson(filePath, label) {
|
|
|
508
527
|
}
|
|
509
528
|
|
|
510
529
|
async function loadContext(args = {}) {
|
|
511
|
-
const workspaceRoot = resolveWorkspaceRoot();
|
|
530
|
+
const workspaceRoot = resolveWorkspaceRoot(args);
|
|
512
531
|
const graphPath = resolveWorkspacePath(workspaceRoot, args.architecturePath || DEFAULT_GRAPH_PATH);
|
|
513
532
|
const schemaPath = resolveSchemaPath(workspaceRoot);
|
|
514
533
|
const context = {
|
|
@@ -9,6 +9,7 @@ const execFileAsync = promisify(execFile);
|
|
|
9
9
|
const {
|
|
10
10
|
getArgoRoot,
|
|
11
11
|
getWorkspaceRoot,
|
|
12
|
+
resolveCallWorkspaceRoot,
|
|
12
13
|
} = require('./argo-paths.js');
|
|
13
14
|
|
|
14
15
|
const HANDOFF_STAGES = ['intent-to-implementation', 'implementation-to-coding'];
|
|
@@ -85,8 +86,27 @@ const TOOLS = [
|
|
|
85
86
|
},
|
|
86
87
|
];
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
// Every tool accepts an optional per-call `workspaceRoot` (absolute path,
|
|
90
|
+
// honored only when listed in ARGO_WORKSPACE_ROOTS). The DeepSeek Harness
|
|
91
|
+
// bridge injects the current session's workspace directory here automatically.
|
|
92
|
+
const WORKSPACE_ROOT_PARAM = Object.freeze({
|
|
93
|
+
type: 'string',
|
|
94
|
+
description:
|
|
95
|
+
'Optional absolute workspace root for this call. Honored only when listed in ARGO_WORKSPACE_ROOTS; otherwise the server launch directory is used.',
|
|
96
|
+
});
|
|
97
|
+
for (const tool of TOOLS) {
|
|
98
|
+
const inputSchema = tool && tool.inputSchema;
|
|
99
|
+
if (inputSchema && inputSchema.type === 'object' && inputSchema.properties) {
|
|
100
|
+
if (!Object.prototype.hasOwnProperty.call(inputSchema.properties, 'workspaceRoot')) {
|
|
101
|
+
inputSchema.properties.workspaceRoot = WORKSPACE_ROOT_PARAM;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveWorkspaceRoot(args) {
|
|
107
|
+
// Per-call workspaceRoot override (honored only when in the
|
|
108
|
+
// ARGO_WORKSPACE_ROOTS allowlist); defaults to the launch-directory root.
|
|
109
|
+
return resolveCallWorkspaceRoot(args);
|
|
90
110
|
}
|
|
91
111
|
|
|
92
112
|
function resolveScriptPath(workspaceRoot, candidates) {
|
|
@@ -231,7 +251,7 @@ function toolResult(payload) {
|
|
|
231
251
|
}
|
|
232
252
|
|
|
233
253
|
async function callTool(name, args, progressToken = null) {
|
|
234
|
-
const workspaceRoot = resolveWorkspaceRoot();
|
|
254
|
+
const workspaceRoot = resolveWorkspaceRoot(args);
|
|
235
255
|
|
|
236
256
|
if (name === 'validateSystemArchitecture') {
|
|
237
257
|
return toolResult(await runValidatorScript(workspaceRoot, 'validateSystemArchitecture'));
|
package/install-argo.ps1
CHANGED
|
@@ -18,6 +18,8 @@ param(
|
|
|
18
18
|
[switch]$SkipMcp,
|
|
19
19
|
[switch]$SkipDsh,
|
|
20
20
|
[string]$DshHome = "$env:USERPROFILE\.dsh",
|
|
21
|
+
[string]$DshCwd = '',
|
|
22
|
+
[string]$DshWorkspaces = '',
|
|
21
23
|
[string]$McpPath
|
|
22
24
|
)
|
|
23
25
|
|
|
@@ -264,44 +266,25 @@ function Get-WakeupGuideline {
|
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
function Write-DshAgentRule {
|
|
267
|
-
# Merge the frontmatter-stripped ArchGraph rule
|
|
268
|
-
#
|
|
269
|
-
#
|
|
269
|
+
# Merge the frontmatter-stripped ArchGraph rule into ~/.dsh/AGENTS.md,
|
|
270
|
+
# replacing the previous ArchGraph block while preserving surrounding user
|
|
271
|
+
# content. The rule body is injected verbatim - no adapter prose, so the
|
|
272
|
+
# working prompt stays identical to the Copilot / Cursor / OpenCode rules.
|
|
270
273
|
param(
|
|
271
274
|
[string]$DshHome,
|
|
272
275
|
[string]$RuleText
|
|
273
276
|
)
|
|
274
|
-
$
|
|
275
|
-
# ArchGraph ARGO Workflow Rules (DeepSeek Harness edition)
|
|
276
|
-
|
|
277
|
-
> Deployed by install-argo.ps1 from argo/rules/archgraph.instructions.md
|
|
278
|
-
> (single source of truth; this file is a deployment artifact).
|
|
279
|
-
> DeepSeek Harness injects ~/.dsh/AGENTS.md through dsh-agent-instructions as
|
|
280
|
-
> user-global instructions; the file is injected verbatim, so the YAML
|
|
281
|
-
> frontmatter of the source rule is stripped here.
|
|
282
|
-
|
|
283
|
-
## DSH adapter note
|
|
284
|
-
- The ARGO MCP server is registered under serverName "argo" through the
|
|
285
|
-
@deepseek-ai/dsh-mcp-client plugin; its tools are exposed to the model with
|
|
286
|
-
the mcp__argo__ prefix (e.g. mcp__argo__getSystemArchitecture). When a rule
|
|
287
|
-
below names a tool like getSystemArchitecture, call the mcp__argo__* form.
|
|
288
|
-
- The argo-init skill is installed at ~/.dsh/skills/argo-init/SKILL.md and is
|
|
289
|
-
loadable through the harness skill tool.
|
|
290
|
-
'@
|
|
291
|
-
$body = Get-MarkdownBody -Content $RuleText
|
|
292
|
-
$ruleContent = $adapterNote + "`n`n" + $body
|
|
277
|
+
$ruleContent = Get-MarkdownBody -Content $RuleText
|
|
293
278
|
$dest = Join-Path $DshHome 'AGENTS.md'
|
|
294
|
-
$marker = '
|
|
279
|
+
$marker = '<WakeupGuideline>'
|
|
295
280
|
New-Item -ItemType Directory -Force -Path $DshHome | Out-Null
|
|
296
281
|
if (Test-Path $dest) {
|
|
297
282
|
$existing = Get-Content $dest -Raw -Encoding UTF8
|
|
298
283
|
if ($existing -like "*$marker*") {
|
|
299
284
|
$endTag = '</ToolsGuideline>'
|
|
300
|
-
$
|
|
301
|
-
if ($markerIdx -lt 0) { $markerIdx = 0 }
|
|
302
|
-
$startIdx = $existing.LastIndexOf('# ArchGraph', $markerIdx)
|
|
285
|
+
$startIdx = $existing.IndexOf($marker)
|
|
303
286
|
if ($startIdx -lt 0) { $startIdx = 0 }
|
|
304
|
-
$endIdx = $existing.IndexOf($endTag, $
|
|
287
|
+
$endIdx = $existing.IndexOf($endTag, $startIdx)
|
|
305
288
|
$before = $existing.Substring(0, $startIdx).TrimEnd()
|
|
306
289
|
if ($endIdx -lt 0) {
|
|
307
290
|
$combined = $before
|
|
@@ -398,6 +381,156 @@ export function apply(ctx) {
|
|
|
398
381
|
return $indexPath
|
|
399
382
|
}
|
|
400
383
|
|
|
384
|
+
function New-DshWorkspaceBridge {
|
|
385
|
+
# Generate the DSH workspace bridge plugin under ~/.dsh/plugins. It
|
|
386
|
+
# connects directly to the argo MCP server (no dsh-mcp-client row needed)
|
|
387
|
+
# with a zero-dependency minimal MCP stdio client, registers every tool as
|
|
388
|
+
# mcp__argo__* and injects the current session's workspace directory
|
|
389
|
+
# (SessionHeader.cwd) as the per-call `workspaceRoot`, so ONE dsh instance
|
|
390
|
+
# follows whichever workspace the user switched to - no model-visible
|
|
391
|
+
# parameters, no internal tool names, no restart. The argo server honors
|
|
392
|
+
# workspaceRoot only when listed in ARGO_WORKSPACE_ROOTS (install-argo.ps1
|
|
393
|
+
# -DshWorkspaces).
|
|
394
|
+
param(
|
|
395
|
+
[string]$DshHome
|
|
396
|
+
)
|
|
397
|
+
$dir = Join-Path (Join-Path $DshHome 'plugins') 'dsh-argo-workspace'
|
|
398
|
+
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
|
399
|
+
$plugin = @'
|
|
400
|
+
// dsh-argo-workspace - generated by install-argo.ps1 (single source of truth:
|
|
401
|
+
// the installer; this file is a deployment artifact, do not edit by hand).
|
|
402
|
+
//
|
|
403
|
+
// Direct MCP bridge to the argo server for DeepSeek Harness. Registers every
|
|
404
|
+
// argo tool as mcp__argo__* and injects the current session's workspace
|
|
405
|
+
// directory (SessionHeader.cwd) as the per-call `workspaceRoot` argument, so
|
|
406
|
+
// one dsh instance follows whichever workspace the user switched to - the
|
|
407
|
+
// model sees no extra parameters and no internal tool names. The argo server
|
|
408
|
+
// honors workspaceRoot only when the workspace is listed in its
|
|
409
|
+
// ARGO_WORKSPACE_ROOTS allowlist.
|
|
410
|
+
//
|
|
411
|
+
// Zero dependencies on purpose: implements the minimal MCP stdio client
|
|
412
|
+
// (JSON-RPC 2.0, one JSON object per line) with Node built-ins only, so the
|
|
413
|
+
// plugin runs from any DSH layout without resolving @modelcontextprotocol/sdk.
|
|
414
|
+
import { spawn } from 'node:child_process'
|
|
415
|
+
import readline from 'node:readline'
|
|
416
|
+
|
|
417
|
+
export const name = 'dsh-argo-workspace'
|
|
418
|
+
export const inject = ['tools']
|
|
419
|
+
|
|
420
|
+
/** Minimal MCP stdio client over the argo server process. */
|
|
421
|
+
function createArgoClient(serverPath, env, cwd) {
|
|
422
|
+
const child = spawn('node', [serverPath], {
|
|
423
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
424
|
+
env,
|
|
425
|
+
...(cwd ? { cwd } : {}),
|
|
426
|
+
})
|
|
427
|
+
const pending = new Map()
|
|
428
|
+
let nextId = 1
|
|
429
|
+
readline.createInterface({ input: child.stdout }).on('line', (line) => {
|
|
430
|
+
let message
|
|
431
|
+
try { message = JSON.parse(line) } catch { return }
|
|
432
|
+
if (message && typeof message.id === 'number' && pending.has(message.id)) {
|
|
433
|
+
const { resolve, reject } = pending.get(message.id)
|
|
434
|
+
pending.delete(message.id)
|
|
435
|
+
if (message.error) reject(new Error(JSON.stringify(message.error)))
|
|
436
|
+
else resolve(message.result)
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
child.stderr.on('data', () => {}) // drain; the argo server logs to stderr
|
|
440
|
+
const request = (method, params) => new Promise((resolve, reject) => {
|
|
441
|
+
const id = nextId++
|
|
442
|
+
pending.set(id, { resolve, reject })
|
|
443
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n')
|
|
444
|
+
})
|
|
445
|
+
return {
|
|
446
|
+
request,
|
|
447
|
+
notify: (method, params) => {
|
|
448
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n')
|
|
449
|
+
},
|
|
450
|
+
close: () => child.kill(),
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Connect, list tools, register them, and keep the client until disposal. */
|
|
455
|
+
export async function apply(ctx, config) {
|
|
456
|
+
const serverPath = config.serverPath
|
|
457
|
+
const workspaces = Array.isArray(config.workspaces) ? config.workspaces : []
|
|
458
|
+
const env = { ...process.env }
|
|
459
|
+
if (workspaces.length > 0) env.ARGO_WORKSPACE_ROOTS = workspaces.join(';')
|
|
460
|
+
const client = createArgoClient(serverPath, env, config.cwd)
|
|
461
|
+
ctx.effect(() => () => client.close(), 'dsh-argo-workspace.dispose()')
|
|
462
|
+
|
|
463
|
+
let tools = []
|
|
464
|
+
try {
|
|
465
|
+
await client.request('initialize', {
|
|
466
|
+
protocolVersion: '2024-11-05',
|
|
467
|
+
capabilities: {},
|
|
468
|
+
clientInfo: { name: 'dsh-argo-workspace', version: '0.1.0' },
|
|
469
|
+
})
|
|
470
|
+
client.notify('notifications/initialized', {})
|
|
471
|
+
const listed = await client.request('tools/list', {})
|
|
472
|
+
tools = (listed && Array.isArray(listed.tools)) ? listed.tools : []
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.warn(`[dsh-argo-workspace] failed to connect to the argo MCP server (${serverPath}): ${error && error.message ? error.message : error}`)
|
|
475
|
+
return
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const disposers = []
|
|
479
|
+
for (const tool of tools) {
|
|
480
|
+
const publicName = 'mcp__argo__' + tool.name
|
|
481
|
+
disposers.push(ctx.tools.register({
|
|
482
|
+
name: publicName,
|
|
483
|
+
description: tool.description ?? '',
|
|
484
|
+
parameters: tool.inputSchema,
|
|
485
|
+
output: {
|
|
486
|
+
schema: {
|
|
487
|
+
type: 'object',
|
|
488
|
+
properties: { content: { type: 'array', items: {} } },
|
|
489
|
+
required: ['content'],
|
|
490
|
+
additionalProperties: false,
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
execute: async (args, exec) => {
|
|
494
|
+
const sessionCwd = exec && exec.agent && exec.agent.session
|
|
495
|
+
? exec.agent.session.requestHeader().cwd
|
|
496
|
+
: undefined
|
|
497
|
+
const injected = { ...(args && typeof args === 'object' ? args : {}) }
|
|
498
|
+
if (typeof sessionCwd === 'string' && sessionCwd !== '') {
|
|
499
|
+
injected.workspaceRoot = sessionCwd
|
|
500
|
+
}
|
|
501
|
+
if (exec && exec.signal && exec.signal.aborted) {
|
|
502
|
+
throw new Error('aborted')
|
|
503
|
+
}
|
|
504
|
+
const result = await client.request('tools/call', {
|
|
505
|
+
name: tool.name,
|
|
506
|
+
arguments: injected,
|
|
507
|
+
})
|
|
508
|
+
const text = Array.isArray(result.content)
|
|
509
|
+
? result.content
|
|
510
|
+
.map((block) => block && block.type === 'text' && typeof block.text === 'string'
|
|
511
|
+
? block.text
|
|
512
|
+
: JSON.stringify(block))
|
|
513
|
+
.join('\n')
|
|
514
|
+
: (result.toolResult !== undefined ? JSON.stringify(result.toolResult) : '(no output)')
|
|
515
|
+
if (result.isError === true) throw new Error(text)
|
|
516
|
+
return {
|
|
517
|
+
content: [{ type: 'text', text }],
|
|
518
|
+
...(result.structuredContent !== undefined ? { structuredContent: result.structuredContent } : {}),
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
}))
|
|
522
|
+
}
|
|
523
|
+
ctx.effect(() => () => {
|
|
524
|
+
for (const dispose of disposers) dispose()
|
|
525
|
+
}, 'dsh-argo-workspace.tools')
|
|
526
|
+
}
|
|
527
|
+
'@
|
|
528
|
+
$indexPath = Join-Path $dir 'index.js'
|
|
529
|
+
[System.IO.File]::WriteAllText($indexPath, $plugin, (New-Object System.Text.UTF8Encoding $false))
|
|
530
|
+
Write-Host " DSH workspace bridge generated -> $indexPath"
|
|
531
|
+
return $indexPath
|
|
532
|
+
}
|
|
533
|
+
|
|
401
534
|
function New-DshAgentPresets {
|
|
402
535
|
# Generate DSH agent presets under ~/.dsh/.agent-presets/<id>/ from
|
|
403
536
|
# argo/agents/*.agent.md (the same single source the Copilot / Cursor /
|
|
@@ -557,20 +690,52 @@ if ($SkipDsh) {
|
|
|
557
690
|
Write-Host "[17/19] argo\rules\<WakeupGuideline> -> $DshHome\plugins\dsh-argo-wakeup\index.js (DeepSeek Harness wakeup plugin)"
|
|
558
691
|
$wakeupDshPath = New-DshWakeupPlugin -DshHome $DshHome -RuleText $ruleSrcContent
|
|
559
692
|
|
|
560
|
-
Write-Host "[18/19]
|
|
561
|
-
|
|
693
|
+
Write-Host "[18/19] argo-workspace + argo-wakeup rows -> $patchPath (DeepSeek Harness MCP bridge + wakeup plugin)"
|
|
694
|
+
# The generated dsh-argo-workspace bridge connects directly to the argo
|
|
695
|
+
# server (no dsh-mcp-client row), registers every tool as mcp__argo__* and
|
|
696
|
+
# injects the current session's workspace (SessionHeader.cwd) as the
|
|
697
|
+
# per-call workspaceRoot, so one dsh instance follows the workspace the
|
|
698
|
+
# user switched to. The server honors workspaceRoot only when the workspace
|
|
699
|
+
# is listed in ARGO_WORKSPACE_ROOTS (pass -DshWorkspaces "D:/a;D:/b").
|
|
700
|
+
$bridgeDshPath = New-DshWorkspaceBridge -DshHome $DshHome
|
|
701
|
+
if ($bridgeDshPath) {
|
|
702
|
+
$bridgeUrl = 'file:///' + (($bridgeDshPath -replace '\\', '/').TrimStart('/'))
|
|
703
|
+
$bridgeConfig = " config:`n serverPath: '$argoServer'`n"
|
|
704
|
+
if ($DshWorkspaces) {
|
|
705
|
+
$wsList = @($DshWorkspaces.Replace('\', '/').Split(';') | ForEach-Object { " - $_" }) -join "`n"
|
|
706
|
+
$bridgeConfig += " workspaces:`n" + $wsList + "`n"
|
|
707
|
+
}
|
|
708
|
+
if ($DshCwd) {
|
|
709
|
+
$bridgeConfig += " cwd: $($DshCwd.Replace('\', '/'))`n"
|
|
710
|
+
}
|
|
711
|
+
$rows = " - id: argo-workspace`n name: '$bridgeUrl'`n" + $bridgeConfig
|
|
712
|
+
} else {
|
|
713
|
+
$rows = ''
|
|
714
|
+
}
|
|
562
715
|
if ($wakeupDshPath) {
|
|
563
716
|
$pluginUrl = 'file:///' + (($wakeupDshPath -replace '\\', '/').TrimStart('/'))
|
|
564
717
|
$rows += " - id: argo-wakeup`n name: '$pluginUrl'`n"
|
|
565
718
|
}
|
|
566
|
-
|
|
567
|
-
|
|
719
|
+
if ($rows) {
|
|
720
|
+
$block = "# BEGIN ArchGraph ARGO deployment (managed by install-argo.ps1)`n- insert:`n" + $rows + "# END ArchGraph ARGO deployment"
|
|
721
|
+
Write-DshManagedBlock -Path $patchPath -Block $block -MarkerStart '# BEGIN ArchGraph ARGO deployment' -MarkerEnd '# END ArchGraph ARGO deployment'
|
|
722
|
+
}
|
|
568
723
|
|
|
569
724
|
Write-Host "[19/19] argo\agents -> $DshHome\.agent-presets\<id> (DeepSeek Harness agent presets)"
|
|
570
725
|
New-DshAgentPresets -DshHome $DshHome -AgentsSrc (Join-Path $argoDir 'agents')
|
|
571
726
|
|
|
572
|
-
Write-Host ' Restart `dsh web` to activate the MCP
|
|
727
|
+
Write-Host ' Restart `dsh web` to activate the MCP bridge and the wakeup plugin;'
|
|
573
728
|
Write-Host ' new sessions pick up the global rule and the argo-init skill automatically.'
|
|
729
|
+
if ($DshWorkspaces) {
|
|
730
|
+
Write-Host " Workspace following enabled: ARGO_WORKSPACE_ROOTS=$DshWorkspaces"
|
|
731
|
+
Write-Host ' The argo tools (mcp__argo__*) now auto-follow the workspace the user switched to'
|
|
732
|
+
Write-Host ' in one dsh instance - no restart needed between workspaces.'
|
|
733
|
+
} else {
|
|
734
|
+
Write-Host ' Multi-workspace: to let one dsh instance follow whichever workspace the user'
|
|
735
|
+
Write-Host ' switches to, re-run with -DshWorkspaces "D:/proj-a;D:/proj-b" (the argo server'
|
|
736
|
+
Write-Host ' only honors workspaces listed in ARGO_WORKSPACE_ROOTS). Without it the server'
|
|
737
|
+
Write-Host ' keeps resolving from the directory dsh was launched from.'
|
|
738
|
+
}
|
|
574
739
|
}
|
|
575
740
|
|
|
576
741
|
if ($SkipDeps) {
|
package/package.json
CHANGED