archgraph-argo 0.9.8 → 0.10.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.
package/README.md CHANGED
@@ -26,7 +26,7 @@ npm install -g archgraph-argo
26
26
  argo-deploy
27
27
  ```
28
28
 
29
- Done — the ARGO toolchain, skills, and rules are deployed, and the `argo` MCP server is registered automatically in **GitHub Copilot**, **Cursor**, **OpenCode**, and **DeepSeek Harness** (dsh).
29
+ Done — the ARGO toolchain, skills, and rules are deployed, and the `argo` MCP server is registered automatically in **GitHub Copilot**, **Cursor**, and **OpenCode**.
30
30
 
31
31
  > Semantic (Graph RAG) queries also need **Neo4j** and a **vector engine** configured in
32
32
  > `~/.argo/.env`; everything else works out of the box.
@@ -18,7 +18,6 @@ const {
18
18
  getWorkspaceRoot,
19
19
  hasStaticWorkspace,
20
20
  resolveArgoPath,
21
- resolveCallWorkspaceRoot,
22
21
  setMcpWorkspaceRoots,
23
22
  } = require('./argo-paths.js');
24
23
  const canonicalSemanticInitStorage = new AsyncLocalStorage();
@@ -305,25 +304,6 @@ const TOOLS = [
305
304
  },
306
305
  ];
307
306
 
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
-
327
307
  function intentElementContextInputSchema() {
328
308
  return {
329
309
  type: 'object',
@@ -394,20 +374,14 @@ function resolveWorkspaceRoot() {
394
374
  return getWorkspaceRoot();
395
375
  }
396
376
 
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
-
403
377
  async function callTool(name, args = {}, progressToken = null, dependencies = undefined) {
404
- loadRepositoryArgoEnvironment(resolveWorkspaceRoot(args));
378
+ loadRepositoryArgoEnvironment(resolveWorkspaceRoot());
405
379
  if (name === 'initializeWorkspace') {
406
- const workspace = await initializeWorkspace(resolveWorkspaceRoot(args));
380
+ const workspace = await initializeWorkspace(resolveWorkspaceRoot());
407
381
  const composition = canonicalSemanticInitStorage.getStore()
408
382
  || systemArchitectureMcp.createDefaultCanonicalSemanticInitComposition();
409
383
  const semanticLifecycle = await runCanonicalSemanticInit(composition, {
410
- repositoryRoot: resolveWorkspaceRoot(args),
384
+ repositoryRoot: resolveWorkspaceRoot(),
411
385
  workspace,
412
386
  });
413
387
  return toolResult({
@@ -121,48 +121,6 @@ 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
-
166
124
  module.exports = {
167
125
  getArgoRoot,
168
126
  getArgoEnvPath,
@@ -170,7 +128,6 @@ module.exports = {
170
128
  hasStaticWorkspace,
171
129
  normalizeRelativePath,
172
130
  resolveArgoPath,
173
- resolveCallWorkspaceRoot,
174
131
  resolveWorkspacePath,
175
132
  setMcpWorkspaceRoots,
176
133
  };
@@ -5,7 +5,7 @@ const crypto = require('node:crypto');
5
5
 
6
6
  const {
7
7
  getArgoRoot,
8
- resolveCallWorkspaceRoot,
8
+ getWorkspaceRoot,
9
9
  } = require('./argo-paths.js');
10
10
 
11
11
  const DEFAULT_GRAPH_PATH = 'design/KG/SystemArchitecture.json';
@@ -381,23 +381,6 @@ 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
-
401
384
  function intentElementContextInputSchema() {
402
385
  return {
403
386
  type: 'object',
@@ -465,15 +448,13 @@ function mutationInputSchema() {
465
448
  };
466
449
  }
467
450
 
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);
451
+ function resolveWorkspaceRoot() {
452
+ return getWorkspaceRoot();
472
453
  }
473
454
 
474
455
  function initializeWorkspace(request) {
475
456
  return require('./argo-mcp-server.js').initializeWorkspace(
476
- request && request.repositoryRoot ? request.repositoryRoot : resolveWorkspaceRoot(request),
457
+ request && request.repositoryRoot ? request.repositoryRoot : resolveWorkspaceRoot(),
477
458
  );
478
459
  }
479
460
 
@@ -527,7 +508,7 @@ function readJson(filePath, label) {
527
508
  }
528
509
 
529
510
  async function loadContext(args = {}) {
530
- const workspaceRoot = resolveWorkspaceRoot(args);
511
+ const workspaceRoot = resolveWorkspaceRoot();
531
512
  const graphPath = resolveWorkspacePath(workspaceRoot, args.architecturePath || DEFAULT_GRAPH_PATH);
532
513
  const schemaPath = resolveSchemaPath(workspaceRoot);
533
514
  const context = {
@@ -9,7 +9,6 @@ const execFileAsync = promisify(execFile);
9
9
  const {
10
10
  getArgoRoot,
11
11
  getWorkspaceRoot,
12
- resolveCallWorkspaceRoot,
13
12
  } = require('./argo-paths.js');
14
13
 
15
14
  const HANDOFF_STAGES = ['intent-to-implementation', 'implementation-to-coding'];
@@ -86,27 +85,8 @@ const TOOLS = [
86
85
  },
87
86
  ];
88
87
 
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);
88
+ function resolveWorkspaceRoot() {
89
+ return getWorkspaceRoot();
110
90
  }
111
91
 
112
92
  function resolveScriptPath(workspaceRoot, candidates) {
@@ -251,7 +231,7 @@ function toolResult(payload) {
251
231
  }
252
232
 
253
233
  async function callTool(name, args, progressToken = null) {
254
- const workspaceRoot = resolveWorkspaceRoot(args);
234
+ const workspaceRoot = resolveWorkspaceRoot();
255
235
 
256
236
  if (name === 'validateSystemArchitecture') {
257
237
  return toolResult(await runValidatorScript(workspaceRoot, 'validateSystemArchitecture'));
package/install-argo.ps1 CHANGED
@@ -16,10 +16,6 @@ param(
16
16
  [switch]$SkipEnv,
17
17
  [switch]$SkipDeps,
18
18
  [switch]$SkipMcp,
19
- [switch]$SkipDsh,
20
- [string]$DshHome = "$env:USERPROFILE\.dsh",
21
- [string]$DshCwd = '',
22
- [string]$DshWorkspaces = '',
23
19
  [string]$McpPath
24
20
  )
25
21
 
@@ -239,505 +235,70 @@ function Add-AgentsRule {
239
235
  }
240
236
  }
241
237
 
242
- # ---- DeepSeek Harness (dsh) conversion helpers ----
243
- # The ArchGraph repository keeps ONE source file per artifact (rule / skill /
244
- # mcp / plugin / agent). The targets below convert that single source into the
245
- # DeepSeek Harness shape at deploy time, exactly like Convert-AgentFile and
246
- # Convert-RuleFile do for Cursor / OpenCode.
247
-
248
- function Get-MarkdownBody {
249
- # Strip a leading YAML frontmatter block (--- ... ---) from markdown text.
250
- # DeepSeek Harness injects AGENTS.md-style instruction files verbatim, so
251
- # the frontmatter must not reach the model prompt.
252
- param([string]$Content)
253
- $m = [regex]::Match($Content, '(?s)^---\s*\r?\n(.*?)\r?\n---\s*\r?\n(.*)$')
254
- if (-not $m.Success) { return $Content }
255
- return $m.Groups[2].Value
256
- }
257
-
258
- function Get-WakeupGuideline {
259
- # Extract the <WakeupGuideline>...</WakeupGuideline> block from the rule
260
- # text, so the DSH wakeup plugin is generated from the same source the
261
- # Copilot / Cursor / OpenCode rules use.
262
- param([string]$RuleText)
263
- $m = [regex]::Match($RuleText, '(?s)<WakeupGuideline>(.*?)</WakeupGuideline>')
264
- if (-not $m.Success) { return '' }
265
- return $m.Groups[1].Value.Trim()
266
- }
267
-
268
- function Write-DshAgentRule {
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.
273
- param(
274
- [string]$DshHome,
275
- [string]$RuleText
276
- )
277
- $ruleContent = Get-MarkdownBody -Content $RuleText
278
- $dest = Join-Path $DshHome 'AGENTS.md'
279
- $marker = '<WakeupGuideline>'
280
- New-Item -ItemType Directory -Force -Path $DshHome | Out-Null
281
- if (Test-Path $dest) {
282
- $existing = Get-Content $dest -Raw -Encoding UTF8
283
- if ($existing -like "*$marker*") {
284
- $endTag = '</ToolsGuideline>'
285
- $startIdx = $existing.IndexOf($marker)
286
- if ($startIdx -lt 0) { $startIdx = 0 }
287
- $endIdx = $existing.IndexOf($endTag, $startIdx)
288
- $before = $existing.Substring(0, $startIdx).TrimEnd()
289
- if ($endIdx -lt 0) {
290
- $combined = $before
291
- if ($combined.Length -gt 0) { $combined += "`n`n" }
292
- $combined += $ruleContent
293
- } else {
294
- $after = $existing.Substring($endIdx + $endTag.Length)
295
- $combined = $before
296
- if ($combined.Length -gt 0) { $combined += "`n`n" }
297
- $combined += $ruleContent
298
- if ($after.Length -gt 0) { $combined += $after }
299
- }
300
- [System.IO.File]::WriteAllText($dest, $combined, (New-Object System.Text.UTF8Encoding $false))
301
- } else {
302
- $combined = $existing.TrimEnd() + "`n`n" + $ruleContent
303
- [System.IO.File]::WriteAllText($dest, $combined, (New-Object System.Text.UTF8Encoding $false))
304
- }
305
- } else {
306
- [System.IO.File]::WriteAllText($dest, $ruleContent, (New-Object System.Text.UTF8Encoding $false))
307
- }
308
- Write-Host " DSH rule installed -> $dest"
309
- }
310
-
311
- function Write-DshManagedBlock {
312
- # Text-level upsert of a marker-delimited block into a file (used for the
313
- # managed rows in ~/.dsh/cordis.patch.yml): replaces the previous managed
314
- # block and preserves surrounding content. No YAML dependency needed.
315
- param(
316
- [string]$Path,
317
- [string]$Block,
318
- [string]$MarkerStart,
319
- [string]$MarkerEnd
320
- )
321
- New-Item -ItemType Directory -Force -Path (Split-Path $Path) | Out-Null
322
- $existing = if (Test-Path $Path) { Get-Content $Path -Raw -Encoding UTF8 } else { '' }
323
- $startIdx = $existing.IndexOf($MarkerStart)
324
- $endIdx = if ($startIdx -ge 0) { $existing.IndexOf($MarkerEnd, $startIdx) } else { -1 }
325
- if ($startIdx -ge 0 -and $endIdx -ge 0) {
326
- $endIdx += $MarkerEnd.Length
327
- $combined = $existing.Substring(0, $startIdx).TrimEnd()
328
- if ($combined.Length -gt 0) { $combined += "`n`n" }
329
- $combined += $Block
330
- $after = $existing.Substring($endIdx).TrimStart()
331
- if ($after.Length -gt 0) { $combined += "`n`n" + $after }
332
- [System.IO.File]::WriteAllText($Path, $combined, (New-Object System.Text.UTF8Encoding $false))
333
- } else {
334
- $combined = $existing.TrimEnd()
335
- if ($combined.Length -gt 0) { $combined += "`n`n" }
336
- $combined += $Block + "`n"
337
- [System.IO.File]::WriteAllText($Path, $combined, (New-Object System.Text.UTF8Encoding $false))
338
- }
339
- }
340
-
341
- function New-DshWakeupPlugin {
342
- # Generate the DSH wakeup-gate Cordis plugin under ~/.dsh/plugins from the
343
- # <WakeupGuideline> block of the rule file. This is the DeepSeek Harness
344
- # equivalent of the OpenCode hook plugin argo/plugins/argo-wakeup.js: it
345
- # registers the gate as the first system-prompt section after the harness
346
- # identity (order -100), before the deployment persona (order 0).
347
- param(
348
- [string]$DshHome,
349
- [string]$RuleText
350
- )
351
- $gate = Get-WakeupGuideline -RuleText $RuleText
352
- if (-not $gate) {
353
- Write-Warning ' <WakeupGuideline> not found in the rule file; DSH wakeup plugin skipped.'
354
- return $null
355
- }
356
- $dir = Join-Path (Join-Path $DshHome 'plugins') 'dsh-argo-wakeup'
357
- New-Item -ItemType Directory -Force -Path $dir | Out-Null
358
- $gateJson = $gate | ConvertTo-Json
359
- $plugin = @"
360
- // dsh-argo-wakeup - generated by install-argo.ps1 from
361
- // argo/rules/archgraph.instructions.md (single source of truth: the rule file;
362
- // this file is a deployment artifact, do not edit by hand).
363
- // DeepSeek Harness equivalent of the OpenCode hook argo/plugins/argo-wakeup.js:
364
- // registers the unconditional wakeup gate as the first system-prompt section
365
- // after the harness identity, so every session identifies its Business Actor
366
- // through the argo MCP server before responding.
367
- export const name = 'dsh-argo-wakeup'
368
- export const inject = ['systemPrompt']
369
- const WAKEUP_GATE = $gateJson
370
- export function apply(ctx) {
371
- ctx.effect(() => ctx.systemPrompt.section({
372
- name: 'argo:wakeup',
373
- order: -90,
374
- text: WAKEUP_GATE,
375
- }), 'argo:wakeup.section()')
376
- }
377
- "@
378
- $indexPath = Join-Path $dir 'index.js'
379
- [System.IO.File]::WriteAllText($indexPath, $plugin, (New-Object System.Text.UTF8Encoding $false))
380
- Write-Host " DSH wakeup plugin generated -> $indexPath"
381
- return $indexPath
382
- }
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
-
534
- function New-DshAgentPresets {
535
- # Generate DSH agent presets under ~/.dsh/.agent-presets/<id>/ from
536
- # argo/agents/*.agent.md (the same single source the Copilot / Cursor /
537
- # OpenCode agent files are converted from). persona.md is a
538
- # frontmatter-stripped copy of the agent body; persona.js is a fixed
539
- # self-contained row that mounts it as the session persona
540
- # (deployment:persona section, order 0).
541
- param(
542
- [string]$DshHome,
543
- [string]$AgentsSrc
544
- )
545
- $files = @(Get-ChildItem -Path (Join-Path $AgentsSrc '*.agent.md') -ErrorAction SilentlyContinue)
546
- if ($files.Count -eq 0) {
547
- Write-Warning ' no *.agent.md files found; DSH agent presets skipped.'
548
- return
549
- }
550
- foreach ($file in $files) {
551
- $content = Get-Content $file.FullName -Raw -Encoding UTF8
552
- $m = [regex]::Match($content, '(?s)^---\s*\r?\n(.*?)\r?\n---\s*\r?\n(.*)$')
553
- $name = ''
554
- $desc = ''
555
- $body = $content
556
- if ($m.Success) {
557
- $front = $m.Groups[1].Value
558
- $body = $m.Groups[2].Value.TrimStart("`r", "`n")
559
- $nm = [regex]::Match($front, '(?m)^name:\s*(.*)$')
560
- if ($nm.Success) { $name = $nm.Groups[1].Value.Trim().Trim('"').Trim("'") }
561
- $dm = [regex]::Match($front, '(?m)^description:\s*(.*)$')
562
- if ($dm.Success) { $desc = $dm.Groups[1].Value.Trim().Trim('"').Trim("'") }
563
- }
564
- $id = $file.BaseName -replace '\.agent$', ''
565
- if (-not $name) { $name = $id }
566
- $dir = Join-Path (Join-Path $DshHome '.agent-presets') $id
567
- New-Item -ItemType Directory -Force -Path $dir | Out-Null
568
-
569
- # preset.yml: display metadata (name + description).
570
- $presetYml = "name: $name`n"
571
- if ($desc) { $presetYml += "description: `"$($desc -replace '"', '\"')`"`n" }
572
- [System.IO.File]::WriteAllText((Join-Path $dir 'preset.yml'), $presetYml, (New-Object System.Text.UTF8Encoding $false))
573
-
574
- # persona.md: the agent body verbatim (single source: the .agent.md file).
575
- [System.IO.File]::WriteAllText((Join-Path $dir 'persona.md'), $body, (New-Object System.Text.UTF8Encoding $false))
576
-
577
- # agent.cordis.yml: mount the local persona row.
578
- $cordisYml = @"
579
- # ArchGraph agent preset "$name" - generated by install-argo.ps1 from
580
- # argo/agents/$($file.Name) (single source of truth; this file is a deployment
581
- # artifact, do not edit by hand). The persona is the frontmatter-stripped agent
582
- # body (persona.md), mounted as the session's deployment:persona section.
583
- - id: persona
584
- name: './persona.js'
585
- "@
586
- [System.IO.File]::WriteAllText((Join-Path $dir 'agent.cordis.yml'), $cordisYml, (New-Object System.Text.UTF8Encoding $false))
587
-
588
- # persona.js: fixed row implementation.
589
- $personaJs = @'
590
- // persona row for an ArchGraph agent preset (generated by install-argo.ps1).
591
- // Loads persona.md next to this file and registers it as the
592
- // deployment:persona system-prompt section (order 0), shadowing the
593
- // deployment persona for the session that mounts this preset.
594
- import { readFileSync } from 'node:fs'
595
- import { fileURLToPath } from 'node:url'
596
- export const name = 'persona'
597
- export const inject = ['systemPrompt']
598
- export function apply(ctx) {
599
- const text = readFileSync(fileURLToPath(new URL('./persona.md', import.meta.url)), 'utf8')
600
- ctx.effect(() => ctx.systemPrompt.section({
601
- name: 'deployment:persona',
602
- order: 0,
603
- text,
604
- }), 'persona.section()')
605
- }
606
- '@
607
- [System.IO.File]::WriteAllText((Join-Path $dir 'persona.js'), $personaJs, (New-Object System.Text.UTF8Encoding $false))
608
- Write-Host " DSH agent preset generated -> $dir"
609
- }
610
- }
611
-
612
238
  Write-Host '==> Deploying Argo toolchain'
613
239
 
614
240
  $schemaSrc = Join-Path $argoDir 'schema'
615
241
  $schemaDest = Join-Path $ArgoRoot 'schema'
616
- Write-Host "[1/19] argo\schema -> $schemaDest"
242
+ Write-Host "[1/14] argo\schema -> $schemaDest"
617
243
  Copy-Tree -Source $schemaSrc -Destination $schemaDest
618
244
 
619
245
  $scriptsSrc = Join-Path $argoDir 'scripts'
620
246
  $scriptsDest = Join-Path $ArgoRoot 'scripts'
621
- Write-Host "[2/19] argo\scripts -> $scriptsDest"
247
+ Write-Host "[2/14] argo\scripts -> $scriptsDest"
622
248
  Copy-Tree -Source $scriptsSrc -Destination $scriptsDest
623
249
 
624
250
  $defaultsSrc = Join-Path $argoDir 'defaults'
625
251
  $defaultsDest = Join-Path $ArgoRoot 'defaults'
626
- Write-Host "[3/19] argo\defaults -> $defaultsDest"
252
+ Write-Host "[3/14] argo\defaults -> $defaultsDest"
627
253
  Copy-Tree -Source $defaultsSrc -Destination $defaultsDest
628
254
 
629
255
  $skillSrc = Join-Path (Join-Path $argoDir 'skills') 'argo-init'
630
256
  $skillDest = Join-Path $SkillsRoot 'argo-init'
631
- Write-Host "[4/19] argo\skills\argo-init -> $skillDest"
257
+ Write-Host "[4/14] argo\skills\argo-init -> $skillDest"
632
258
  Copy-Tree -Source $skillSrc -Destination $skillDest
633
259
 
634
260
  $ruleSrc = Join-Path (Join-Path $argoDir 'rules') 'archgraph.instructions.md'
635
261
  $ruleDest = Join-Path $PromptsRoot 'archgraph.instructions.md'
636
- Write-Host "[5/19] argo\rules\archgraph.instructions.md -> $ruleDest"
262
+ Write-Host "[5/14] argo\rules\archgraph.instructions.md -> $ruleDest"
637
263
  New-Item -ItemType Directory -Force -Path $PromptsRoot | Out-Null
638
264
  Copy-Item -Force -Path $ruleSrc -Destination $ruleDest
639
265
 
640
266
  $depsSrc = Join-Path $argoDir 'package.json'
641
267
  $depsDest = Join-Path $ArgoRoot 'package.json'
642
- Write-Host "[6/19] argo\package.json -> $depsDest"
268
+ Write-Host "[6/14] argo\package.json -> $depsDest"
643
269
  Copy-Item -Force -Path $depsSrc -Destination $depsDest
644
270
 
645
271
  $cursorSkillDest = Join-Path $CursorSkillsRoot 'argo-init'
646
- Write-Host "[7/19] argo\skills\argo-init -> $cursorSkillDest (Cursor)"
272
+ Write-Host "[7/14] argo\skills\argo-init -> $cursorSkillDest (Cursor)"
647
273
  Copy-Tree -Source $skillSrc -Destination $cursorSkillDest
648
274
 
649
275
  $openCodeSkillDest = Join-Path $OpenCodeSkillsRoot 'argo-init'
650
- Write-Host "[8/19] argo\skills\argo-init -> $openCodeSkillDest (OpenCode)"
276
+ Write-Host "[8/14] argo\skills\argo-init -> $openCodeSkillDest (OpenCode)"
651
277
  Copy-Tree -Source $skillSrc -Destination $openCodeSkillDest
652
278
 
653
- Write-Host "[9/19] argo\rules\archgraph.instructions.md -> $OpenCodeAgentsPath (OpenCode global AGENTS.md)"
279
+ Write-Host "[9/14] argo\rules\archgraph.instructions.md -> $OpenCodeAgentsPath (OpenCode global AGENTS.md)"
654
280
  Add-AgentsRule -AgentsPath $OpenCodeAgentsPath -RulePath $ruleSrc
655
281
 
656
282
  $agentsSrc = Join-Path $argoDir 'agents'
657
- Write-Host "[10/19] argo\agents -> $CopilotAgentsRoot (Copilot user-level)"
283
+ Write-Host "[10/14] argo\agents -> $CopilotAgentsRoot (Copilot user-level)"
658
284
  Copy-Agents -Source $agentsSrc -Destination $CopilotAgentsRoot
659
285
 
660
- Write-Host "[11/19] argo\agents -> $CursorAgentsRoot (Cursor user-level, converted to .md)"
286
+ Write-Host "[11/14] argo\agents -> $CursorAgentsRoot (Cursor user-level, converted to .md)"
661
287
  Copy-Agents -Source $agentsSrc -Destination $CursorAgentsRoot -Target cursor
662
288
 
663
- Write-Host "[12/19] argo\agents -> $OpenCodeAgentsRoot (OpenCode user-level, converted to .md)"
289
+ Write-Host "[12/14] argo\agents -> $OpenCodeAgentsRoot (OpenCode user-level, converted to .md)"
664
290
  Copy-Agents -Source $agentsSrc -Destination $OpenCodeAgentsRoot -Target opencode
665
291
 
666
292
  $pluginsSrc = Join-Path $argoDir 'plugins'
667
- Write-Host "[13/19] argo\plugins -> $PluginsRoot (Argo opencode plugins)"
293
+ Write-Host "[13/14] argo\plugins -> $PluginsRoot (Argo opencode plugins)"
668
294
  Copy-Tree -Source $pluginsSrc -Destination $PluginsRoot
669
295
 
670
296
  $cursorRuleSrc = Join-Path (Join-Path $argoDir 'rules') 'archgraph.instructions.md'
671
297
  $cursorRuleDest = Join-Path $CursorRulesRoot 'archgraph.mdc'
672
- Write-Host "[14/19] argo\rules\archgraph.instructions.md -> $cursorRuleDest (Cursor global rule, alwaysApply)"
298
+ Write-Host "[14/14] argo\rules\archgraph.instructions.md -> $cursorRuleDest (Cursor global rule, alwaysApply)"
673
299
  New-Item -ItemType Directory -Force -Path $CursorRulesRoot | Out-Null
674
300
  Convert-RuleFile -SourceFile $cursorRuleSrc -DestinationFile $cursorRuleDest
675
301
 
676
- if ($SkipDsh) {
677
- Write-Host 'Skipped DeepSeek Harness integration (-SkipDsh).'
678
- } else {
679
- $ruleSrcContent = Get-Content $ruleSrc -Raw -Encoding UTF8
680
- $dshSkillDest = Join-Path (Join-Path $DshHome 'skills') 'argo-init'
681
- $patchPath = Join-Path $DshHome 'cordis.patch.yml'
682
- $argoServer = (Join-Path $ArgoRoot 'scripts\argo-mcp-server.js').Replace('\', '/')
683
-
684
- Write-Host "[15/19] argo\rules\archgraph.instructions.md -> $DshHome\AGENTS.md (DeepSeek Harness user-global rule, frontmatter stripped)"
685
- Write-DshAgentRule -DshHome $DshHome -RuleText $ruleSrcContent
686
-
687
- Write-Host "[16/19] argo\skills\argo-init -> $dshSkillDest (DeepSeek Harness skill)"
688
- Copy-Tree -Source (Join-Path $argoDir 'skills\argo-init') -Destination $dshSkillDest
689
-
690
- Write-Host "[17/19] argo\rules\<WakeupGuideline> -> $DshHome\plugins\dsh-argo-wakeup\index.js (DeepSeek Harness wakeup plugin)"
691
- $wakeupDshPath = New-DshWakeupPlugin -DshHome $DshHome -RuleText $ruleSrcContent
692
-
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
- }
715
- if ($wakeupDshPath) {
716
- $pluginUrl = 'file:///' + (($wakeupDshPath -replace '\\', '/').TrimStart('/'))
717
- $rows += " - id: argo-wakeup`n name: '$pluginUrl'`n"
718
- }
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
- }
723
-
724
- Write-Host "[19/19] argo\agents -> $DshHome\.agent-presets\<id> (DeepSeek Harness agent presets)"
725
- New-DshAgentPresets -DshHome $DshHome -AgentsSrc (Join-Path $argoDir 'agents')
726
-
727
- Write-Host ' Restart `dsh web` to activate the MCP bridge and the wakeup plugin;'
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
- }
739
- }
740
-
741
302
  if ($SkipDeps) {
742
303
  Write-Host 'Skipped dependency install (-SkipDeps).'
743
304
  } elseif (Get-Command npm -ErrorAction SilentlyContinue) {
package/package.json CHANGED
@@ -1,31 +1,31 @@
1
- {
2
- "name": "archgraph-argo",
3
- "version": "0.9.8",
4
- "description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
5
- "license": "MIT",
6
- "bin": {
7
- "argo-deploy": "bin/argo-deploy.js"
8
- },
9
- "files": [
10
- "argo/scripts",
11
- "argo/schema",
12
- "argo/defaults",
13
- "argo/agents",
14
- "argo/plugins",
15
- "argo/skills/argo-init",
16
- "argo/rules",
17
- "argo/package.json",
18
- "vendor",
19
- "install-argo.ps1",
20
- "bin"
21
- ],
22
- "devDependencies": {
23
- "neo4j-driver": "^6.2.0"
24
- },
25
- "scripts": {
26
- "test": "node --test \"tests/*.test.js\""
27
- },
28
- "engines": {
29
- "node": ">=18"
30
- }
31
- }
1
+ {
2
+ "name": "archgraph-argo",
3
+ "version": "0.10.0",
4
+ "description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "argo-deploy": "bin/argo-deploy.js"
8
+ },
9
+ "files": [
10
+ "argo/scripts",
11
+ "argo/schema",
12
+ "argo/defaults",
13
+ "argo/agents",
14
+ "argo/plugins",
15
+ "argo/skills/argo-init",
16
+ "argo/rules",
17
+ "argo/package.json",
18
+ "vendor",
19
+ "install-argo.ps1",
20
+ "bin"
21
+ ],
22
+ "devDependencies": {
23
+ "neo4j-driver": "^6.2.0"
24
+ },
25
+ "scripts": {
26
+ "test": "node --test \"tests/*.test.js\""
27
+ },
28
+ "engines": {
29
+ "node": ">=18"
30
+ }
31
+ }