archgraph-argo 0.26.2 → 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 +127 -57
- package/argo/.env.example +181 -163
- package/argo/plugins/argo-wakeup.js +29 -27
- package/argo/rules/archgraph.instructions.md +189 -187
- package/argo/schema/schema-bundle.config.json +22 -0
- package/argo/schema/schema-bundle.rules.json +12229 -0
- package/argo/scripts/agentSearchDiagnose.js +410 -407
- package/argo/scripts/argo-mcp-server.js +122 -9
- package/argo/scripts/ensureArgoHarnessEnvironment.js +14 -0
- package/argo/scripts/external-graph-query.js +162 -0
- package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +0 -13
- package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +9 -61
- package/argo/scripts/graph-semantics.js +363 -227
- package/argo/scripts/runArchitectureTests.js +591 -583
- package/argo/scripts/schema-bundle.js +932 -0
- package/argo/scripts/systemarchitecture-mcp-server.js +758 -110
- package/argo/scripts/validateSystemArchitecture.js +262 -253
- 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 +7 -9
- package/argo/skills/ea-human-reconcile/SKILL.md +40 -39
- package/cordis.patch.yml +15 -6
- package/dsh-argo-wakeup/index.js +17 -17
- package/dsh-argo-workspace/index.js +227 -141
- package/install-argo.ps1 +336 -206
- package/package.json +57 -53
|
@@ -1,141 +1,227 @@
|
|
|
1
|
-
// dsh-argo-workspace -
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
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
|
-
|
|
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
|
-
ctx.effect(() => () => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
+
}
|