archgraph-argo 0.27.0 → 0.28.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 +126 -65
- package/argo/.env.example +12 -1
- package/argo/rules/archgraph.instructions.md +1 -1
- package/argo/schema/{argob.config.json → schema-bundle.config.json} +4 -2
- package/argo/schema/{argob-rules.json → schema-bundle.rules.json} +12229 -12229
- package/argo/scripts/agentSearchDiagnose.js +410 -407
- package/argo/scripts/argo-mcp-server.js +59 -12
- package/argo/scripts/ensureArgoHarnessEnvironment.js +1 -1
- package/argo/scripts/external-graph-query.js +162 -162
- package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +3 -0
- package/argo/scripts/graph-semantics.js +100 -3
- package/argo/scripts/runArchitectureTests.js +591 -591
- package/argo/scripts/{argob-schema.js → schema-bundle.js} +386 -28
- package/argo/scripts/systemarchitecture-mcp-server.js +673 -68
- package/argo/scripts/validateSystemArchitecture.js +3 -1
- package/argo/scripts/validator-mcp-server.js +1 -1
- package/argo/scripts/workspace-write-guard.js +144 -0
- package/argo/skills/argo-init/SKILL.md +2 -2
- package/argo/skills/ea-human-reconcile/SKILL.md +40 -40
- package/cordis.patch.yml +15 -6
- package/dsh-argo-wakeup/index.js +17 -17
- package/dsh-argo-workspace/index.js +227 -142
- package/install-argo.ps1 +122 -21
- package/package.json +57 -56
|
@@ -1,142 +1,227 @@
|
|
|
1
|
-
// dsh-argo-workspace - single source of truth for the DSH workspace bridge.
|
|
2
|
-
// install-argo.ps1 copies this file verbatim into ~/.dsh/plugins, so the
|
|
3
|
-
// deployed plugin and this shipped bundle artifact are byte-identical.
|
|
4
|
-
//
|
|
5
|
-
// Direct MCP bridge
|
|
6
|
-
//
|
|
7
|
-
// directory (SessionHeader.cwd) as the per-call
|
|
8
|
-
// one dsh instance follows whichever workspace the
|
|
9
|
-
// model sees no extra parameters and no internal tool
|
|
10
|
-
// honors the injected workspaceRoot unconditionally.
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// (
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
1
|
+
// dsh-argo-workspace - single source of truth for the DSH workspace bridge.
|
|
2
|
+
// install-argo.ps1 copies this file verbatim into ~/.dsh/plugins, so the
|
|
3
|
+
// deployed plugin and this shipped bundle artifact are byte-identical.
|
|
4
|
+
//
|
|
5
|
+
// Direct MCP stdio bridge(es) for DeepSeek Harness. Registers every tool of each
|
|
6
|
+
// configured MCP server as mcp__<id>__* and, for the argo server, injects the
|
|
7
|
+
// current session's workspace directory (SessionHeader.cwd) as the per-call
|
|
8
|
+
// `workspaceRoot` argument, so one dsh instance follows whichever workspace the
|
|
9
|
+
// user switched to - the model sees no extra parameters and no internal tool
|
|
10
|
+
// names. The argo server honors the injected workspaceRoot unconditionally.
|
|
11
|
+
//
|
|
12
|
+
// Multiple servers are supported so a host that can only spawn local stdio
|
|
13
|
+
// children (DSH) can still mount the remote federation Graph MCP through the
|
|
14
|
+
// stdio bridge (GRAPH_MCP_URL). Configure via `config.servers` (a list); the
|
|
15
|
+
// legacy single-server shape (`config.serverPath`) is still honored.
|
|
16
|
+
//
|
|
17
|
+
// Zero dependencies on purpose: implements the minimal MCP stdio client
|
|
18
|
+
// (JSON-RPC 2.0, one JSON object per line) with Node built-ins only, so the
|
|
19
|
+
// plugin runs from any DSH layout without resolving @modelcontextprotocol/sdk.
|
|
20
|
+
import { spawn } from 'node:child_process'
|
|
21
|
+
import readline from 'node:readline'
|
|
22
|
+
import { fileURLToPath } from 'node:url'
|
|
23
|
+
|
|
24
|
+
export const name = 'dsh-argo-workspace'
|
|
25
|
+
export const inject = ['tools']
|
|
26
|
+
|
|
27
|
+
// 包内默认:<pkg>/argo/scripts/argo-mcp-server.js —— 随包走,跨机器可移植。
|
|
28
|
+
const DEFAULT_SERVER_PATH = fileURLToPath(
|
|
29
|
+
new URL('../argo/scripts/argo-mcp-server.js', import.meta.url),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// A `serverPath`/`args` entry that starts with ./ or ../ resolves relative to this
|
|
33
|
+
// plugin module (portable bundle references); an absolute path is used as-is.
|
|
34
|
+
function resolveMaybeRelative(value) {
|
|
35
|
+
if (typeof value === 'string' && /^[.][\\/]/.test(value)) {
|
|
36
|
+
try {
|
|
37
|
+
return fileURLToPath(new URL(value, import.meta.url))
|
|
38
|
+
} catch {
|
|
39
|
+
return value
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Normalize config into a list of server specs (backward compatible). */
|
|
46
|
+
function normalizeServerSpecs(config) {
|
|
47
|
+
const topWorkspaces = Array.isArray(config.workspaces) ? config.workspaces : []
|
|
48
|
+
const withCommon = (spec) => {
|
|
49
|
+
const env = { ...process.env, ...(spec.env || {}) }
|
|
50
|
+
if (spec.injectWorkspaceRoot && topWorkspaces.length > 0) {
|
|
51
|
+
env.ARGO_WORKSPACE_ROOTS = topWorkspaces.join(';')
|
|
52
|
+
}
|
|
53
|
+
// Assert the broker-controlled workspace channel: the argo server will only
|
|
54
|
+
// trust the per-call sessionWorkspaceRoot (which this bridge sets from the
|
|
55
|
+
// durable SessionHeader.cwd, overwriting any model value) when this flag is
|
|
56
|
+
// present. The model cannot set a server process env var, so the trust is
|
|
57
|
+
// unforgeable.
|
|
58
|
+
if (spec.injectWorkspaceRoot) {
|
|
59
|
+
env.ARGO_WORKSPACE_ROOT_TRUSTED = '1'
|
|
60
|
+
}
|
|
61
|
+
return { ...spec, env, cwd: spec.cwd ?? (spec.injectWorkspaceRoot ? config.cwd : undefined) }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (Array.isArray(config.servers) && config.servers.length > 0) {
|
|
65
|
+
return config.servers.map((server) => {
|
|
66
|
+
const id = typeof server.id === 'string' && server.id !== '' ? server.id : 'server'
|
|
67
|
+
const injectWorkspaceRoot = server.injectWorkspaceRoot !== undefined
|
|
68
|
+
? server.injectWorkspaceRoot === true
|
|
69
|
+
: id === 'argo'
|
|
70
|
+
const serverPath = resolveMaybeRelative(server.serverPath)
|
|
71
|
+
?? (id === 'argo' ? DEFAULT_SERVER_PATH : undefined)
|
|
72
|
+
const args = Array.isArray(server.args)
|
|
73
|
+
? server.args.map(resolveMaybeRelative)
|
|
74
|
+
: (serverPath ? [serverPath] : [])
|
|
75
|
+
return withCommon({
|
|
76
|
+
id,
|
|
77
|
+
// mcp__<id>__ prefix mirrors the host's server-name convention.
|
|
78
|
+
prefix: typeof server.prefix === 'string' && server.prefix !== '' ? server.prefix : 'mcp__' + id + '__',
|
|
79
|
+
command: typeof server.command === 'string' && server.command !== '' ? server.command : 'node',
|
|
80
|
+
args,
|
|
81
|
+
env: server.env && typeof server.env === 'object' ? server.env : {},
|
|
82
|
+
cwd: server.cwd,
|
|
83
|
+
injectWorkspaceRoot,
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Legacy single-server shape: the argo workspace bridge.
|
|
89
|
+
const serverPath = config.serverPath ?? process.env.ARGO_SERVER_PATH ?? DEFAULT_SERVER_PATH
|
|
90
|
+
return [withCommon({
|
|
91
|
+
id: 'argo',
|
|
92
|
+
prefix: 'mcp__argo__',
|
|
93
|
+
command: 'node',
|
|
94
|
+
args: [serverPath],
|
|
95
|
+
env: {},
|
|
96
|
+
cwd: config.cwd,
|
|
97
|
+
injectWorkspaceRoot: true,
|
|
98
|
+
})]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Minimal MCP stdio client over one spawned server process. */
|
|
102
|
+
function createStdioClient(command, args, env, cwd) {
|
|
103
|
+
const child = spawn(command, args, {
|
|
104
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
105
|
+
env,
|
|
106
|
+
...(cwd ? { cwd } : {}),
|
|
107
|
+
})
|
|
108
|
+
const pending = new Map()
|
|
109
|
+
let nextId = 1
|
|
110
|
+
readline.createInterface({ input: child.stdout }).on('line', (line) => {
|
|
111
|
+
let message
|
|
112
|
+
try { message = JSON.parse(line) } catch { return }
|
|
113
|
+
if (message && typeof message.id === 'number' && pending.has(message.id)) {
|
|
114
|
+
const { resolve, reject } = pending.get(message.id)
|
|
115
|
+
pending.delete(message.id)
|
|
116
|
+
if (message.error) reject(new Error(JSON.stringify(message.error)))
|
|
117
|
+
else resolve(message.result)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
child.stderr.on('data', () => {}) // drain; the server logs to stderr
|
|
121
|
+
const request = (method, params) => new Promise((resolve, reject) => {
|
|
122
|
+
const id = nextId++
|
|
123
|
+
pending.set(id, { resolve, reject })
|
|
124
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n')
|
|
125
|
+
})
|
|
126
|
+
return {
|
|
127
|
+
request,
|
|
128
|
+
notify: (method, params) => {
|
|
129
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n')
|
|
130
|
+
},
|
|
131
|
+
close: () => child.kill(),
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Connect one server, list its tools, and register them under its prefix. */
|
|
136
|
+
async function mountServer(ctx, spec, registered) {
|
|
137
|
+
const client = createStdioClient(spec.command, spec.args, spec.env, spec.cwd)
|
|
138
|
+
ctx.effect(() => () => client.close(), `dsh-argo-workspace.dispose(${spec.id})`)
|
|
139
|
+
|
|
140
|
+
let tools = []
|
|
141
|
+
try {
|
|
142
|
+
await client.request('initialize', {
|
|
143
|
+
protocolVersion: '2024-11-05',
|
|
144
|
+
capabilities: {},
|
|
145
|
+
clientInfo: { name: 'dsh-argo-workspace', version: '0.1.0' },
|
|
146
|
+
})
|
|
147
|
+
client.notify('notifications/initialized', {})
|
|
148
|
+
const listed = await client.request('tools/list', {})
|
|
149
|
+
tools = (listed && Array.isArray(listed.tools)) ? listed.tools : []
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn(`[dsh-argo-workspace] failed to connect to '${spec.id}' (${spec.args.join(' ')}): ${error && error.message ? error.message : error}`)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const tool of tools) {
|
|
156
|
+
const publicName = spec.prefix + tool.name
|
|
157
|
+
registered.push(ctx.tools.register({
|
|
158
|
+
name: publicName,
|
|
159
|
+
description: tool.description ?? '',
|
|
160
|
+
parameters: tool.inputSchema,
|
|
161
|
+
output: {
|
|
162
|
+
schema: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
properties: {
|
|
165
|
+
content: { type: 'array', items: {} },
|
|
166
|
+
structuredContent: {},
|
|
167
|
+
},
|
|
168
|
+
required: ['content'],
|
|
169
|
+
additionalProperties: false,
|
|
170
|
+
},
|
|
171
|
+
render: (_args, value) => value.content,
|
|
172
|
+
},
|
|
173
|
+
execute: async (args, exec) => {
|
|
174
|
+
// SessionHeader.cwd (the durable session workspace), NOT
|
|
175
|
+
// requestHeader() — that returns the request EpochHeader
|
|
176
|
+
// (config/system/tools) which has no cwd field.
|
|
177
|
+
const injected = { ...(args && typeof args === 'object' ? args : {}) }
|
|
178
|
+
if (spec.injectWorkspaceRoot) {
|
|
179
|
+
const sessionCwd = exec && exec.agent && exec.agent.session
|
|
180
|
+
? exec.agent.session.header?.cwd
|
|
181
|
+
: undefined
|
|
182
|
+
if (typeof sessionCwd === 'string' && sessionCwd !== '') {
|
|
183
|
+
injected.workspaceRoot = sessionCwd
|
|
184
|
+
// Broker-only trusted channel: the server allows WRITES to this
|
|
185
|
+
// workspace (see workspace-write-guard.js). Set AFTER the spread so
|
|
186
|
+
// a model-supplied value can never win.
|
|
187
|
+
injected.sessionWorkspaceRoot = sessionCwd
|
|
188
|
+
} else {
|
|
189
|
+
// No session workspace: never forward a model-supplied trusted field.
|
|
190
|
+
delete injected.sessionWorkspaceRoot
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (exec && exec.signal && exec.signal.aborted) {
|
|
194
|
+
throw new Error('aborted')
|
|
195
|
+
}
|
|
196
|
+
const result = await client.request('tools/call', {
|
|
197
|
+
name: tool.name,
|
|
198
|
+
arguments: injected,
|
|
199
|
+
})
|
|
200
|
+
const text = Array.isArray(result.content)
|
|
201
|
+
? result.content
|
|
202
|
+
.map((block) => block && block.type === 'text' && typeof block.text === 'string'
|
|
203
|
+
? block.text
|
|
204
|
+
: JSON.stringify(block))
|
|
205
|
+
.join('\n')
|
|
206
|
+
: (result.toolResult !== undefined ? JSON.stringify(result.toolResult) : '(no output)')
|
|
207
|
+
if (result.isError === true) throw new Error(text)
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: 'text', text }],
|
|
210
|
+
...(result.structuredContent !== undefined ? { structuredContent: result.structuredContent } : {}),
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
}))
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Connect every configured server, register its tools, keep clients until disposal. */
|
|
218
|
+
export async function apply(ctx, config) {
|
|
219
|
+
const specs = normalizeServerSpecs(config || {})
|
|
220
|
+
const registered = []
|
|
221
|
+
for (const spec of specs) {
|
|
222
|
+
await mountServer(ctx, spec, registered)
|
|
223
|
+
}
|
|
224
|
+
ctx.effect(() => () => {
|
|
225
|
+
for (const dispose of registered) dispose()
|
|
226
|
+
}, 'dsh-argo-workspace.tools')
|
|
227
|
+
}
|
package/install-argo.ps1
CHANGED
|
@@ -28,6 +28,9 @@ param(
|
|
|
28
28
|
[string]$DoubaoHome = "$env:USERPROFILE\Doubao",
|
|
29
29
|
[string]$DoubaoWorkspace = "$env:LOCALAPPDATA\Doubao\User Data\Default\.doubao\agent_mode\workspace",
|
|
30
30
|
[switch]$SkipDoubao,
|
|
31
|
+
[string]$CodexHome = "$env:USERPROFILE\.codex",
|
|
32
|
+
[string]$CodexRepoRoot = '',
|
|
33
|
+
[switch]$SkipCodex,
|
|
31
34
|
[string]$McpPath,
|
|
32
35
|
[string]$GraphMcpUrl = 'https://argo.derekworkspacev5.com/mcp'
|
|
33
36
|
)
|
|
@@ -596,6 +599,71 @@ function Write-OpenClawMcpConfig {
|
|
|
596
599
|
[System.IO.File]::WriteAllText($configPath, $json, (New-Object System.Text.UTF8Encoding $false))
|
|
597
600
|
}
|
|
598
601
|
|
|
602
|
+
function ConvertTo-TomlString {
|
|
603
|
+
# Encode a value as a TOML basic string (escape backslash + double quote).
|
|
604
|
+
param([string]$Value)
|
|
605
|
+
return '"' + ($Value -replace '\\', '\\\\' -replace '"', '\"') + '"'
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function Write-CodexMcpConfig {
|
|
609
|
+
# Register the argo and graph-mcp MCP servers in the Codex config file
|
|
610
|
+
# (~/.codex/config.toml). Codex stores MCP servers as standard TOML tables
|
|
611
|
+
# [mcp_servers.<name>] (NOT the JSON/YAML shapes the other hosts use), so
|
|
612
|
+
# this is a text-level table upsert:
|
|
613
|
+
# - the previous managed tables ([mcp_servers.argo], [mcp_servers.argo.env],
|
|
614
|
+
# [mcp_servers.graph-mcp]) are removed first, so a re-deploy replaces
|
|
615
|
+
# rather than duplicates them (duplicate TOML tables are a parse error);
|
|
616
|
+
# - every unrelated config.toml key (the desktop app's own settings, other
|
|
617
|
+
# MCP servers, plugins, marketplaces) is preserved verbatim;
|
|
618
|
+
# - the file is written without a BOM.
|
|
619
|
+
#
|
|
620
|
+
# Workspace: Codex spawns every stdio MCP server with the SESSION workspace
|
|
621
|
+
# as its process cwd and without ARGO_* env (verified), and argo-paths.js
|
|
622
|
+
# falls back to process.cwd() when ARGO_REPO_ROOT is unset. So, like
|
|
623
|
+
# OpenCode / DeepSeek Harness, the single global argo server FOLLOWS whichever
|
|
624
|
+
# workspace the user opened in Codex and ARGO_REPO_ROOT is NOT pinned by
|
|
625
|
+
# default. Pass -RepoRoot to pin a fixed workspace instead (opt-in).
|
|
626
|
+
param(
|
|
627
|
+
[string]$ConfigPath,
|
|
628
|
+
[string]$ArgoServer,
|
|
629
|
+
[string]$GraphMcpUrl,
|
|
630
|
+
[string]$RepoRoot
|
|
631
|
+
)
|
|
632
|
+
$targetHeaders = @('[mcp_servers.argo]', '[mcp_servers.argo.env]', '[mcp_servers.graph-mcp]')
|
|
633
|
+
$existing = if (Test-Path $ConfigPath) { [System.IO.File]::ReadAllText($ConfigPath) } else { '' }
|
|
634
|
+
$lines = if ($existing -eq '') { @() } else { $existing -split "\r?\n" }
|
|
635
|
+
|
|
636
|
+
$kept = New-Object System.Collections.Generic.List[string]
|
|
637
|
+
$i = 0
|
|
638
|
+
while ($i -lt $lines.Count) {
|
|
639
|
+
$trim = $lines[$i].Trim()
|
|
640
|
+
if ($targetHeaders -contains $trim) {
|
|
641
|
+
# Skip the header and its body up to the next top-level table header.
|
|
642
|
+
$i++
|
|
643
|
+
while ($i -lt $lines.Count -and -not $lines[$i].TrimStart().StartsWith('[')) { $i++ }
|
|
644
|
+
continue
|
|
645
|
+
}
|
|
646
|
+
$kept.Add($lines[$i])
|
|
647
|
+
$i++
|
|
648
|
+
}
|
|
649
|
+
while ($kept.Count -gt 0 -and $kept[$kept.Count - 1].Trim() -eq '') { $kept.RemoveAt($kept.Count - 1) }
|
|
650
|
+
|
|
651
|
+
$blocks = New-Object System.Collections.Generic.List[string]
|
|
652
|
+
$blocks.Add((@('[mcp_servers.argo]', 'command = "node"', "args = [$(ConvertTo-TomlString $ArgoServer)]") -join "`n"))
|
|
653
|
+
if (-not [string]::IsNullOrWhiteSpace($RepoRoot)) {
|
|
654
|
+
$blocks.Add(("[mcp_servers.argo.env]`nARGO_REPO_ROOT = $(ConvertTo-TomlString $RepoRoot)"))
|
|
655
|
+
}
|
|
656
|
+
$blocks.Add(("[mcp_servers.graph-mcp]`nurl = $(ConvertTo-TomlString $GraphMcpUrl)"))
|
|
657
|
+
|
|
658
|
+
$result = ($kept -join "`n")
|
|
659
|
+
if ($result.Trim() -ne '') { $result = $result.TrimEnd() + "`n`n" }
|
|
660
|
+
$result += ($blocks -join "`n`n") + "`n"
|
|
661
|
+
|
|
662
|
+
New-Item -ItemType Directory -Force -Path (Split-Path $ConfigPath) | Out-Null
|
|
663
|
+
[System.IO.File]::WriteAllText($ConfigPath, $result, (New-Object System.Text.UTF8Encoding $false))
|
|
664
|
+
Write-Host " Codex MCP: argo (stdio) + graph-mcp (streamable http) -> $ConfigPath"
|
|
665
|
+
}
|
|
666
|
+
|
|
599
667
|
function Write-DshManagedBlock {
|
|
600
668
|
# Text-level upsert of a marker-delimited block into a file (used for the
|
|
601
669
|
# managed rows in ~/.dsh/cordis.patch.yml): replaces the previous managed
|
|
@@ -776,10 +844,9 @@ Write-Host "[4/22] argo\skills\argo-init -> $skillDest"
|
|
|
776
844
|
Copy-Tree -Source $skillSrc -Destination $skillDest
|
|
777
845
|
$reconcileSkillSrc = Join-Path (Join-Path $argoDir 'skills') 'ea-human-reconcile'
|
|
778
846
|
$diagSkillSrc = Join-Path (Join-Path $argoDir 'skills') 'agent-search-diagnosis'
|
|
779
|
-
Write-Host
|
|
847
|
+
Write-Host " argo\skills\ea-human-reconcile -> $SkillsRoot\ea-human-reconcile (EA human draft reconcile skill)"
|
|
780
848
|
Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $SkillsRoot 'ea-human-reconcile')
|
|
781
|
-
|
|
782
|
-
Copy-Tree -Source $diagSkillSrc -Destination (Join-Path $SkillsRoot 'agent-search-diagnosis')
|
|
849
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to Copilot.
|
|
783
850
|
|
|
784
851
|
$ruleSrc = Join-Path (Join-Path $argoDir 'rules') 'archgraph.instructions.md'
|
|
785
852
|
$ruleDest = Join-Path $PromptsRoot 'archgraph.instructions.md'
|
|
@@ -795,10 +862,9 @@ Copy-Item -Force -Path $depsSrc -Destination $depsDest
|
|
|
795
862
|
$cursorSkillDest = Join-Path $CursorSkillsRoot 'argo-init'
|
|
796
863
|
Write-Host "[7/22] argo\skills\argo-init -> $cursorSkillDest (Cursor)"
|
|
797
864
|
Copy-Tree -Source $skillSrc -Destination $cursorSkillDest
|
|
798
|
-
Write-Host
|
|
865
|
+
Write-Host " argo\skills\ea-human-reconcile -> $CursorSkillsRoot\ea-human-reconcile (Cursor)"
|
|
799
866
|
Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $CursorSkillsRoot 'ea-human-reconcile')
|
|
800
|
-
|
|
801
|
-
Copy-Tree -Source $diagSkillSrc -Destination (Join-Path $CursorSkillsRoot 'agent-search-diagnosis')
|
|
867
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to Cursor.
|
|
802
868
|
|
|
803
869
|
$mcpBridgeSrc = Join-Path $argoDir 'mcp-bridges'
|
|
804
870
|
$mcpBridgeDest = Join-Path $CursorMcpBridgesRoot ''
|
|
@@ -808,9 +874,9 @@ Copy-Tree -Source $mcpBridgeSrc -Destination $mcpBridgeDest
|
|
|
808
874
|
$openCodeSkillDest = Join-Path $OpenCodeSkillsRoot 'argo-init'
|
|
809
875
|
Write-Host "[8/22] argo\skills\argo-init -> $openCodeSkillDest (OpenCode)"
|
|
810
876
|
Copy-Tree -Source $skillSrc -Destination $openCodeSkillDest
|
|
811
|
-
Write-Host
|
|
877
|
+
Write-Host " argo\skills\ea-human-reconcile -> $OpenCodeSkillsRoot\ea-human-reconcile (OpenCode)"
|
|
812
878
|
Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $OpenCodeSkillsRoot 'ea-human-reconcile')
|
|
813
|
-
Write-Host
|
|
879
|
+
Write-Host " argo\skills\agent-search-diagnosis -> $OpenCodeSkillsRoot\agent-search-diagnosis (OpenCode-only: assumes opencode session export)"
|
|
814
880
|
Copy-Tree -Source $diagSkillSrc -Destination (Join-Path $OpenCodeSkillsRoot 'agent-search-diagnosis')
|
|
815
881
|
|
|
816
882
|
Write-Host "[9/22] argo\rules\archgraph.instructions.md -> $OpenCodeAgentsPath (OpenCode global AGENTS.md)"
|
|
@@ -849,15 +915,14 @@ if ($SkipDsh) {
|
|
|
849
915
|
|
|
850
916
|
Write-Host "[16/22] argo\skills\argo-init -> $dshSkillDest (DeepSeek Harness skill)"
|
|
851
917
|
Copy-Tree -Source (Join-Path $argoDir 'skills\argo-init') -Destination $dshSkillDest
|
|
852
|
-
Write-Host
|
|
918
|
+
Write-Host " argo\skills\ea-human-reconcile -> $DshHome\skills\ea-human-reconcile (DeepSeek Harness skill)"
|
|
853
919
|
Copy-Tree -Source (Join-Path $argoDir 'skills\ea-human-reconcile') -Destination (Join-Path (Join-Path $DshHome 'skills') 'ea-human-reconcile')
|
|
854
|
-
|
|
855
|
-
Copy-Tree -Source $diagSkillSrc -Destination (Join-Path (Join-Path $DshHome 'skills') 'agent-search-diagnosis')
|
|
920
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to DeepSeek Harness.
|
|
856
921
|
|
|
857
922
|
Write-Host "[17/22] dsh-argo-wakeup\index.js (single source; gate from argo\rules\archgraph.instructions.md <WakeupGuideline>) -> $DshHome\plugins\dsh-argo-wakeup\index.js (DeepSeek Harness wakeup plugin)"
|
|
858
923
|
$wakeupDshPath = New-DshWakeupPlugin -DshHome $DshHome
|
|
859
924
|
|
|
860
|
-
Write-Host "[18/22] argo-workspace + argo-wakeup rows -> $patchPath (DeepSeek Harness MCP
|
|
925
|
+
Write-Host "[18/22] argo-workspace (argo + graph-mcp) + argo-wakeup rows -> $patchPath (DeepSeek Harness MCP bridges + wakeup plugin)"
|
|
861
926
|
# The generated dsh-argo-workspace bridge connects directly to the argo
|
|
862
927
|
# server (no dsh-mcp-client row), registers every tool as mcp__argo__* and
|
|
863
928
|
# injects the current session's workspace (SessionHeader.cwd) as the
|
|
@@ -865,9 +930,20 @@ if ($SkipDsh) {
|
|
|
865
930
|
# user switched to. The server honors the injected workspaceRoot
|
|
866
931
|
# unconditionally.
|
|
867
932
|
$bridgeDshPath = New-DshWorkspaceBridge -DshHome $DshHome
|
|
933
|
+
# Deploy the federation Graph MCP stdio bridge next to the DSH plugins, so the
|
|
934
|
+
# remote graph-mcp (registry_*/mirror_*/graph_*) mounts as an ordinary local
|
|
935
|
+
# stdio server - the same bridge Cursor/Doubao already use.
|
|
936
|
+
$dshBridgeDir = Join-Path $DshHome 'mcp-bridges'
|
|
937
|
+
Copy-Tree -Source (Join-Path $argoDir 'mcp-bridges') -Destination $dshBridgeDir
|
|
938
|
+
$dshGraphBridgePath = (Join-Path $dshBridgeDir 'graph-mcp-stdio.js').Replace('\', '/')
|
|
868
939
|
if ($bridgeDshPath) {
|
|
869
940
|
$bridgeUrl = 'file:///' + (($bridgeDshPath -replace '\\', '/').TrimStart('/'))
|
|
870
|
-
|
|
941
|
+
# One bridge plugin mounts every server; the argo server follows the session
|
|
942
|
+
# workspace (workspaceRoot), graph-mcp does not inject workspaceRoot.
|
|
943
|
+
$serversYaml = " servers:`n"
|
|
944
|
+
$serversYaml += " - id: argo`n serverPath: '$argoServer'`n injectWorkspaceRoot: true`n"
|
|
945
|
+
$serversYaml += " - id: graph-mcp`n command: node`n args:`n - '$dshGraphBridgePath'`n env:`n GRAPH_MCP_URL: '$GraphMcpUrl'`n injectWorkspaceRoot: false`n"
|
|
946
|
+
$bridgeConfig = " config:`n" + $serversYaml
|
|
871
947
|
if ($DshWorkspaces) {
|
|
872
948
|
$wsList = @($DshWorkspaces.Replace('\', '/').Split(';') | ForEach-Object { " - $_" }) -join "`n"
|
|
873
949
|
$bridgeConfig += " workspaces:`n" + $wsList + "`n"
|
|
@@ -891,10 +967,8 @@ if ($SkipDsh) {
|
|
|
891
967
|
Write-Host "[19/22] argo\agents -> $DshHome\.agent-presets\<id> (DeepSeek Harness agent presets)"
|
|
892
968
|
New-DshAgentPresets -DshHome $DshHome -AgentsSrc (Join-Path $argoDir 'agents')
|
|
893
969
|
|
|
894
|
-
Write-Host "
|
|
895
|
-
Write-Host "
|
|
896
|
-
Write-Host " the local argo server); an HTTP remote MCP server has no equivalent cordis row here."
|
|
897
|
-
Write-Host " Restart `dsh web` to activate the MCP bridge and the wakeup plugin;"
|
|
970
|
+
Write-Host " graph-mcp registered for DeepSeek Harness as a stdio bridge -> $dshGraphBridgePath (GRAPH_MCP_URL=$GraphMcpUrl)"
|
|
971
|
+
Write-Host " Restart `dsh web` to activate the MCP bridges and the wakeup plugin;"
|
|
898
972
|
Write-Host ' new sessions pick up the global rule and the argo-init skill automatically.'
|
|
899
973
|
Write-Host ' The argo tools (mcp__argo__*) auto-follow the workspace the user switched to'
|
|
900
974
|
Write-Host ' in one dsh instance - no restart needed between workspaces (the server honors'
|
|
@@ -914,10 +988,9 @@ if ($SkipOpenClaw) {
|
|
|
914
988
|
|
|
915
989
|
Write-Host "[21/22] argo\skills\argo-init -> $openClawSkillDest (OpenClaw managed skill, all agents)"
|
|
916
990
|
Copy-Tree -Source (Join-Path $argoDir 'skills\argo-init') -Destination $openClawSkillDest
|
|
917
|
-
Write-Host
|
|
991
|
+
Write-Host " argo\skills\ea-human-reconcile -> $OpenClawHome\skills\ea-human-reconcile (OpenClaw managed skill, all agents)"
|
|
918
992
|
Copy-Tree -Source (Join-Path $argoDir 'skills\ea-human-reconcile') -Destination (Join-Path (Join-Path $OpenClawHome 'skills') 'ea-human-reconcile')
|
|
919
|
-
|
|
920
|
-
Copy-Tree -Source $diagSkillSrc -Destination (Join-Path (Join-Path $OpenClawHome 'skills') 'agent-search-diagnosis')
|
|
993
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to OpenClaw.
|
|
921
994
|
|
|
922
995
|
Write-Host ' OpenClaw injects AGENTS.md into Project Context on every session, so the wakeup'
|
|
923
996
|
Write-Host ' gate (UNCONDITIONAL STARTUP GATE) is active on the next OpenClaw session; restart'
|
|
@@ -937,7 +1010,8 @@ if ($SkipDoubao) {
|
|
|
937
1010
|
# fully ported surface). MCP servers are registered through Doubao's in-app
|
|
938
1011
|
# custom connector UI (STDIO), not a config file, so the installer emits a
|
|
939
1012
|
# copy-paste connector recipe instead of writing a config.
|
|
940
|
-
|
|
1013
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to Doubao.
|
|
1014
|
+
$doubaoSkills = @('argo-init', 'ea-human-reconcile')
|
|
941
1015
|
$doubaoSkillsSrc = Join-Path $argoDir 'skills'
|
|
942
1016
|
Write-Host '==> Deploying Doubao (desktop agent mode) integration'
|
|
943
1017
|
Copy-DoubaoSkills -Root (Join-Path $DoubaoWorkspace '.user_skills') -SourceSkillsRoot $doubaoSkillsSrc -SkillNames $doubaoSkills -Label 'workspace .user_skills'
|
|
@@ -1142,6 +1216,33 @@ if ($SkipMcp) {
|
|
|
1142
1216
|
}
|
|
1143
1217
|
}
|
|
1144
1218
|
|
|
1219
|
+
if ($SkipCodex) {
|
|
1220
|
+
Write-Host 'Skipped Codex integration (-SkipCodex).'
|
|
1221
|
+
} else {
|
|
1222
|
+
$ruleSrcContent = Get-Content $ruleSrc -Raw -Encoding UTF8
|
|
1223
|
+
$codexAgentsPath = Join-Path $CodexHome 'AGENTS.md'
|
|
1224
|
+
$codexSkillsRoot = Join-Path $CodexHome 'skills'
|
|
1225
|
+
|
|
1226
|
+
Write-Host '==> Deploying Codex integration'
|
|
1227
|
+
Write-Host "[Codex 1/3] argo\rules\archgraph.instructions.md -> $codexAgentsPath (Codex global AGENTS.md, frontmatter stripped)"
|
|
1228
|
+
Write-ArchGraphRuleBlock -DestPath $codexAgentsPath -RuleText $ruleSrcContent -Label 'Codex rule'
|
|
1229
|
+
|
|
1230
|
+
Write-Host "[Codex 2/3] argo\skills -> $codexSkillsRoot (Codex user skills: argo-init, ea-human-reconcile)"
|
|
1231
|
+
Copy-DoubaoSkills -Root $codexSkillsRoot -SourceSkillsRoot (Join-Path $argoDir 'skills') -SkillNames @('argo-init', 'ea-human-reconcile') -Label 'Codex user skills'
|
|
1232
|
+
# agent-search-diagnosis is OpenCode-only (it assumes `opencode` session export); not deployed to Codex.
|
|
1233
|
+
|
|
1234
|
+
if ($SkipMcp) {
|
|
1235
|
+
Write-Host ' Skipped Codex MCP registration (-SkipMcp).'
|
|
1236
|
+
} else {
|
|
1237
|
+
$codexConfigPath = Join-Path $CodexHome 'config.toml'
|
|
1238
|
+
Write-Host "[Codex 3/3] argo MCP (stdio) + graph-mcp -> $codexConfigPath (Codex config.toml)"
|
|
1239
|
+
Write-CodexMcpConfig -ConfigPath $codexConfigPath -ArgoServer $argoServer -GraphMcpUrl $GraphMcpUrl -RepoRoot $CodexRepoRoot
|
|
1240
|
+
}
|
|
1241
|
+
Write-Host ' Codex injects the global AGENTS.md into every session, so the wakeup gate is'
|
|
1242
|
+
Write-Host ' active on the next Codex session; skills load on trigger. Restart Codex so it'
|
|
1243
|
+
Write-Host ' picks up config.toml and the new skills.'
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1145
1246
|
$wakeupPluginPath = Join-Path $PluginsRoot 'argo-wakeup.js'
|
|
1146
1247
|
if (Test-Path $wakeupPluginPath) {
|
|
1147
1248
|
Write-Host '==> Registering argo-wakeup plugin in OpenCode'
|